Table of Contents

Authorization Server — Authentication Flows

Step-by-step flows the MibAuthorizationServer implements, with sequence diagrams. For the components referenced here (endpoints, schemes, stores, keys), see AuthorizationServerOverview.md.

All flows run on OpenIddict 5.8.0. Access tokens are reference (opaque) tokens validated locally against AUTH_GRANTS.

1. Interactive login + Authorization Code (browser / React UI)

Used by MibServer3 (BFF) to log a human in. The user's credentials are never exposed to the BFF — only the authorization code and tokens are.

sequenceDiagram
    autonumber
    participant U as Browser
    participant BFF as MibServer3 (BFF)
    participant AUTH as Auth Server :8601
    participant SPA as React Auth UI
    participant DB as DB (AUTH_GRANTS / users)

    U->>BFF: open protected page
    BFF-->>U: 302 → /oauth/authorize?client_id&redirect_uri&scope
    U->>AUTH: GET /oauth/authorize
    Note over AUTH: no Cookies session yet
    AUTH-->>U: Challenge(Cookies) → 302 to React /login?returnUrl=/oauth/authorize…
    U->>SPA: load login UI
    SPA->>AUTH: POST /api/v2/auth/login (username, password, returnUrl)
    Note over AUTH: LoginWorkflow.VerifyCredentials<br/>(may require password change / TOTP step)
    AUTH->>DB: validate user, write ADM_USER_LOGIN_HISTORY
    AUTH-->>SPA: 200 (LoginFinalizer issued Cookies session)
    SPA-->>U: navigate back to returnUrl (/oauth/authorize…)
    U->>AUTH: GET /oauth/authorize (now with Cookies session)
    Note over AUTH: Connect/AuthorizationController<br/>builds principal, auto-consent (UseNewLoginUI)
    AUTH->>DB: Upsert authorization (AUTH_GRANTS)
    AUTH-->>U: 302 redirect_uri?code=…
    U->>BFF: GET /oauth/callback?code=…
    BFF->>AUTH: POST /oauth/token (code, client_id, client_secret, redirect_uri)
    AUTH-->>BFF: access (reference) + refresh token
    BFF-->>U: authenticated session

Key points:

  • The login step (/api/v2/auth/login) only establishes the Cookies session on the auth server; it does not issue OAuth tokens. LoginFinalizer builds the cookie ClaimsPrincipal (subject + name + roles) and records the login in ADM_USER_LOGIN_HISTORY.
  • The authorization code is issued by Connect/AuthorizationController only once a valid Cookies session exists; consent is automatic for first-party clients (useNewLoginUI=true).
  • Multi-step login (password expired/change, TOTP) is handled by LoginWorkflow + LoginStepResultHandler before the session is finalized — see PasswordChangeFlowsOperation.md and TwoFactorAuthenticationFlows.md.

2. Resource Owner Password Credentials (ROPC)

Used by APIs (MibServerApi, File Management, the Auth Server itself) that authenticate with a username/password directly. OAUTH_CLIENT_TYPE = 3.

sequenceDiagram
    autonumber
    participant API as API client
    participant AUTH as Auth Server :8601
    participant DB as DB

    API->>AUTH: POST /oauth/token<br/>grant_type=password, username, password, client_id, client_secret, scope
    Note over AUTH: PasswordGrantTokenHandler
    AUTH->>DB: validate client secret (plain text) + user credentials
    alt valid
        AUTH-->>API: 200 access (reference) [+ refresh if offline_access]
    else bad client
        AUTH-->>API: 400 invalid_client
    else blocked user
        AUTH-->>API: 400 unauthorized_client
    end

3. Client Credentials

Service-to-service authentication with no user context.

sequenceDiagram
    autonumber
    participant SVC as Service client
    participant AUTH as Auth Server :8601

    SVC->>AUTH: POST /oauth/token<br/>grant_type=client_credentials, client_id, client_secret, scope
    Note over AUTH: ClientCredentialsTokenHandler
    AUTH-->>SVC: 200 access (reference), no refresh token

4. Refresh token rotation

sequenceDiagram
    autonumber
    participant C as Client
    participant AUTH as Auth Server :8601
    participant DB as AUTH_GRANTS

    C->>AUTH: POST /oauth/token<br/>grant_type=refresh_token, refresh_token, client_id, client_secret
    Note over AUTH: RefreshTokenGrantHandler<br/>re-evaluates user IsActive
    AUTH->>DB: rotate (revoke old, issue new)
    AUTH-->>C: 200 new access + new refresh
    C->>AUTH: reuse the OLD refresh token
    AUTH-->>C: 400 invalid_grant
  • Default model is one-time-use (rolling) refresh tokens with zero reuse leeway — reusing a rotated token fails with invalid_grant.
  • Set reuseRefreshToken=true in MibIdentityConfig to allow reuse (rolling disabled). Absolute vs sliding expiration is controlled by slidingRefreshTokenExpiration. See Configuration.

5. Protected resource request (reference-token validation)

How a request bearing a reference access token is authorized on a protected endpoint (e.g. ApiV1Controller, ApiV2BaseController, UserController).

sequenceDiagram
    autonumber
    participant C as Caller
    participant EP as Protected endpoint<br/>[Authorize(MibApiProtection)]
    participant V as OpenIddict Validation<br/>(UseLocalServer)
    participant DB as AUTH_GRANTS

    C->>EP: request + Authorization: Bearer <reference token>
    EP->>V: policy scheme forwards to validation
    V->>DB: look up reference token, check status + expiration
    alt valid
        V-->>EP: ClaimsPrincipal (subject, client, scopes)
        EP-->>C: 200
    else missing/invalid/revoked
        EP-->>C: 401
    end

The BFF's "current user" lookup (UserController.Me_AllowAnonymous, called with ?access_token=) validates the reference token through the native IReferenceTokenValidator (introduced when the IdentityServer4 ITokenValidator adapter was removed) rather than the [Authorize] pipeline.

6. Logout

sequenceDiagram
    autonumber
    participant U as Browser
    participant AUTH as Auth Server :8601
    participant DB as AUTH_GRANTS

    U->>AUTH: GET /logout?redirect_uri=…
    AUTH->>AUTH: sign out Cookies session, clear server-side session id
    AUTH->>DB: IUserGrantRevoker.RemoveAllAsync(subject, client)<br/>revoke tokens + authorizations
    AUTH-->>U: 302 redirect_uri (or login page)

After logout the revoked reference tokens no longer pass validation (flow 5 returns 401).

Who supplies redirect_uri (MEDIAIBOX-12282). The auth server redirects to it verbatim, so a relative value would resolve against the auth server's own origin — the caller must send an absolute URL. The CMS front-end server's /auth/logout builds it: a returnUrl on the request is honoured when it is a same-origin CMS page, and a sign-out that names no destination — which is every sign-out from the SPA's account menu — resolves to the CMS SPA root (FrontendAppPath, falling back to /app/). It previously fell back to RootUrl, which serves the legacy interface, so an operator signing out of the SPA signed back in to the legacy UI. Recovery from an expired session is a different flow and still returns the operator to the page they were interrupted on.

7. Key bootstrap (startup)

On startup BootstrapOpenIddictKeysAsync ensures the signing key (AUTH_SIGNING_CREDENTIALS / AUTH_VALIDATION_KEYS) and the encryption key (AUTH_ENC_CREDENTIALS) exist, creating them on first run. The encryption key must persist across restarts; otherwise existing refresh tokens and authorization codes (JWE-encrypted) become undecryptable and every user must re-authenticate. See AuthorizationServerOverview.md.