PrimebrickPrimebrick
  • Primebrick.dev
  • GitHub
  • Documentation
  • Services
  • Libraries
  • API Catalog
Resources
  • Landing Page
  • API Catalog
  • GitHub
PrimebrickPrimebrick

© 2026 Primebrick. MIT License.

github
Backend
    Audit SystemAuditable Joins and Display Name ResolutionAuditService and Delta CalculatorAuth Router: Login, Token Refresh, and User Management APIAuthentication and AuthorizationAuthentication Middleware and Token NormalizationCore ArchitectureCustomers API RouterCustomers Data Access LayerCustomers ModuleDatabase ManagementDocker Infrastructure and Casdoor SetupDomain Entity SystemExport LibraryGetting StartedGitFlow, Versioning, and CI HooksGlossaryInfrastructure and DevOpsMigration Patch Registry and ApplicationOpenAPI DocumentationOrganizations ModuleOverviewProject StructureRBAC Middleware and Permission SystemRepository and Query DSLSchema Snapshot and Diff SystemServer Bootstrap and Middleware PipelineUtility Scripts ReferenceREADME
Frontend
Microservices
powered by Zudoku
Backend

Authentication Middleware and Token Normalization

Authentication Middleware and Token Normalization

Relevant source files

The following files were used as context for generating this wiki page:

  • src/modules/auth/auth.middleware.ts
  • src/modules/auth/session-context.ts
  • src/modules/auth/token-normalizer.ts
  • src/modules/auth/types.ts

The authentication system in Primebrick v3 is designed to be identity-provider (IDP) agnostic, supporting both direct JWT validation and upstream API gateway trust modes. It ensures that regardless of the authentication source, downstream business logic receives a consistent AuthUser object mapped to an internal system UUID.

Authentication Modes

The middleware supports two distinct execution paths configured via the environment, converging into a single internal identity model src/modules/auth/auth.middleware.ts:20-21.

1. STANDALONE Mode

In this mode, the backend is responsible for verifying the authenticity of the credentials. It attempts to extract a JWT from two locations:

  1. Authorization Header: Bearer <jwt> src/modules/auth/auth.middleware.ts:99-102.
  2. HttpOnly Cookie: access_token (used primarily by the Svelte frontend) src/modules/auth/auth.middleware.ts:105-107.

The token is verified using openid-client and JWKS (JSON Web Key Sets) fetched from the IDP's OIDC discovery endpoint src/modules/auth/auth.middleware.ts:8-9, src/modules/auth/auth.middleware.ts:118-119.

2. GATEWAY Mode

In this mode, the backend trusts an upstream gateway (e.g., Kong, Tyk, APISIX) that has already performed authentication.

  • Anti-Spoofing: To prevent direct access to the API bypassing the gateway, a shared secret (X-Gateway-Secret) must be provided in the headers src/modules/auth/auth.middleware.ts:147-154.
  • Header Mapping: User identity, roles, and organization info are read directly from headers injected by the gateway src/modules/auth/auth.middleware.ts:156-176.

Auth Flow and Entity Mapping

The following diagram illustrates how external credentials (JWT or Headers) are transformed into the internal AuthUser type and propagated through the system.

Auth Data Flow: From Request to Session

Code
graph TD subgraph "Entry Points" [req_headers] --> |"Bearer JWT"| STANDALONE["fromStandalone()"] [req_cookies] --> |"access_token"| STANDALONE [gateway_headers] --> |"X-User-*"| GATEWAY["fromGateway()"] end STANDALONE --> |"JWT Claims"| TN["normalizeIdpToken()"] GATEWAY --> |"Header Strings"| TN subgraph "Normalization & Enrichment" TN --> |"NormalizedIdpUser"| UPR["resolveInternalUuid()"] UPR --> |"internalUuid"| EP["expandPermissions()"] EP --> |"permissions + isAdmin"| BAU["buildAuthUser()"] end BAU --> |"AuthUser"| REQ["req.user"] REQ --> |"Session"| ALS["runWithSession()"] Sources["Sources: src/modules/auth/auth.middleware.ts:68-88, src/modules/auth/token-normalizer.ts:69-134"]

Token Normalization

Different IDPs (Casdoor, Keycloak, Entra ID) structure JWT claims differently, especially roles. The token-normalizer.ts provides a robust mechanism to flatten these differences.

AUTH_ROLES_PATH

The system uses a configurable dotted path (e.g., realm_access.roles for Keycloak or roles for Casdoor) to locate role data within the JWT src/modules/auth/token-normalizer.ts:10-12. The readPath function traverses the JSON payload to extract this data src/modules/auth/token-normalizer.ts:24-31.

Role Coercion

The coerceRoles function handles various role formats:

  • Flat string arrays: ["admin", "user"]
  • Object arrays: [{ "name": "admin" }]
  • Mixed or null values are safely stringified or filtered src/modules/auth/token-normalizer.ts:40-51.

Internal UUID Resolution

A critical security boundary is the separation of the IDP's sub (subject) claim from the internal system ID.

  • idp_code: The original JWT sub is stored for traceability src/modules/auth/types.ts:19-20.
  • id: The resolveInternalUuid function maps the idp_code to a local user_profiles.uuid src/modules/auth/auth.middleware.ts:130-136. If the user does not exist, they are Just-In-Time (JIT) provisioned.

Sources: src/modules/auth/token-normalizer.ts:1-134, src/modules/auth/auth.middleware.ts:129-142.

Session Context Propagation

Primebrick uses AsyncLocalStorage (ALS) to provide the authenticated context to downstream services without polluting every function signature with a user parameter src/modules/auth/session-context.ts:4-9.

AuthUser vs Session

  • AuthUser: Attached to req.user for Express-level access src/modules/auth/types.ts:16-32.
  • Session: A subset of AuthUser mirrored into ALS src/modules/auth/session-context.ts:42-76.

Usage in Code

  • Middleware Setup: The authMiddleware calls runWithSession(session, () => next()) to wrap the entire request lifecycle src/modules/auth/auth.middleware.ts:81-88.
  • DAL/Repository Access: Data access layers call requireActor() to retrieve the current user's UUID for audit columns like created_by or updated_by src/modules/auth/session-context.ts:104-114.
  • System Operations: For background jobs or migrations where no HTTP request exists, runAsSystem() provides a synthetic session with the actor set to "system" src/modules/auth/session-context.ts:130-135.

Entity Mapping: Code Structures

Code EntityPurposeFile
AuthUserInterface for the user attached to req.usersrc/modules/auth/types.ts:16
SessionInterface for data stored in AsyncLocalStoragesrc/modules/auth/session-context.ts:42
normalizeIdpTokenLogic for extracting user data from IDP-specific JWTssrc/modules/auth/token-normalizer.ts:69
loadRoleMappingsLoads role_mappings table into a memory cachesrc/modules/auth/auth.middleware.ts:51
requireActorHelper to get the current actor UUID from the sessionsrc/modules/auth/session-context.ts:104

Sources: src/modules/auth/session-context.ts:1-136, src/modules/auth/auth.middleware.ts:1-93


Last modified on July 13, 2026
Authentication and AuthorizationCore Architecture
On this page
  • Authentication Modes
    • 1. STANDALONE Mode
    • 2. GATEWAY Mode
    • Auth Flow and Entity Mapping
  • Token Normalization
    • AUTH_ROLES_PATH
    • Role Coercion
    • Internal UUID Resolution
  • Session Context Propagation
    • AuthUser vs Session
    • Usage in Code