OpenAPI security schemes — declaring JWT auth in Swagger
Source: markstack/src/openapi Category: Snippet — OpenAPI
OpenAPI security schemes — declaring your API’s auth method in OpenAPI so Swagger UI knows what to do. Two blocks: a securitySchemes definition at the top level, and a security reference on each protected route (or globally). Swagger UI uses this to render a padlock icon and an “Authorize” button that puts the token in subsequent requests.
Define the scheme once
Section titled “Define the scheme once”In your top-level OpenAPI spec (or the swagger-jsdoc setup):
const options = { definition: { openapi: '3.0.3', info: { title: 'markstack API', version: '1.0.0' },
components: { securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', }, }, }, }, apis: ['./src/routes/*.ts'],};Apply globally (every route requires auth by default)
Section titled “Apply globally (every route requires auth by default)” // ...definition... security: [{ bearerAuth: [] }],Every route in the spec now requires Authorization: Bearer <token>. Exceptions (login, register, public docs) add an empty security override:
/** * @swagger * /auth/login: * post: * security: [] # no auth for login * summary: Log in * ... */Apply per-route (opt-in)
Section titled “Apply per-route (opt-in)”If most of your API is public, the opposite — only tag authed routes:
/** * @swagger * /bookmarks: * post: * summary: Create a bookmark * security: * - bearerAuth: [] * requestBody: * ... */Swagger UI effects
Section titled “Swagger UI effects”With this in place:
- Each authed route shows a 🔒 in the UI
- An “Authorize” button appears at the top; clicking it prompts for the token
- After authorizing, “Try it out” buttons include
Authorization: Bearer <token>automatically
Other common schemes
Section titled “Other common schemes”// API key in a headerapiKeyAuth: { type: 'apiKey', in: 'header', name: 'X-API-Key',},
// OAuth2oauth2: { type: 'oauth2', flows: { authorizationCode: { authorizationUrl: 'https://example.com/oauth/authorize', tokenUrl: 'https://example.com/oauth/token', scopes: { read: 'Read', write: 'Write' }, }, },},
// Basic auth (just for completeness; avoid in modern APIs)basicAuth: { type: 'http', scheme: 'basic' },Gotchas
Section titled “Gotchas”bearerFormat: 'JWT'is informational. Swagger doesn’t validate the token shape; it just shows JWT in the UI as a hint.- Global
securityat the top + per-routesecurity: []is the pattern most APIs want — most endpoints are private, a handful are public. - Swagger UI loses the token on page refresh. It stores in sessionStorage by default. For dev-only APIs, fine; for demos you want shareable, consider passing the token in the URL (there’s a
preauthorizeApiKeyhook). - Multiple schemes. An endpoint can accept either of two auth methods:
security: [{ bearerAuth: [] }, { apiKeyAuth: [] }]. Array = OR (any works); object inside array = AND (all must be supplied). - Scopes for OAuth2. The empty array
[]means “no specific scopes required”. For scoped APIs:[{ oauth2: ['read:bookmarks'] }]. - The UI’s “Authorize” form. For bearer it’s one field; for OAuth2 it’s a whole flow; for api key it’s a single value. Users get confused if the UI form doesn’t match what your server expects. Test.
- Don’t document the login endpoint as secured. Obvious, but the global
securitydefault catches it; remember to override withsecurity: []. - Docs at
/api/docsshould be public. Not strictly a security scheme issue — just a reminder that the docs endpoint itself shouldn’t require auth.
See also
Section titled “See also”- patterns/swagger-jsdoc-inline — where these schemes live in the source
- patterns/jwt-refresh-rotation — what the bearer token represents