Skip to content

Note: This document is superseded by Client Authentication Workflow (Detailed).

Client Authentication

Client accounts are scoped to a specific agency. These accounts are disjoint, meaning a single email can be registered in multiple agencies without any connection between them.

Registration Flow (Email + Password)

Client registration requires an agencyId.

  1. Request Registration: The RegisterClientCommand is executed. It checks if the agencyId is valid and if the email is already used within that specific agency.
  2. Verify Email: The VerifyEmailClientCommand is executed. It activates the client account scoped to the agency and returns a Token Pair.
sequenceDiagram
    participant Client as Frontend (Panel)
    participant API as ClientAuthController
    participant Bus as CommandBus
    participant Handler as RegisterClientHandler
    participant DB as Identity Database
    participant Pub as IdentityPublisher

    Client->>API: POST /auth/client/register {email, agencyId, ...}
    API->>Bus: Execute RegisterClientCommand
    Bus->>Handler: execute()
    Handler->>DB: Check if agency exists
    Handler->>DB: Save Client (isActive: false)
    Handler->>DB: Save Verification Token
    Handler->>Pub: publishClientRegistered()
    Pub-->>Client: Email with code (via EventBus)
    Handler-->>API: Result.ok
    API-->>Client: 200 OK (Verification code sent)

Login Flow (Email + Password)

Login is handled by the LocalClientStrategy. The agencyId must be provided in the request body.

sequenceDiagram
    participant Client as Frontend (Panel)
    participant Guard as LocalClientAuthGuard
    participant Strat as LocalClientStrategy
    participant API as ClientAuthController
    participant Bus as CommandBus
    participant Handler as LoginClientHandler

    Client->>Guard: POST /auth/client/login {email, password, agencyId}
    Guard->>Strat: validate(email, password, agencyId)
    Strat->>Strat: Check password (HashingService)
    Strat-->>Guard: return Client entity
    Guard->>API: req.user = Client
    API->>Bus: Execute LoginClientCommand(Client)
    Bus->>Handler: execute()
    Handler-->>API: TokenPair {accessToken, refreshToken}
    API-->>Client: 200 OK (Tokens)

Google OAuth Flow

Client Google OAuth is uniquely scoped using the state parameter in the OAuth 2.0 flow.

  1. Redirect: User is sent to Google's consent screen via ClientAuthController.googleRedirect?agencyId=.... The agencyId is embedded in the state parameter.
  2. Callback: Google redirects back to ClientAuthController.googleCallback with code and state (which contains our agencyId).
  3. Command Execution: GoogleLoginClientCommand uses the agencyId to correctly scope the login/registration.
sequenceDiagram
    participant User as User Browser
    participant API as ClientAuthController
    participant Bus as CommandBus
    participant Handler as GoogleLoginClientHandler

    User->>API: GET /auth/client/google?agencyId=...
    API->>API: Prepare OAuth URL (state=agencyId)
    API->>User: Redirect to Google
    User->>User: Consent & Authenticate
    User->>API: GET /auth/client/google/callback?code=...&state=agencyId
    API->>Bus: Execute GoogleLoginClientCommand(code, agencyId)
    Bus->>Handler: execute()
    Handler->>Handler: Exchange code for profile
    Handler->>Handler: Link or Create Client (scoped to agencyId)
    Handler-->>API: TokenPair
    API-->>User: 200 OK (Tokens)