Table of Contents

Authorization Server — Architecture Overview

This document explains how the MibAuthorizationServer works internally: the OAuth/OIDC engine, the components, the endpoints, the token model, the database mapping and where each piece lives in the code. It is meant both as an onboarding entry point and as maintenance context.

For installation and the IdentityServer4 → OpenIddict upgrade, see AuthorizationServerInstallation.md. For client/token configuration keys, see AuthorizationServerConfiguration.md. For step-by-step request flows with sequence diagrams, see AuthorizationServerAuthenticationFlows.md.

What it is

MibAuthorizationServer (port 8601 by default) is the MIB platform's OAuth 2.0 / OpenID Connect provider. It authenticates users and services and issues tokens consumed by the rest of the platform (MibServer3/BFF, MibServerApi, microservices). It also hosts the user-administration pages and the password/2FA flows.

The OAuth/OIDC engine is OpenIddict 5.8.0 on .NET 8. It previously ran on IdentityServer4 (v3.1.4, EOL), which has been fully removed (see MEDIAIBOX-11999). The migration preserved the database schema, endpoints, grant types, RS512 signing and reference (opaque) access tokens, so the change is transparent to most clients.

High-level architecture

flowchart TB
    subgraph Clients
        SPA["React Auth UI"]
        BFF["MibServer3 / BFF"]
        API["MibServerApi and microservices"]
    end

    subgraph AuthServer["MibAuthorizationServer :8601"]
        direction TB
        subgraph OpenIddict
            Server["AddServer<br/>token / authorize / userinfo"]
            Validation["AddValidation<br/>local reference-token introspection"]
            Core["AddCore<br/>custom stores and managers"]
        end
        Login["Interactive login pipeline<br/>api/v2/auth + Connect/AuthorizationController"]
        Keys["MibKeyProvider<br/>signing and encryption keys"]
    end

    DB[("SQL Server<br/>API_CLIENTS, AUTH_GRANTS,<br/>AUTH_*_CREDENTIALS, AUTH_VALIDATION_KEYS,<br/>ADM_USER_LOGIN_HISTORY, users")]

    SPA --> Login
    BFF -->|"/oauth/authorize, /oauth/token"| Server
    API -->|"/oauth/token ROPC and client_credentials"| Server
    BFF -->|"Bearer reference token"| Validation
    Core --> DB
    Login --> DB
    Keys --> DB

The OpenIddict engine

Configured in ConfigureMibAuth.SetupOpenIddict (MediaiBox.Cms.Authorization.Server/AppStart/ConfigureMibAuth.cs) as three cooperating parts:

AddCore — persistence over the existing schema

OpenIddict's stores are replaced by custom stores that read/write the current MIB tables (no EF entities, no data migration):

OpenIddict concept Custom type Backing table
Application (client) MibApplicationStore / MibApplicationManager API_CLIENTS
Authorization (grant) MibAuthorizationStore AUTH_GRANTS
Token MibTokenStore AUTH_GRANTS
Scope MibScopeStore (static set, see below)

MibApplicationManager overrides ValidateClientSecretAsync to compare the plain-text secret stored in API_CLIENTS.OAUTH_CLIENT_SECRET (secret hashing is intentionally out of scope of the migration). The store getters never throw NotImplementedException because OpenIddict's manager cache calls them.

An authorization and its tokens share the AUTH_GRANTS table; the authorization row is distinguished by a sentinel value "openiddict-authorization".

