Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Middlewares

Middlewares process requests before they reach dispatchers and can modify responses on the way back. They’re used for cross-cutting concerns like authentication, rate limiting, and caching.

Overview

Middlewares are configured with x-barbacane-middlewares:

x-barbacane-middlewares:
  - name: <middleware-name>
    config:
      # middleware-specific config

Middleware Chain

Middlewares execute in order:

Request  →  [Global MW 1]  →  [Global MW 2]  →  [Operation MW]  →  Dispatcher
                                                                        │
Response ←  [Global MW 1]  ←  [Global MW 2]  ←  [Operation MW]  ←───────┘

Global vs Operation Middlewares

Global Middlewares

Apply to all operations:

openapi: "3.1.0"
info:
  title: My API
  version: "1.0.0"

# These apply to every operation
x-barbacane-middlewares:
  - name: request-id
    config:
      header: X-Request-ID
  - name: cors
    config:
      allowed_origins: ["https://app.example.com"]

paths:
  /users:
    get:
      # Inherits global middlewares
      x-barbacane-dispatch:
        name: http
        config:
          upstream: backend

Operation Middlewares

Apply to specific operations (run after global):

paths:
  /admin/users:
    get:
      x-barbacane-middlewares:
        - name: auth-jwt
          config:
            required: true
            scopes: ["admin:read"]
      x-barbacane-dispatch:
        name: http
        config:
          upstream: backend

Override Global Middleware

Operation middlewares can override global config by name:

# Global: 100 requests/minute
x-barbacane-middlewares:
  - name: rate-limit
    config:
      requests_per_minute: 100

paths:
  /public/feed:
    get:
      # Override: 1000 requests/minute for this endpoint
      x-barbacane-middlewares:
        - name: rate-limit
          config:
            requests_per_minute: 1000

Authentication Middlewares

jwt-auth

Validates JWT tokens with RS256/HS256 signatures.

x-barbacane-middlewares:
  - name: jwt-auth
    config:
      secret: "your-hs256-secret"  # For HS256
      # OR
      public_key: |                 # For RS256
        -----BEGIN PUBLIC KEY-----
        ...
        -----END PUBLIC KEY-----
      issuer: https://auth.example.com
      audience: my-api
      required_claims:
        - sub
        - email

Configuration

PropertyTypeDefaultDescription
secretstring-HS256 secret key
public_keystring-RS256 public key (PEM format)
issuerstring-Expected iss claim
audiencestring-Expected aud claim
required_claimsarray[]Claims that must be present
leewayinteger0Seconds of clock skew tolerance

Context Headers

Sets headers for downstream:

  • x-auth-sub - Subject (user ID)
  • x-auth-claims - Full JWT claims as JSON

apikey-auth

Validates API keys from header or query parameter.

x-barbacane-middlewares:
  - name: apikey-auth
    config:
      key_location: header        # or "query"
      header_name: X-API-Key      # when key_location is "header"
      query_param: api_key        # when key_location is "query"
      keys:
        sk_live_abc123:
          id: key-001
          name: Production Key
          scopes: ["read", "write"]
        sk_test_xyz789:
          id: key-002
          name: Test Key
          scopes: ["read"]

Configuration

PropertyTypeDefaultDescription
key_locationstringheaderWhere to find key (header or query)
header_namestringX-API-KeyHeader name (when key_location: header)
query_paramstringapi_keyQuery param name (when key_location: query)
keysobject{}Map of valid API keys to metadata

Context Headers

Sets headers for downstream:

  • x-auth-key-id - Key identifier
  • x-auth-key-name - Key human-readable name
  • x-auth-key-scopes - Comma-separated scopes

oauth2-auth

Validates Bearer tokens via RFC 7662 token introspection.

x-barbacane-middlewares:
  - name: oauth2-auth
    config:
      introspection_endpoint: https://auth.example.com/oauth2/introspect
      client_id: my-api-client
      client_secret: "env://OAUTH2_CLIENT_SECRET"  # resolved at startup
      required_scopes: "read write"                 # space-separated
      timeout: 5.0                                  # seconds

