Appearance
Client Authentication Workflow (Detailed)
This document provides an end-to-end view of the client authentication system. Client accounts represent agency customers, are always scoped to a specific agency, and only authenticate against active agencies.
1. Agency-Scoped Registration & Verification
Clients register within the context of an agency.
1.1 Initiate Registration
- Client sends
email,password,firstName,lastName, andagencyIdtoPOST /api/identity/auth/client/register. - The system checks if the email exists within that specific agency.
- Registration is rejected when the target agency is inactive.
- A new
Cliententity is created (isActive: false). - Previous unused verification codes for this client are marked as used.
- A CSPRNG 6-digit code is generated (
crypto.randomInt()), hashed via the platform hashing service, and stored. - The token tracks
attemptsto prevent brute force.
1.2 Verify Email
- Client submits the
codeand device info. - The system fetches the latest active token.
- Rate Limiting & Lockout: Endpoint is protected by
@RateLimit(5 req/min per IP via Redis sliding window). If the token exceedsMAX_VERIFICATION_ATTEMPTS(5), it is locked. - The input code is validated against the stored hash. During transition, legacy plaintext rows are still accepted.
- Failed attempts increment the counter. Success activates the client and generates an initial Token Pair tied to a new Session.
- Verify-email responses include tokens plus actor profile (
id,email,firstName,lastName) and the current session entry (session).
sequenceDiagram
participant C as Client App
participant A as Auth API
participant DB as Database
C->>A: POST /api/identity/auth/client/register {email, agencyId}
A->>DB: Store Client (scoped to agency)
A->>A: Generate CSPRNG code
A->>DB: Store hashed code
A-->>C: 200 OK (Email Sent)
C->>A: POST /api/identity/auth/client/verify-email {code}
A->>DB: Fetch code & check lock status
alt Valid code
A->>DB: Activate Client
A->>A: Generate Session & Tokens
A-->>C: 200 OK + Tokens + Profile + Session
end
2. Login, Sessions & OAuth
2.1 Standard Login
- The
LocalClientStrategyvalidates theemail,password, and requires theagencyId. - All login denials (missing agency context, invalid agency, invalid credentials, inactive account) return the same generic structured 401 (
IDENTITY.INVALID_CREDENTIALS). - Login also fails when the agency exists but is inactive.
- The system creates a
Session(recording IP, browser, and device fingerprint). Clients are allowed up to 10 concurrent sessions (oldest is revoked if exceeded). - Login responses include token pair plus actor profile (
id,email,firstName,lastName) and the current session entry (session).
2.2 Google OAuth (CSRF Protected)
Client OAuth requires linking the authentication to a specific agency.
- Redirect: The client requests
GET /api/identity/auth/client/google?agencyId=.... The server generates a randomstatetoken, stores a hashed-key one-time entry in Redis with{ agencyId }(TTL: 5 minutes), and passesstateto Google. - Callback: Google returns
codeandstate. The server atomically consumes (read + delete) the stored state, retrieves theagencyId, and completes authentication securely (mitigating CSRF and state replay). - Error Privacy: OAuth misconfiguration/request issues use structured 400 (
IDENTITY.GOOGLE_AUTH_INVALID_REQUEST), while provider exchange failures return generic structured 401 (IDENTITY.GOOGLE_AUTH_FAILED) without leaking provider internals. - Google callback returns the same enriched login response shape used by password login.
sequenceDiagram
participant C as Client App
participant A as Auth API
participant G as Google
participant Cache as State Store
C->>A: GET /api/identity/auth/client/google?agencyId=123
A->>A: Generate UUID 'state'
A->>Cache: Save {state: 'abc', agencyId: 123}
A-->>C: Redirect to Google with state='abc'
C->>G: Authenticate
G-->>C: Redirect /api/identity/auth/client/google/callback?code=xyz&state=abc
C->>A: GET /api/identity/auth/client/google/callback?code=xyz&state=abc
A->>Cache: Lookup 'abc' -> gets agencyId 123
A->>A: Exchange code & Login Client
A-->>C: 200 OK + Tokens
3. Token Rotation & Session Management
Clients use the same robust token rotation and session management infrastructure as Users.
3.1 Session Validation
Every access token request and refresh token request checks session.isActive. If the session was revoked, the tokens are instantly useless.
3.2 Refresh Token Security
- Refresh tokens are single-use.
- Atomic updates: When refreshed, the old token's hash is revoked.
- Reuse Detection: If a revoked refresh token is presented, the system immediately invalidates ALL sessions for that client, logs a warning, and returns
IDENTITY.REFRESH_TOKEN_REUSE_DETECTEDwith a neutral re-authentication message.
3.3 Client Access to Session Management
Clients have full access to view and manage their sessions via endpoints protected by @Auth():
GET /api/identity/auth/sessions: List active devices.GET /api/identity/auth/sessions/current: Read the current device session payload.POST /api/identity/auth/sessions/revoke: Revoke a specific device.POST /api/identity/auth/sessions/revoke-others: Revoke all sessions except the current one.POST /api/identity/auth/client/change-password: Change password, revoke all other active sessions, revoke current-session refresh tokens, and return a fresh token pair for the current session.
sequenceDiagram
participant C as Client App
participant A as Auth API
participant DB as Database
C->>A: POST /api/identity/auth/client/change-password
A->>DB: Update Password
A->>DB: Revoke all other Client Sessions
A->>DB: Revoke current-session refresh tokens
A->>A: Generate fresh token pair for current session
A-->>C: 200 OK + Tokens