AddServer — issuing tokens

  • Endpoints (see the reference table below) — both the historical /oauth/* paths and the OpenIddict-native /connect/* paths are registered.
  • Scopes: openid, email, profile, offline_access, mibapi.
  • Grants: authorization_code, password (ROPC), client_credentials, refresh_token.
  • Reference (opaque) access tokens (UseReferenceAccessTokens). Access-token JWE is disabled, but OpenIddict 5.x still encrypts refresh tokens and authorization codes as JWE — hence the mandatory encryption key (see Keys).
  • Rolling refresh tokens with zero reuse leeway by default (SetRefreshTokenReuseLeeway(TimeSpan.Zero)); the absolute/sliding and reuse/one-time model is read from MibIdentityConfig (ConfigureRefreshTokenModel).
  • Grant handlers (scoped event handlers on HandleTokenRequestContext): ClientCredentialsTokenHandler, PasswordGrantTokenHandler, RefreshTokenGrantHandler.
  • Parity handlers: InvalidClientResponseCodeHandler (maps OpenIddict's 401 on bad client to IdentityServer4's 400 invalid_client), AuthorizationErrorRedirectHandler (authorize-endpoint error redirect).
  • ASP.NET passthrough is enabled for the authorize and userinfo endpoints, so they are served by MVC controllers (Connect/AuthorizationController, Connect/UserInfoController) instead of OpenIddict's built-in handlers.

AddValidation — protecting resources

UseLocalServer() + UseAspNetCore(): the server validates its own reference tokens locally (no network introspection). This backs the MibApiProtection scheme (below).

Authentication schemes

Wired in Program.cs:

Scheme Purpose
Cookies (default) Interactive browser session created after login; consumed by the authorize endpoint to issue the authorization code. Cookie is protected with MibAesProtector; for /api/* paths a missing/expired session returns 401/403 instead of redirecting.
MibApiProtection (MibAuthSchemes.ApiProtection) A policy scheme that forwards to the OpenIddict validation scheme. Applied via [Authorize(AuthenticationSchemes = MibAuthSchemes.ApiProtection)] on ApiV1Controller, ApiV2BaseController and UserController to protect them with a reference access token.
OpenIddict Validation The actual reference-token validator the policy scheme forwards to. Also used directly by Connect/UserInfoController.

Keys

MibKeyProvider (MediaiBox.Cms.Authorization.MibIdentity/Stores/OpenIddict/MibKeyProvider.cs) owns three key materials, persisted in the database and bootstrapped on first start (BootstrapOpenIddictKeysAsync in Program.cs):

Key Algorithm Table Notes
Signing RS512 AUTH_SIGNING_CREDENTIALS Token signature; the public key is published at the JWKS endpoint. Stable across the migration.
Validation RS512 AUTH_VALIDATION_KEYS Validation key(s).
Encryption AES-256 (Aes256KW / Aes256CbcHmacSha512) AUTH_ENC_CREDENTIALS New. Encrypts refresh tokens and authorization codes (JWE). Must persist across restarts — an ephemeral key invalidates all live refresh tokens/codes on every restart.

OpenIddictKeyConfiguration (an IConfigureOptions<OpenIddictServerOptions>) injects the signing and encryption credentials into the server options at startup.

Endpoints

Purpose Path(s)
Discovery /.well-known/openid-configuration
JWKS /.well-known/openid-configuration/jwks
Token /oauth/token, /connect/token
Authorize /oauth/authorize, /connect/authorize
Userinfo /connect/userinfo
Introspection /connect/introspect
Revocation /connect/revocation
End session (logout) /connect/endsession
Interactive login (React) POST /api/v2/auth/login, /update-password, /totp, /forgot-password, /reset-password
Interactive logout /logout

The /oauth/token and /oauth/authorize paths come from MibAuthorizationServerConfig (tokenEndpoint/authorizeEndpoint) and default to those values; both they and the /connect/* aliases resolve to the same handlers.

Path-prefix deployments: when the server runs under a URL sub-path (customUrlBase, e.g. /MibAuth), every functional endpoint above is also registered under that prefix (/MibAuth/oauth/authorize, /MibAuth/connect/token, …) so it is reachable through the reverse proxy; the bare paths stay registered for same-origin and internal service-to-service callers. The discovery document is served at {prefix}/.well-known/openid-configuration and reflects the prefix. See Installation › Deploying under a URL sub-path.

Token model

  • Access token: reference (opaque) string; validated locally by the validation handler against AUTH_GRANTS. Lifetime AccessTokenLifetime (default 3600 s).
  • Refresh token: issued when offline_access is requested. Rolling (one-time) by default; absolute vs sliding expiration and reuse vs one-time are configurable (see Configuration). Encrypted as JWE.
  • Authorization code: short-lived (default 300 s), encrypted as JWE.
  • Token cleanup: an optional hosted service (OpenIddictTokenPruningService, enabled via tokenCleanUpServiceEnabled) prunes expired grants from AUTH_GRANTS.

Database tables

Table Used for
API_CLIENTS OAuth clients (id, plain-text secret, grant type, redirect URI). OAUTH_CLIENT_TYPE: 1 = authorization code, 3 = ROPC.
AUTH_GRANTS Authorizations and tokens (reference access, refresh, codes).
AUTH_SIGNING_CREDENTIALS RS512 signing key.
AUTH_VALIDATION_KEYS Validation keys.
AUTH_ENC_CREDENTIALS New — AES-256 token/code encryption key.
ADM_USER_LOGIN_HISTORY Login audit trail (IP, user agent, client id, outcome).
user/account tables Credentials, roles, 2FA/Latch/TOTP, preferences.

Code map (for maintenance)

Area Location
Engine wiring, schemes, keys, lifetimes MediaiBox.Cms.Authorization.Server/AppStart/ConfigureMibAuth.cs, Program.cs
Grant handlers …/Server/AppStart/{ClientCredentials,PasswordGrant,RefreshTokenGrant}…Handler.cs
Authorize / userinfo controllers …/Server/Controllers/Connect/
Interactive login API …/Server/Controllers/Api/AuthenticationV2Controller.cs + …/Helper/Authentication/ (LoginFinalizer, IAuthorizationContextParser, IReferenceTokenValidator, IUserGrantRevoker)
Custom stores / managers / key provider MediaiBox.Cms.Authorization.MibIdentity/Stores/OpenIddict/
Token lifetime / refresh config MediaiBox.Cms.Authorization.MibIdentity/Config/MibIdentityConfig.cs
Server-level config MediaiBox.Cms.Authorization.Model/Config/MibAuthorizationServerConfig.cs

Security notes

  • Client secrets are stored and compared in plain text in API_CLIENTS. Hashing is a tracked follow-up, not part of the current engine.
  • Forced re-login on engine change: grants serialized by IdentityServer4 are not readable by OpenIddict, so the cutover invalidates all live tokens once. This is the documented migration path (no compatibility shim).
  • Consent is automatic/inline for first-party clients when useNewLoginUI=true; there is no consent screen.