Overview
Athena ERP uses Supabase Auth as its identity provider. While Supabase handles user authentication (login, signup, password reset), Athena maintains its own authorization model in PostgreSQL. This hybrid approach provides:- ✅ Battle-tested authentication flows (OAuth, magic links, MFA)
- ✅ Flexible authorization with school-specific roles
- ✅ Database-driven permission resolution
- ✅ Multi-tenant support via school memberships
Architecture
JWT Validation Process
Athena implements a two-tier validation strategy to handle JWT tokens reliably:Tier 1: Local JWT Decode
The API first attempts to decode the JWT using the sharedJWT_SECRET:
The raw JWT token from the
Authorization: Bearer headerThe JWT secret shared with Supabase (from
SUPABASE_JWT_SECRET)The signing algorithm (must match Supabase configuration)
Tier 2: Supabase User Info Endpoint
If local decoding fails (e.g., during key rotation), Athena falls back to Supabase’s/auth/v1/user endpoint:
Implementation: app/auth/jwt.py:64-98
Issuer Verification
After decoding, Athena verifies the token’s issuer matches the configured Supabase project:Token Payload Mapping
Athena extracts the following fields from Supabase JWT:Standard Claims
User ID in UUID format. Used to lookup the user in Athena’s
users table.User’s email address (synced with Supabase).
JWT issuer. Must be
{SUPABASE_URL}/auth/v1.App Metadata (Optional)
Suggested school context (UUID). The API validates this against
school_memberships.JWT-level roles (e.g.,
["superadmin"]). Combined with database roles for authorization.app_metadata is set in Supabase during user creation or updated via Supabase Management API. It provides hints but is not the source of truth for authorization.Database Authorization
After JWT validation, Athena resolves the user’s actual permissions by querying the local database:Step 1: Fetch User
Step 2: Fetch School Memberships
Step 3: Resolve School Context
The active school is determined by (in order):X-School-Idheader (explicit)- JWT
app_metadata.school_id(fallback) - Single membership (automatic)
Step 4: Build Auth Context
The finalAuthContext contains:
The authenticated user object from the database.
The decoded JWT payload.
The active school membership (if a school is selected).
The active school object (if a school is selected).
All active school memberships for the user.
Combined roles from JWT
app_metadata.roles and membership.roles.Configuration
Required Environment Variables
Development vs Production
Development:Endpoint: GET /auth/me
Retrieve the authenticated user’s profile, including roles and school memberships.Request
Headers
Bearer token from Supabase authentication.Format:
Bearer {access_token}Optional UUID of the school to use as context. Required if user has multiple memberships.
Response
User’s UUID.
User’s email address.
User’s full name.
Whether the user account is active.
Combined roles from JWT and active school membership.
UUID of the active school (if one is selected).
List of all active school memberships.
Example Response
Error Responses
Token is missing, invalid, or expired.
User account is inactive or lacks permission to access the requested school.
User has multiple memberships but no
X-School-Id header provided.Best Practices
Token Refresh
Supabase tokens expire after 1 hour by default. Implement token refresh in your client:Multi-Tenant Requests
For users with multiple school memberships, always includeX-School-Id:
Error Handling
Always handle401 and 403 errors by redirecting to login or showing an error message:
Security Considerations
JWT Secret Management
- Never expose
JWT_SECRETto clients - Rotate secrets periodically using Supabase dashboard
- Use environment variables instead of hardcoding
Token Storage
- Store tokens in httpOnly cookies (preferred) or secure localStorage
- Never store tokens in URL parameters
- Clear tokens on logout
SSRF Protection
Athena prevents Server-Side Request Forgery by:- Never parsing the
issclaim to make HTTP requests - Only using server-configured
SUPABASE_URL - Validating the issuer against a whitelist
Rate Limiting
Consider implementing rate limiting on authentication endpoints to prevent:- Brute force attacks
- Token enumeration
- DDoS attempts
Troubleshooting
”Token inválido, expirado o malformado”
Causes:- Token has expired (>1 hour old)
- JWT secret mismatch between Athena and Supabase
- Token was issued by a different Supabase project
- Refresh the token using Supabase client
- Verify
JWT_SECRETmatches Supabase project settings - Check
SUPABASE_URLpoints to the correct project
”Usuario no encontrado en la base local”
Cause: User exists in Supabase but not in Athena’s database. Solution: Create a user record in Athena’susers table:
“Debes enviar el header X-School-Id para elegir un colegio”
Cause: User has multiple school memberships but no school was specified. Solution: IncludeX-School-Id header in all requests:
Related Resources
Authentication Overview
Learn about Athena’s authentication architecture
Permissions Reference
View the complete role and permission matrix
Supabase Documentation
Official Supabase Auth documentation