Request Lifecycle
Every API request goes through a consistent auth pipeline before reaching business logic. The session resolution and authorization guards are implemented in lib/auth/request-session.ts and lib/auth/server/session.ts.
Full lifecycle diagram
Step-by-step walkthrough
1. Credential extraction
getRequestSession(request) checks two places in order:
Authorization: Bearer <token>header — for programmatic/API clientsgh_sessionhttpOnly cookie — for browser sessions
If neither is present, the session is null and the route returns 401.
2. Session lookup
The session ID is used to query auth_sessions. The row includes:
expires_at— if in the past, the row is deleted and the request is treated as unauthenticatedencrypted_github_token— the user’s GitHub token, AES-256-GCM encrypted at rest- Foreign key joins to
users,organization_memberships,organizations, andsession_installations
3. Token decryption
lib/auth/server/crypto.ts decrypts the GitHub token using AES-256-GCM with a 12-byte IV. The plaintext token exists only in process memory for the duration of the request. It is never written back to the database and never included in responses.
4. Authorization guards
Three guards run in sequence. Each is a short-circuit:
| Guard | Returns | Condition |
|---|---|---|
requireApiSession | 401 | No valid session |
requireOrganizationAccess | 404 | Session exists but user is not a member of the requested org |
requireOrganizationAdmin | 403 | User is a member but not an admin (only on admin endpoints) |
Using 404 for requireOrganizationAccess (rather than 403) avoids leaking whether an org exists to an unauthorized user.
5. Business logic
After the guards pass, the handler runs with the AuthSession object in scope. It picks the appropriate GitHub client:
- User OAuth token (from the session) — for GraphQL proxy, org listing
- Installation token via
getInstallationOctokit(installationId)— for leaderboard ingest, mass-invite
Both Supabase admin client and GitHub client calls happen here.
6. Response serialization
Responses use the SessionView type, which strips githubToken from the session before serialization. The GitHub token never appears in any HTTP response body.