The client_secret uses a secret reference (env://) which is resolved at gateway startup. See Secrets for details.

Configuration

PropertyTypeDefaultDescription
introspection_endpointstringrequiredRFC 7662 introspection URL
client_idstringrequiredClient ID for introspection auth
client_secretstringrequiredClient secret for introspection auth
required_scopesstring-Space-separated required scopes
timeoutfloat5.0Introspection request timeout (seconds)

Context Headers

Sets headers for downstream:

  • x-auth-sub - Subject
  • x-auth-scope - Token scopes
  • x-auth-client-id - Client ID
  • x-auth-username - Username (if present)
  • x-auth-claims - Full introspection response as JSON

Error Responses

  • 401 Unauthorized - Missing token, invalid token, or inactive token
  • 403 Forbidden - Token lacks required scopes

Includes RFC 6750 WWW-Authenticate header with error details.


Rate Limiting

rate-limit

Limits request rate per client using a sliding window algorithm. Implements IETF draft-ietf-httpapi-ratelimit-headers.

x-barbacane-middlewares:
  - name: rate-limit
    config:
      quota: 100
      window: 60
      policy_name: default
      partition_key: client_ip

Configuration

PropertyTypeDefaultDescription
quotaintegerrequiredMaximum requests allowed in the window
windowintegerrequiredWindow duration in seconds
policy_namestringdefaultPolicy name for RateLimit-Policy header
partition_keystringclient_ipRate limit key source

Partition Key Sources

  • client_ip - Client IP from X-Forwarded-For or X-Real-IP
  • header:<name> - Header value (e.g., header:X-API-Key)
  • context:<key> - Context value (e.g., context:auth.sub)
  • Any static string - Same limit for all requests

Response Headers

On allowed requests:

  • X-RateLimit-Policy - Policy name and configuration
  • X-RateLimit-Limit - Maximum requests in window
  • X-RateLimit-Remaining - Remaining requests
  • X-RateLimit-Reset - Unix timestamp when window resets

On rate-limited requests (429):

  • RateLimit-Policy - IETF draft header
  • RateLimit - IETF draft combined header
  • Retry-After - Seconds until retry is allowed

CORS

cors

Handles Cross-Origin Resource Sharing per the Fetch specification. Processes preflight OPTIONS requests and adds CORS headers to responses.

x-barbacane-middlewares:
  - name: cors
    config:
      allowed_origins:
        - https://app.example.com
        - https://admin.example.com
      allowed_methods:
        - GET
        - POST
        - PUT
        - DELETE
      allowed_headers:
        - Authorization
        - Content-Type
      expose_headers:
        - X-Request-ID
      max_age: 86400
      allow_credentials: false

Configuration

PropertyTypeDefaultDescription
allowed_originsarray[]Allowed origins (["*"] for any, or specific origins)
allowed_methodsarray["GET", "POST"]Allowed HTTP methods
allowed_headersarray[]Allowed request headers (beyond simple headers)
expose_headersarray[]Headers exposed to browser JavaScript
max_ageinteger3600Preflight cache time (seconds)
allow_credentialsbooleanfalseAllow credentials (cookies, auth headers)

Origin Patterns

Origins can be:

  • Exact match: https://app.example.com
  • Wildcard subdomain: *.example.com (matches sub.example.com)
  • Wildcard: * (only when allow_credentials: false)

Error Responses

  • 403 Forbidden - Origin not in allowed list
  • 403 Forbidden - Method not allowed (preflight)
  • 403 Forbidden - Headers not allowed (preflight)

Preflight Responses

Returns 204 No Content with:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Max-Age
  • Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers

Caching

cache

Caches responses in memory with TTL support.

x-barbacane-middlewares:
  - name: cache
    config:
      ttl: 300
      vary:
        - Accept-Language
        - Accept-Encoding
      methods:
        - GET
        - HEAD
      cacheable_status:
        - 200
        - 301

Configuration

PropertyTypeDefaultDescription
ttlinteger300Cache duration (seconds)
varyarray[]Headers that vary cache key
methodsarray["GET", "HEAD"]HTTP methods to cache
cacheable_statusarray[200, 301]Status codes to cache

Cache Key

Cache key is computed from:

  • HTTP method
  • Request path
  • Vary header values (if configured)

Cache-Control Respect

The middleware respects Cache-Control response headers:

  • no-store - Response not cached
  • no-cache - Cache but revalidate
  • max-age=N - Use specified TTL instead of config

Planned Middlewares

The following middlewares are planned for future milestones:

request-id

Adds request ID for tracing.

x-barbacane-middlewares:
  - name: request-id
    config:
      header: X-Request-ID
      generate_if_missing: true

Configuration

PropertyTypeDefaultDescription
headerstringX-Request-IDHeader name
generate_if_missingbooleantrueGenerate UUID if not present

idempotency

Ensures idempotent processing.

x-barbacane-middlewares:
  - name: idempotency
    config:
      header: Idempotency-Key
      ttl: 86400

Configuration

PropertyTypeDefaultDescription
headerstringIdempotency-KeyHeader containing key
ttlinteger86400Key expiration (seconds)

Context Passing

Middlewares can set context for downstream components:

# Auth middleware sets context:auth.sub
x-barbacane-middlewares:
  - name: auth-jwt
    config:
      required: true

# Rate limit uses auth context
  - name: rate-limit
    config:
      key: context:auth.sub  # Rate limit per user

Middleware Development (Future)

See Plugin Development for creating custom middlewares.

Middleware Interface

trait Middleware {
    /// Initialize with configuration.
    fn init(config: Value) -> Result<Self, Error>;

    /// Process incoming request.
    async fn on_request(
        &self,
        ctx: &mut RequestContext,
    ) -> Result<MiddlewareAction, Error>;

    /// Process outgoing response.
    async fn on_response(
        &self,
        ctx: &mut ResponseContext,
    ) -> Result<(), Error>;
}

enum MiddlewareAction {
    Continue,           // Pass to next middleware
    Respond(Response),  // Short-circuit with response
}

Best Practices

Order Matters

Put middlewares in logical order:

x-barbacane-middlewares:
  - name: request-id     # 1. Add tracing ID first
  - name: cors           # 2. Handle CORS early
  - name: rate-limit     # 3. Rate limit before auth (cheaper)
  - name: auth-jwt       # 4. Authenticate

Fail Fast

Put restrictive middlewares early to reject bad requests quickly:

x-barbacane-middlewares:
  - name: rate-limit     # Reject over-limit immediately
  - name: auth-jwt       # Reject unauthorized before processing

Use Global for Common Concerns

# Global: apply to everything
x-barbacane-middlewares:
  - name: request-id
  - name: cors
  - name: rate-limit

paths:
  /public:
    get:
      # No additional middlewares needed

  /private:
    get:
      # Only add what's different
      x-barbacane-middlewares:
        - name: auth-jwt