Skip to content

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:

  1. BetterAuthAdapter -- base class using the better-auth library
  2. FirebaseAuthAdapter -- extends BetterAuthAdapter with Firebase-specific operations (custom tokens, user import/export)
  3. firebaseIdToken plugin -- better-auth plugin that verifies Firebase ID tokens from Authorization headers
  4. genericOidc plugin -- 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:

silo/{siloKey}/module/admin/userRoles/{email}

Roles include Owner. Admin authorization is checked before executing any adminFunctions in a server module.

Configuration

Adding a new SSO provider

  1. Define the provider in client/util/loginproviders.ts
  2. Register the JWKS endpoint in server/util/server-init.ts
  3. Configure Firebase permissions for custom token creation

See the SSO Integration Guide in psi-product for step-by-step instructions.