zmdbzero-maintenance data layer
Docs Benchmarks Anti-patterns OpenAPI
Docs / Validation and contracts

Security SchemesSupported

OpenAPI security is part of the shared HTTP contract. The declaration, runtime router, OpenAPI renderer, and generated client all consume the same HttpOperationIR.security; the renderer does not reconstruct requirements from controllers or guards.

Declare schemes and requirements#

import { defineHttpContract, httpOperation, type SecurityScheme } from '@zmdb/web/contract';

const SCHEMES = {
  bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
  headerKey: { type: 'apiKey', in: 'header', name: 'x-api-key' },
  mesh: { type: 'mutualTLS' },
  oauth2: {
    type: 'oauth2',
    flows: {
      authorizationCode: {
        authorizationUrl: 'https://id.example.com/oauth/authorize',
        tokenUrl: 'https://id.example.com/oauth/token',
        scopes: {
          'users:read': 'Read users',
          'users:write': 'Create and change users',
        },
      },
    },
  },
  oidc: {
    type: 'openIdConnect',
    openIdConnectUrl: 'https://id.example.com/.well-known/openid-configuration',
  },
} as const satisfies Record<string, SecurityScheme>;

export const HTTP_CONTRACT = defineHttpContract({
  securitySchemes: SCHEMES,
  operations: {
    createUser: httpOperation<CreateUserOperation>({
      controller: UsersController,
      handler: 'create',
      method: 'POST',
      path: '/users',
      parameters: [],
      requestBody: { kind: 'json', mediaType: 'application/json', required: true },
      responses: {
        201: { description: 'Created', body: { kind: 'json', mediaType: 'application/json' } },
      },
      security: [{ bearerAuth: ['users:write'], headerKey: [] }],
      version: { kind: 'none' },
      deprecated: false,
    }),
  },
});

One requirement object means every named scheme is required. Several requirement objects mean alternatives:

security: [{ bearerAuth: ['users:write'] }, { headerKey: [] }];

An empty array means explicitly public:

security: [];

Scheme vocabulary#

SecurityScheme covers every OpenAPI 3.1 scheme type:

The HTTP union keeps basic and bearer separate, so bearerFormat cannot be attached to basic authentication. OAuth2 requires at least one flow and every flow has a scopes map. refreshUrl is optional.

Never place a token, password, key, certificate, or client secret in the scheme record. It describes how credentials travel; credentials are supplied at runtime.

Runtime agreement#

Register the compiled contract with the same route options and guards the application runs:

const compiled = compileHttpContracts(sources, { session });
const router = createRouter({ guardRegistry: GUARD_REGISTRY });

router.registerContract(compiled, [new UsersController()], ROUTES);

registerContract refuses startup when:

Security-aware guards still declare { scheme, scopes }, and several guards on one route merge into one AND requirement. A legacy guard may use RouteOptions.security, but that value must equal the contract operation. The comparison happens at registration; OpenAPI simply emits the already-verified contract.

Generated document#

Schemes appear under components.securitySchemes. Every operation has its own security, including public operations:

{
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "paths": {
    "/users": {
      "post": {
        "security": [{ "bearerAuth": ["users:write"] }]
      }
    },
    "/status": {
      "get": {
        "security": []
      }
    }
  }
}

There is no document-level default. A reviewer can read each operation without resolving inheritance, and a missing or undeclared scheme is a generation error.

@Public() remains auditable#

@Public() is the runtime marker for an unauthenticated handler. Contract compilation requires the matching operation to declare security: [], and contract registration checks the same decision.

From an application root:

rg -n '^\s*@Public\(\)' src --glob '*.ts'

The generated-document audit is the set of operations carrying security: [].

---

See also: Generated client authentication · Guards · OpenAPI Generation · OpenAPI Operations · Authentication