Authentication
Sophia uses different credentials depending on who is calling the API.
| Caller | How they authenticate |
|---|---|
| SaaS users (recruiters, admins) | Authorization: Bearer <Firebase ID token>. Server verifies with Firebase Admin, then loads user and org from the database (role and org come from DB, not JWT claims). |
| Candidates (interview) | Join token in the URL for peek/verify; optional access password; then x-candidate-session for interview APIs. No Firebase. |
| Interview bot | Header x-internal-token must match server secret. |
| Stripe | Webhook stripe-signature verified against the webhook secret on the raw body. No Firebase. |
SaaS users (Firebase)
- Client: email/password or Google sign-in; holds short-lived ID tokens.
- Server env:
FIREBASE_PROJECT_ID,FIREBASE_CLIENT_EMAIL,FIREBASE_PRIVATE_KEY. - Client env:
VITE_FIREBASE_API_KEY,VITE_FIREBASE_AUTH_DOMAIN,VITE_FIREBASE_PROJECT_ID. - Public routes: signup, verify email, resend verification. Protected:
/v1/auth/me, logout. - Logout revokes Firebase refresh tokens; existing ID tokens may work until they expire (~1 hour).
Candidate flow (summary)
- Peek — join link token in path; limited branding/title only.
- Verify — optional interview password; returns a session token.
- Session — send session on candidate routes until expiry or interview ends.
Join and session values are stored hashed, not in plaintext.
Team invites
Invite links use hashed tokens on public accept routes (email or Google), separate from daily Bearer auth.