Overview
MedMitra uses Supabase Authentication to secure API endpoints. All authenticated requests require a valid JWT (JSON Web Token) obtained through the Supabase authentication flow.
Keep your authentication tokens secure. Never expose them in client-side code repositories or public forums.
Authentication Flow
The authentication system follows the OAuth 2.0 Bearer Token pattern:
- User authenticates with Supabase (email/password, OAuth, etc.)
- Supabase returns a JWT access token
- Include the token in the
Authorization header for API requests
- Backend validates the token with Supabase
Supabase Configuration
Environment Variables
The backend requires these environment variables:
SUPABASE_URL=your_supabase_project_url
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
Frontend Configuration
The frontend uses:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_public_key
The backend uses the service role key for admin operations, while the frontend uses the anon key for user authentication.
Getting an Access Token
Using the Supabase JavaScript Client
For web applications, use the Supabase client library:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
)
// Sign in with email and password
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'secure_password'
})
if (data.session) {
const accessToken = data.session.access_token
console.log('Access token:', accessToken)
}
Using the REST API Directly
You can also authenticate directly through Supabase’s REST API:
curl -X POST 'https://your-project.supabase.co/auth/v1/token?grant_type=password' \
-H "apikey: YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "secure_password"
}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "v1.MRjJBwIhNO...",
"user": {
"id": "b8acad4b-4944-4d66-b405-de70886e7248",
"email": "[email protected]"
}
}
Making Authenticated Requests
Including the Token
Include the access token in the Authorization header:
curl -X GET 'http://localhost:8000/cases/all_cases?user_id=USER_ID' \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
JavaScript Example
const supabase = createClient(url, key)
const { data: { session } } = await supabase.auth.getSession()
if (session) {
const response = await fetch('http://localhost:8000/cases/all_cases?user_id=' + session.user.id, {
headers: {
'Authorization': `Bearer ${session.access_token}`
}
})
const data = await response.json()
console.log('Cases:', data.cases)
}
Python Example
import requests
access_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
user_id = "b8acad4b-4944-4d66-b405-de70886e7248"
headers = {
"Authorization": f"Bearer {access_token}"
}
response = requests.get(
f"http://localhost:8000/cases/all_cases?user_id={user_id}",
headers=headers
)
cases = response.json()
print(cases)
User ID Management
Current Implementation
In the current version, most endpoints require user_id as a parameter:
// Example: Creating a case
const formData = new FormData()
formData.append('user_id', session.user.id) // Required field
formData.append('patient_name', 'John Doe')
formData.append('patient_age', 45)
The user_id associates cases with specific doctors. Each doctor can only access their own cases.
Hardcoded Placeholder
Some endpoints have a placeholder function for future middleware:
# routes/case.py:39-41
async def get_current_user_id() -> str:
return "b8acad4b-4944-4d66-b405-de70886e7248"
This will be replaced with proper token validation middleware in production.
Token Management
Token Expiration
Supabase access tokens expire after 1 hour by default. Handle expiration by:
- Checking expiration: Monitor the
expires_in field
- Refreshing tokens: Use the refresh token before expiration
- Re-authenticating: Prompt user to sign in again if refresh fails
Refresh Token Example
const { data, error } = await supabase.auth.refreshSession({
refresh_token: currentSession.refresh_token
})
if (data.session) {
const newAccessToken = data.session.access_token
// Use the new token for subsequent requests
}
Cross-Tab Authentication Sync
MedMitra includes a custom AuthSync class that synchronizes authentication across browser tabs:
import { AuthSync } from '@/lib/auth-sync'
// Sign out from all tabs
const authSync = AuthSync.getInstance()
await authSync.triggerSignOut()
This ensures users are signed out simultaneously across all open tabs.
Security Best Practices
For Frontend Applications
- Never store tokens in localStorage - Use
httpOnly cookies when possible
- Use HTTPS in production - Encrypt token transmission
- Implement token refresh - Maintain valid sessions without re-authentication
- Clear tokens on logout - Remove all authentication data
For Backend Services
- Validate all tokens - Verify JWT signatures with Supabase
- Use service role key securely - Only on backend, never expose to frontend
- Implement rate limiting - Prevent abuse and brute force attacks
- Log authentication failures - Monitor for suspicious activity
Row-Level Security (RLS)
Supabase enforces Row-Level Security policies to ensure:
- Doctors can only access their own cases
- Users can only modify data they own
- Service role key bypasses RLS for admin operations
Example RLS Policy
-- Policy: Doctors can only view their own cases
CREATE POLICY "Doctors view own cases"
ON cases FOR SELECT
USING (doctor_id = auth.uid());
Authentication Errors
Common Error Responses
401 Unauthorized
{
"detail": "Invalid authentication credentials"
}
403 Forbidden
{
"detail": "Insufficient permissions to access this resource"
}
Debugging Authentication Issues
- Verify token format: Should be
Bearer <token> in header
- Check token expiration: Tokens expire after 1 hour
- Validate user ID: Ensure user_id matches authenticated user
- Review CORS settings: Backend allows all origins in development
Testing Authentication
Manual Testing with cURL
# 1. Get access token
TOKEN=$(curl -X POST 'https://your-project.supabase.co/auth/v1/token?grant_type=password' \
-H "apikey: $ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"password"}' \
| jq -r '.access_token')
# 2. Use token in request
curl -X GET "http://localhost:8000/cases/all_cases?user_id=USER_ID" \
-H "Authorization: Bearer $TOKEN"
Automated Testing
For integration tests, use a test user:
import pytest
from supabase import create_client
@pytest.fixture
def auth_token():
supabase = create_client(SUPABASE_URL, SUPABASE_ANON_KEY)
response = supabase.auth.sign_in_with_password({
"email": "[email protected]",
"password": "test_password"
})
return response.session.access_token
def test_get_cases(auth_token):
headers = {"Authorization": f"Bearer {auth_token}"}
response = requests.get("http://localhost:8000/cases/all_cases", headers=headers)
assert response.status_code == 200
Next Steps
Now that you understand authentication:
- Create your first case - See Create Case
- Retrieve cases - Learn about Get All Cases
- Upload documents - Check Upload Files