Skip to content

Architecture

System Overview

CRED Auth acts as the centralized identity provider for the CRED platform. It issues JWT access tokens and ID tokens that are consumed by downstream services (cred-api-commercial, cred-agent, MCP server, etc.).

flowchart TB
    subgraph auth["CRED Auth"]
        direction TB

        subgraph controllers["HTTP Controllers"]
            WK["Well-Known Controller<br/>.well-known/*"]
            AC["Auth Controller<br/>/auth/*"]
            APP["App Controller<br/>GET /"]
        end

        subgraph oidc["OIDC Provider โ€” nest-oidc-provider"]
            EP["/oauth/auth ยท /oauth/token ยท /oauth/jwks ยท /oauth/reg<br/>/oauth/me ยท /oauth/token/introspection ยท /oauth/token/revocation"]
        end

        subgraph services["Services"]
            IC["Interaction Controller<br/>/login/:uid/*"]
            US["Users Service<br/>Knex โ†’ User"]
            JWT["JWT Service<br/>HS256 / RS256"]
        end

        subgraph db["PostgreSQL โ€” Knex"]
            TABLES["User table ยท OAuthPayload table"]
        end

        subgraph sec["Security Layer"]
            GUARDS["ThrottlerGuard ยท CsrfGuard ยท Helmet ยท ValidationPipe"]
        end

        controllers --> oidc --> services --> db
    end

Core Modules

App Module (src/app.module.ts)

Root module that composes all feature modules. Imports ConfigModule.forRoot() for environment variable access.

OIDC Module (src/oidc/)

The heart of the authorization server. Wires oidc-provider into NestJS via nest-oidc-provider.

Component File Purpose
OidcConfigService oidc.config.service.ts Builds the full oidc-provider Configuration โ€” adapter, cookies, JWKS, TTLs, features, claims, findAccount, PKCE policy
WellKnownController well-known.controller.ts Serves /.well-known/openid-configuration, oauth-authorization-server, and oauth-protected-resource
InteractionController interactions/interaction.controller.ts Login/consent UI โ€” GET /login/:uid, POST submit/confirm/abort
InteractionService interactions/interaction.service.ts Validates credentials, loads interaction details, finishes login/consent/abort via provider.interactionFinished
KnexAdapter adapters/knex.adapter.ts oidc-provider persistence adapter โ€” stores sessions, tokens, clients, grants in the OAuthPayload table
OidcErrorLoggerService oidc-error-logger.service.ts Provider event listeners, proxy configuration, custom redirect URI rules for MCP desktop schemes

Auth Module (src/auth/)

REST-based JWT authentication for direct API access (separate from OIDC flows).

Component File Purpose
AuthController auth.controller.ts POST /auth/login (get JWT), GET /auth/me (get profile)
AuthService auth.service.ts Thin wrapper around UsersService for Passport
JwtService jwt.service.ts Sign/verify JWTs โ€” auto-detects HS256 (JWT_SECRET) or RS256 (OAUTH_JWKS_PRIVATE_KEY)
LocalStrategy strategies/local.strategy.ts Passport local strategy (email + password)
JwtAuthGuard guards/jwt-auth.guard.ts Bearer token verification guard

Users Module (src/users/)

Component File Purpose
UsersService users.service.ts Queries the "User" table via Knex. Provides findByEmail, findById, validateCredentials (bcrypt, timing-safe). Rejects inactive users.

Database Module (src/database/)

Component File Purpose
KnexModule knex.module.ts Global module providing KNEX_CONNECTION token. Connects to PostgreSQL via DB_CONNECTION with optional DB_SSL support.

Security Module (src/security/)

Component File Purpose
SecurityModule security.module.ts Global module โ€” registers ThrottlerGuard as APP_GUARD, exports CsrfService and CsrfGuard
CsrfService csrf/csrf.service.ts Stateless HMAC-signed CSRF tokens with secret rotation support
CsrfGuard csrf/csrf.guard.ts Validates _csrf body field or X-CSRF-Token header; session ID from route :uid or X-Session-Id
Throttle Decorators decorators/throttle.decorator.ts @ThrottleLogin, @ThrottleApi, @ThrottleRelaxed, @ThrottleDiscovery

Project Structure

