Securing REST APIs with Swagger: Implementing OAuth2 Authorization Code Flow
Implementing OAuth2 Authorization Code flow in Swagger is straightforward: declare the flow in the spec, configure Swagger UI, and add a stateless JWT validator. Follow the checklist to avoid common pitfalls and know when to redesign.
24 Jul 2025, 22:16 UTC

Problem & Takeaway
When exposing a public REST API, a common requirement is to protect endpoints with OAuth2 while keeping the API stateless and easy to consume. Swagger (OpenAPI) lets you declare the OAuth2 Authorization Code flow once in the spec, and Swagger UI can then drive the entire authentication journey. The key decision is how to map this flow into a minimal, secure implementation that respects trust boundaries and operational realities.
Requirements
- Stateless API that accepts bearer tokens in the
Authorizationheader. - Support for scopes to enforce fine‑grained access control.
- Secure transport (HTTPS) for both the authorization and token exchanges.
- Clear separation of concerns: OAuth2 provider, API server, and client UI.
- Ability to revoke tokens and handle expiration.
Smallest Suitable Design
The design centers on a single securityScheme in the OpenAPI spec and a lightweight middleware on the API side. This keeps the API stateless and leverages existing OAuth2 providers (e.g., Auth0, Keycloak, or an internal provider).
OpenAPI Spec – Defining the Scheme
components:
securitySchemes:
oauth2_auth_code:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/authorize
tokenUrl: https://auth.example.com/token
scopes:
read: "Read data"
write: "Write data"
security:
- oauth2_auth_code: [read, write]
Place this snippet in openapi.yaml or openapi.json. The security field applies the scheme globally; you can override per‑operation if needed.
Swagger UI Configuration
Run Swagger UI against the spec and supply the same OAuth2 URLs. Example swagger-ui-dist configuration:
const ui = SwaggerUIBundle({
url: '/openapi.yaml',
dom_id: '#swagger-ui',
oauth2RedirectUrl: window.location.origin + '/oauth2-redirect.html',
requestInterceptor: req => {
if (req.headers) {
req.headers['Authorization'] = 'Bearer ' + localStorage.getItem('access_token');
}
return req;
},
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
});
When users click Authorize, Swagger UI opens the provider’s login page, receives the authorization code, exchanges it for a token, and stores the access token in localStorage for subsequent requests.
API Middleware – Token Validation
Use a stateless JWT validator or introspection endpoint. Example Node.js middleware with express-jwt:
const { expressjwt: jwt } = require('express-jwt');
const jwksRsa = require('jwks-rsa');
app.use(jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksUri: 'https://auth.example.com/.well-known/jwks.json'
}),
audience: 'api://default',
issuer: 'https://auth.example.com/',
algorithms: ['RS256']
}));
This middleware rejects requests without a valid token, returning 401 Unauthorized. It also attaches the decoded token to req.user for downstream handlers to enforce scopes.
Trust & Data Boundaries
- Client ↔ OAuth Provider: The client (Swagger UI) exchanges the code for a token directly with the provider over HTTPS. The API server never sees the code.
- API Server ↔ OAuth Provider: The server validates the token either via JWT verification or introspection. If introspection is used, the server must authenticate itself to the provider (client secret) – a secret that must be stored securely (e.g., environment variable, secret manager).
- Tokens are short‑lived; the server should treat any token not matching the expected audience or issuer as invalid.
Operational Checks
- Spec Validation – Run
swagger-cli validate openapi.yamlto ensure thecomponents.securitySchemesblock is syntactically correct. - Swagger UI Flow – Open the UI, click Authorize, log in, and confirm that the token appears in the
Authorizationheader of subsequent requests. - Endpoint Test – Send a
GET /datarequest with the token usingcurl -H "Authorization: Bearer <token>" https://api.example.com/dataand expect200 OK. Repeat without the header and expect401 Unauthorized. - Verify that the token’s
expclaim is respected; a request with an expired token should fail.
Failure Modes
- Missing HTTPS: If the authorization or token endpoints are served over HTTP, the authorization code can be intercepted. Enforce TLS in the provider configuration.
- Token Revocation Ignored: Revoked tokens may still be accepted until they expire. Implement introspection if real‑time revocation is required, or use short token lifetimes.
- PKCE Not Used: Public clients (like a browser‑based Swagger UI) are vulnerable to code interception attacks. Modern providers support PKCE automatically; ensure it is enabled.
- Scope Mis‑assignment: If the API server does not check scopes, a token with
writecould accessreadendpoints. Add explicit scope checks in route handlers.
When to Redesign
- High‑Risk Clients: If the API is consumed by native mobile apps or other non‑browser clients, consider using the Authorization Code flow with PKCE or the Client Credentials flow instead.
- Token Size Constraints: For very large JWTs, switch to opaque tokens with introspection to reduce header size.
- Multi‑Tenant Architecture: If each tenant requires a separate audience, adjust the
audienceclaim validation or use separate security schemes per tenant. - Regulatory Requirements: If the API must log every authentication event, integrate an audit trail into the middleware.
Practical Checklist
| Check | How to Verify | Risk |
|---|---|---|
| HTTPS Enabled | Open https://auth.example.com/authorize in browser. | Code interception. |
| Swagger UI Authorize Flow | Observe token in localStorage after login. | Token not sent to API. |
| Token Validation | Send request with invalid token; expect 401. | Unauthorized access. |
| Scope Enforcement | Attempt write action with read token; expect 403. | Privilege escalation. |
Conclusion
By declaring a single OAuth2 Authorization Code flow in the OpenAPI spec and wiring a lightweight JWT validator, you achieve a stateless, secure API that integrates seamlessly with Swagger UI. Regular operational checks and awareness of failure modes keep the design robust, while the clear trust boundaries ensure that only authenticated, scoped requests reach your business logic.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.