Skip to main content

Overview

Paw & Care uses Supabase authentication with JWT (JSON Web Token) based authentication. All API requests must include a valid JWT token in the Authorization header.

Getting Started

1. Configure Supabase

Set up your Supabase project credentials:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key

2. Initialize Supabase Client

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.VITE_SUPABASE_URL,
  process.env.VITE_SUPABASE_ANON_KEY
);

Authentication Flow

Sign Up

Create a new user account:
const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'secure-password',
  options: {
    data: {
      firstName: 'Sarah',
      lastName: 'Chen',
      role: 'veterinarian'
    }
  }
});

Sign In

Authenticate an existing user:
const { data, error } = await supabase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'secure-password'
});

if (data.session) {
  const accessToken = data.session.access_token;
  // Use this token in API requests
}

Get Current Session

Retrieve the active session:
const { data: { session } } = await supabase.auth.getSession();

if (session) {
  console.log('User ID:', session.user.id);
  console.log('Access token:', session.access_token);
}

Sign Out

const { error } = await supabase.auth.signOut();

Making Authenticated Requests

Using Supabase Client

The Supabase client automatically includes the JWT token:
// Automatically authenticated
const { data: pets } = await supabase
  .from('pets')
  .select('*');

Using REST API Directly

Include the JWT token in the Authorization header:
curl -X GET http://localhost:3000/api/pets \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json"

Using API Client

The API client handles token management:
import { petsApi } from '@/lib/api';

const pets = await petsApi.getAll();

Token Management

Token Expiration

JWT tokens expire after 1 hour. Refresh tokens are valid for 30 days.

Auto-Refresh

Supabase automatically refreshes tokens:
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'TOKEN_REFRESHED') {
    console.log('Token refreshed:', session?.access_token);
  }
});

Manual Refresh

const { data, error } = await supabase.auth.refreshSession();

User Roles & Permissions

Available Roles

veterinarian
role
Full access to all practice data and operations
technician
role
Read/write access to appointments and records, limited admin access
receptionist
role
Access to scheduling, client management, limited medical record access
admin
role
Full system administration and configuration

Checking User Role

const { data: { user } } = await supabase.auth.getUser();
const role = user?.user_metadata?.role;

if (role === 'veterinarian') {
  // Allow access to sensitive operations
}

Row Level Security (RLS)

Supabase enforces row-level security policies:

Pets Table Policy

-- Users can only access pets from their practice
CREATE POLICY "Users can view their practice pets"
  ON pets FOR SELECT
  USING (practice_id = auth.jwt() ->> 'practice_id');

Medical Records Policy

-- Only veterinarians can finalize records
CREATE POLICY "Veterinarians can finalize records"
  ON medical_records FOR UPDATE
  USING (auth.jwt() ->> 'role' = 'veterinarian');

Security Best Practices

Never expose your Supabase service role key in client-side code. Only use the anon key.

Secure Token Storage

// ✅ Good: Store in httpOnly cookie (server-side)
res.cookie('auth-token', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict'
});

// ❌ Bad: Store in localStorage (vulnerable to XSS)
localStorage.setItem('token', token);

Validate Tokens Server-Side

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_KEY // Server-side only
);

const { data: { user }, error } = await supabase.auth.getUser(token);

Error Handling

Handle authentication errors:
const { data, error } = await supabase.auth.signInWithPassword({
  email,
  password
});

if (error) {
  switch (error.message) {
    case 'Invalid login credentials':
      // Show user-friendly error
      break;
    case 'Email not confirmed':
      // Prompt to verify email
      break;
    default:
      console.error('Auth error:', error);
  }
}

Common Error Codes

401 Unauthorized
error
Missing or invalid JWT token
403 Forbidden
error
Valid token but insufficient permissions
422 Invalid Credentials
error
Incorrect email or password

Testing Authentication

Test API authentication:
# Check health endpoint (no auth required)
curl http://localhost:3000/api/health

# Test authenticated endpoint
curl -X GET http://localhost:3000/api/pets \
  -H "Authorization: Bearer $TOKEN"

Next Steps

Pets API

Manage pet records

Appointments

Schedule appointments

Build docs developers (and LLMs) love