src/
โ”œโ”€โ”€ main.ts                         # Bootstrap (Helmet, CORS, ValidationPipe, HBS, trust proxy)
โ”œโ”€โ”€ app.module.ts                   # Root module
โ”œโ”€โ”€ app.controller.ts               # GET / โ€” service metadata
โ”œโ”€โ”€ auth/
โ”‚   โ”œโ”€โ”€ auth.module.ts
โ”‚   โ”œโ”€โ”€ auth.controller.ts          # POST /auth/login, GET /auth/me
โ”‚   โ”œโ”€โ”€ auth.service.ts
โ”‚   โ”œโ”€โ”€ jwt.service.ts              # JWT sign/verify (HS256 or RS256)
โ”‚   โ”œโ”€โ”€ dto/login.dto.ts
โ”‚   โ”œโ”€โ”€ guards/jwt-auth.guard.ts
โ”‚   โ””โ”€โ”€ strategies/local.strategy.ts
โ”œโ”€โ”€ database/
โ”‚   โ””โ”€โ”€ knex.module.ts              # Global Knex connection
โ”œโ”€โ”€ oidc/
โ”‚   โ”œโ”€โ”€ oidc.module.ts              # OidcModule.forRootAsync
โ”‚   โ”œโ”€โ”€ oidc.config.service.ts      # Full oidc-provider Configuration
โ”‚   โ”œโ”€โ”€ well-known.controller.ts    # Discovery endpoints
โ”‚   โ”œโ”€โ”€ oidc-error-logger.service.ts
โ”‚   โ”œโ”€โ”€ adapters/
โ”‚   โ”‚   โ””โ”€โ”€ knex.adapter.ts         # oidc-provider DB adapter
โ”‚   โ””โ”€โ”€ interactions/
โ”‚       โ”œโ”€โ”€ interaction.controller.ts
โ”‚       โ”œโ”€โ”€ interaction.service.ts
โ”‚       โ””โ”€โ”€ dto/
โ”œโ”€โ”€ security/
โ”‚   โ”œโ”€โ”€ security.module.ts          # ThrottlerGuard + CSRF
โ”‚   โ”œโ”€โ”€ csrf/
โ”‚   โ”‚   โ”œโ”€โ”€ csrf.service.ts
โ”‚   โ”‚   โ””โ”€โ”€ csrf.guard.ts
โ”‚   โ”œโ”€โ”€ decorators/
โ”‚   โ”‚   โ””โ”€โ”€ throttle.decorator.ts
โ”‚   โ””โ”€โ”€ index.ts
โ””โ”€โ”€ users/
    โ”œโ”€โ”€ users.module.ts
    โ””โ”€โ”€ users.service.ts
views/
โ”œโ”€โ”€ layouts/main.hbs                # Base layout
โ”œโ”€โ”€ login.hbs                       # Login/consent page
โ””โ”€โ”€ error.hbs                       # Error page

JWT Signing Strategy

CRED Auth supports two JWT signing methods, auto-detected at startup:

Priority Method Algorithm When Used Compatible With
1 JWT_SECRET HS256 Symmetric shared secret cred-api-commercial (same secret)
2 OAUTH_JWKS_PRIVATE_KEY RS256 Asymmetric RSA key pair Any service verifying via JWKS endpoint

Both the REST /auth/login endpoint and the OIDC access token flow use the same signing configuration, ensuring all tokens issued by the server are verifiable through a single code path.

Note

When JWT_SECRET is set, it takes precedence. OIDC ID tokens are always signed with RS256 from the JWKS keystore regardless of this setting.

Data Flow: Authorization Code + PKCE

sequenceDiagram
    participant C as Client
    participant A as CRED Auth
    participant DB as PostgreSQL

    C->>A: GET /oauth/auth + PKCE challenge
    A->>DB: Store Interaction
    A-->>C: 302 โ†’ /login/:uid

    C->>A: GET /login/:uid
    A-->>C: HTML (login form)

    C->>A: POST /login/:uid/submit + email, password, _csrf
    A->>DB: Validate credentials
    DB-->>A: User record
    A->>DB: Store Grant
    A-->>C: 302 โ†’ /callback?code=โ€ฆ

    C->>A: POST /oauth/token + code + code_verifier
    A->>DB: Verify code + PKCE
    A-->>C: access_token, id_token, refresh_token