Olympus Docs
CookbookIntegrations & billing

Document your API auth in OpenAPI

Make OAuth2 / OIDC discoverable in your API spec

If you ship an API, OpenAPI (Swagger) spec documents it. Including auth scheme makes integration easy for consumers.

OAuth2 in OpenAPI

openapi: 3.0.3
info:
  title: Your API
  version: 1.0.0

components:
  securitySchemes:
    olympus_oauth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://ciam.your-domain.com/oauth2/auth
          tokenUrl: https://ciam.your-domain.com/oauth2/token
          refreshUrl: https://ciam.your-domain.com/oauth2/token
          scopes:
            "openid": "OpenID identification"
            "offline_access": "Maintain refresh token"
            "profile": "Basic profile info"
            "email": "Email address"
            "orders:read": "Read orders"
            "orders:write": "Modify orders"

security:
  - olympus_oauth2: [openid, email]

paths:
  /orders:
    get:
      security:
        - olympus_oauth2: [orders:read]
      responses:
        '200':
          description: List of orders
    post:
      security:
        - olympus_oauth2: [orders:write]
      responses:
        '201':
          description: Order created

Consumers see required scopes per endpoint.

Swagger UI

Render OpenAPI with Swagger UI:

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
</head>
<body>
  <div id="ui"></div>
  <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
  <script>
    SwaggerUIBundle({
      url: "/openapi.yaml",
      dom_id: "#ui",
      oauth2RedirectUrl: window.location.origin + "/swagger-oauth2-redirect.html",
    });
    ui.initOAuth({
      clientId: "swagger-ui-client",
      scopes: "openid offline_access",
      usePkceWithAuthorizationCodeGrant: true,
    });
  </script>
</body>
</html>

Developers can "Try it out" with their own OAuth flow.

Bearer token (for service accounts)

components:
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      bearerFormat: JWT  # or opaque

paths:
  /admin/...:
    get:
      security:
        - bearer: []

For server-to-server callers using client_credentials.

OIDC discovery

components:
  securitySchemes:
    olympus_oidc:
      type: openIdConnect
      openIdConnectUrl: https://ciam.your-domain.com/.well-known/openid-configuration

Auto-discovery: tools fetch the well-known endpoint to learn flow URLs, scopes, etc.

Hosting

Generate from your code:

// Generated from JSDoc / TypeScript types
import { OpenAPIObject } from "@asteasolutions/zod-to-openapi";

const spec: OpenAPIObject = generateOpenAPI();

Tools per language:

  • TypeScript: zod-to-openapi, drizzle-zod-to-openapi.
  • Python: FastAPI auto-generates.
  • Go: swag.
  • Rust: utoipa.

Serve at /openapi.json and /openapi.yaml.

Per-endpoint scopes documentation

For each endpoint:

paths:
  /orders/{id}:
    delete:
      summary: Cancel an order
      security:
        - olympus_oauth2: [orders:write]
      parameters:
        - name: id
          in: path
          required: true
      responses:
        '204':
          description: Order cancelled
        '403':
          description: |
            Missing required scope: `orders:write`
            Or: caller is not the order owner / admin.

Document not only scope but also custom authz.

Documenting custom claims

If your API uses custom claims:

components:
  schemas:
    AccessTokenClaims:
      type: object
      properties:
        sub:
          type: string
          description: User UUID
        scope:
          type: string
          description: Space-separated scopes
        tenant_id:
          type: string
          description: |
            Custom Olympus claim, identifies which tenant the user belongs to.
            Use to scope queries.

Helpful for API consumers writing their own validation.

Reusable security scheme refs

In multiple endpoints, refer to one scheme:

paths:
  /orders:
    get:
      security:
        - $ref: '#/components/securitySchemes/olympus_oauth2'
          scopes: [orders:read]

Centralized definition; reused everywhere.

Validating requests

OpenAPI middleware validates incoming requests against spec:

import { OpenAPIBackend } from "openapi-backend";

const api = new OpenAPIBackend({ definition: "openapi.yaml" });
api.register({
  validationFail: (c, req, res) => res.status(400).json({ error: c.validation.errors }),
  notImplemented: (c, req, res) => res.status(501),
  notFound: (c, req, res) => res.status(404),
});
app.use(api.handler);

Type mismatch / missing scope → automatic rejection.

Versioning the spec

info:
  version: 1.4.0
  description: |
    See [changelog](/docs/changelog) for version history.

When you ship a new API version (v2), new spec file (openapi-v2.yaml).

Client generation

Consumers can generate client libraries:

openapi-generator-cli generate -i https://your-domain.com/openapi.yaml -g typescript-axios -o ./your-api-client

Get a typed client for free. Updates with each spec change.

On this page