Authentication Flow
PSI supports multiple authentication methods via an adapter pattern. The default is Google Sign-In via Firebase Auth, with full OIDC Single Sign-On support for partner identity providers.
Supported Providers
| Provider | Partner | Mode |
|---|---|---|
| Google Sign-In | Default (all) | Firebase popup |
| ZDF Keycloak (Integration) | ZDF | OIDC fragment |
| ZDF Keycloak (Production) | ZDF | OIDC fragment |
| CBC Azure AD B2C | CBC | OIDC fragment |
| CBC International Azure AD B2C | CBC | OIDC fragment |
| Radio-Canada Azure AD B2C | Radio-Canada | OIDC code |
| Radio-Canada International Azure AD B2C | Radio-Canada | OIDC code |
| RTBF Login | RTBF | OIDC fragment |
| KRO-NCRV/NPO ID | Dutch broadcasters | OIDC fragment |
| SRG SSR Account | SRG SSR | OIDC fragment |
| Bluesky | Cross-platform | Client-side auth |
SSO Authentication Flow
When a user logs in via an SSO provider, the following sequence occurs:
sequenceDiagram
participant User
participant Frontend as PSI Frontend
participant IdP as OIDC Provider
participant Backend as PSI Backend
participant FBAuth as Firebase Auth
User->>Frontend: Click login, select SSO provider
Frontend->>IdP: Authorization request<br/>(client_id, redirect_uri, scope, response_type)
IdP->>User: Authentication prompt
User->>IdP: Provide credentials
IdP->>Frontend: Redirect with ID token<br/>(in URL fragment or via code exchange)
Frontend->>Backend: POST /api/auth/convertToken (JWT)
Backend->>IdP: Fetch JWKS from /.well-known/jwks.json
Backend->>Backend: Verify JWT signature and claims
Backend->>FBAuth: Create or update Firebase user
Backend->>FBAuth: Generate custom login token
Backend->>Frontend: Return custom login token
Frontend->>FBAuth: Sign in with custom token
FBAuth->>Frontend: Firebase session established
Frontend->>Backend: Subsequent API requests<br/>(Firebase ID token in Authorization header)
Backend->>Frontend: Response data
Frontend->>User: Display content OIDC Modes
PSI supports two OIDC response modes:
Fragment mode (recommended for most providers):
- The IdP returns the ID token directly in the URL fragment (
#id_token=...) - The frontend extracts the token from the URL
- Used by: ZDF, CBC, RTBF, KRO/NCRV/NPO
Code mode:
- The IdP returns an authorization code
- The code is exchanged server-side for tokens
- Used by: Radio-Canada
Registration Flow
For providers that support user registration:
sequenceDiagram
participant User
participant Frontend as PSI Frontend
participant IdP as OIDC Provider
participant Backend as PSI Backend
participant FBAuth as Firebase Auth
participant Email as Email Service
User->>Frontend: Open registration page
Frontend->>User: Display registration form
User->>Frontend: Submit registration data
Frontend->>IdP: Create user (email, password)
IdP->>Email: Send verification email
Email-->>User: Verification email with code
User->>Frontend: Submit verification code
Frontend->>IdP: Validate verification code
IdP->>Frontend: Verification success, return tokens
Frontend->>Backend: POST /api/auth/convertToken (JWT)
Backend->>FBAuth: Create Firebase user (verified email)
Backend->>Frontend: Return custom login token
Frontend->>FBAuth: Sign in with custom token
Frontend-->>User: Registration complete Auth Architecture
Client Side
The client uses an AuthAdapter interface:
interface AuthAdapter {
getCurrentUser(): User | null;
getIdTokenAsync(): Promise<string>;
signOutAsync(): Promise<void>;
signInWithGoogleAsync(): Promise<void>;
signInWithTokenAsync(token: string): Promise<void>;
onAuthStateChanged(callback): Unsubscribe;
}
The default implementation (FirebaseAuthAdapter) wraps the Firebase Auth SDK.
Server Side
The server uses a layered auth architecture:
BetterAuthAdapter-- base class using the better-auth libraryFirebaseAuthAdapter-- extends BetterAuthAdapter with Firebase-specific operations (custom tokens, user import/export)firebaseIdTokenplugin -- better-auth plugin that verifies Firebase ID tokens fromAuthorizationheadersgenericOidcplugin -- better-auth plugin that verifies JWTs from any OIDC provider via JWKS (with provider-specific implementations for Azure AD B2C, CBC/RC)
Admin Roles
Admin roles are stored in Firebase RTDB at:
Roles include Owner. Admin authorization is checked before executing any adminFunctions in a server module.
Configuration
Adding a new SSO provider
- Define the provider in
client/util/loginproviders.ts - Register the JWKS endpoint in
server/util/server-init.ts - Configure Firebase permissions for custom token creation
See the SSO Integration Guide in psi-product for step-by-step instructions.