OpenAPI generator
WIPauthBuildOpenApiSpec + authRenderOpenApiYaml - emit an OpenAPI 3.1 spec for the mounted auth routes.
What it does
duck-auth's OpenAPI generator introspects your AuthEngine instance and
emits an OpenAPI 3.1 specification for every mounted route - /signin,
/signout, /session, and one /providers/:providerId/begin per
registered provider. The spec includes request schemas, response
schemas, every typed AuthError code, and security schemes.
Use cases:
- Generate a typed client SDK for partners.
- Drive contract tests in CI.
- Render an interactive API console (Swagger UI / Stoplight) for your integrators.
- Validate request shapes at the gateway before they reach your service.
Generate from CLI
The simplest path:
bunx @gentleduck/auth emit-openapi --out openapi.yaml
The CLI loads your auth.ts, calls authBuildOpenApiSpec, and writes the
result to disk.
Flags:
--out <path>- file destination. Default: stdout.--format yaml|json- serialisation. Default: yaml.--prefix <path>- route prefix (default/auth).--include <pattern>- only operations matching this glob.
Generate programmatically
import { authBuildOpenApiSpec, authRenderOpenApiYaml } from '@gentleduck/auth/openapi'
import { auth } from './lib/auth'
const spec = authBuildOpenApiSpec({
baseUrl: 'https://api.example.com',
title: 'Example Auth',
version: '1.0.0',
prefix: '/auth',
providers: ['password', 'magic-link', 'google', 'github', 'passkey'],
})
console.log(authRenderOpenApiYaml(spec))
Or write to disk:
import { writeFile } from 'node:fs/promises'
const yaml = authRenderOpenApiYaml(spec)
await writeFile('openapi.yaml', yaml, 'utf-8')
What's in the spec
The generated spec includes:
Paths
| Path | Methods |
|---|---|
/auth/signin | POST |
/auth/signout | POST |
/auth/session | GET |
/auth/providers/{providerId}/begin | POST |
/auth/providers/{providerId}/callback | GET (OAuth callback) |
/.well-known/openid-configuration | GET (if OIDC discovery enabled) |
/.well-known/jwks.json | GET (if JwtTransport with asym keys) |
Schemas
Session- sid, expiresAt, aal, amr, kind, identityId.Identity- id, email, emailVerified, profile (typed by yourProfilegeneric).AuthError- code, status, meta. OneoneOfentry perAUTH/*code.- Per-provider input schemas -
PasswordSignInInput,MagicLinkSignInInput,OAuthSignInInput, etc.
Security schemes
cookieAuth-apiKeyincookienamed after your CookieTransport.bearerAuth-httpwithbearerscheme.jwtAuth-httpwithbearerandbearerFormat: JWT.apiKeyAuth-apiKeyinheadernamedX-API-Key.
Each operation references the security schemes that apply.
Driving a typed SDK
A common workflow:
# 1. Emit the spec
bunx @gentleduck/auth emit-openapi --out openapi.yaml
# 2. Generate a TypeScript client (using openapi-typescript)
bunx openapi-typescript openapi.yaml -o sdk/types.ts
# 3. Use it
import type { paths } from './sdk/types'
type SignInResponse = paths['/auth/signin']['post']['responses']['200']['content']['application/json']
For richer codegen (operations as fetch wrappers), try
openapi-fetch or oazapfts on top of the same openapi.yaml.
Customising the spec
Override or add fields after authBuildOpenApiSpec:
const spec = authBuildOpenApiSpec({ baseUrl: '...', title: 'My API' })
spec.info.contact = { name: 'API team', email: 'api@example.com' }
spec.info.license = { name: 'MIT', url: 'https://opensource.org/license/mit' }
spec.servers = [
{ url: 'https://api.example.com', description: 'Production' },
{ url: 'https://staging.example.com', description: 'Staging' },
]
// Add a custom security requirement to all operations
for (const path of Object.values(spec.paths)) {
for (const op of Object.values(path)) {
if (typeof op === 'object' && 'security' in op) {
op.security = [...(op.security ?? []), { 'rate-limit-key': [] }]
}
}
}
The shape conforms to OpenAPI 3.1, so any spec-aware tooling consumes it without translation.