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
| Property | Type | Default | Description |
|---|---|---|---|
secret | string | - | HS256 secret key |
public_key | string | - | RS256 public key (PEM format) |
issuer | string | - | Expected iss claim |
audience | string | - | Expected aud claim |
required_claims | array | [] | Claims that must be present |
leeway | integer | 0 | Seconds 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
| Property | Type | Default | Description |
|---|---|---|---|
key_location | string | header | Where to find key (header or query) |
header_name | string | X-API-Key | Header name (when key_location: header) |
query_param | string | api_key | Query param name (when key_location: query) |
keys | object | {} | Map of valid API keys to metadata |
Context Headers
Sets headers for downstream:
x-auth-key-id- Key identifierx-auth-key-name- Key human-readable namex-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
| Property | Type | Default | Description |
|---|---|---|---|
introspection_endpoint | string | required | RFC 7662 introspection URL |
client_id | string | required | Client ID for introspection auth |
client_secret | string | required | Client secret for introspection auth |
required_scopes | string | - | Space-separated required scopes |
timeout | float | 5.0 | Introspection request timeout (seconds) |
Context Headers
Sets headers for downstream:
x-auth-sub- Subjectx-auth-scope- Token scopesx-auth-client-id- Client IDx-auth-username- Username (if present)x-auth-claims- Full introspection response as JSON
Error Responses
401 Unauthorized- Missing token, invalid token, or inactive token403 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
| Property | Type | Default | Description |
|---|---|---|---|
quota | integer | required | Maximum requests allowed in the window |
window | integer | required | Window duration in seconds |
policy_name | string | default | Policy name for RateLimit-Policy header |
partition_key | string | client_ip | Rate limit key source |
Partition Key Sources
client_ip- Client IP fromX-Forwarded-FororX-Real-IPheader:<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 configurationX-RateLimit-Limit- Maximum requests in windowX-RateLimit-Remaining- Remaining requestsX-RateLimit-Reset- Unix timestamp when window resets
On rate-limited requests (429):
RateLimit-Policy- IETF draft headerRateLimit- IETF draft combined headerRetry-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
| Property | Type | Default | Description |
|---|---|---|---|
allowed_origins | array | [] | Allowed origins (["*"] for any, or specific origins) |
allowed_methods | array | ["GET", "POST"] | Allowed HTTP methods |
allowed_headers | array | [] | Allowed request headers (beyond simple headers) |
expose_headers | array | [] | Headers exposed to browser JavaScript |
max_age | integer | 3600 | Preflight cache time (seconds) |
allow_credentials | boolean | false | Allow credentials (cookies, auth headers) |
Origin Patterns
Origins can be:
- Exact match:
https://app.example.com - Wildcard subdomain:
*.example.com(matchessub.example.com) - Wildcard:
*(only whenallow_credentials: false)
Error Responses
403 Forbidden- Origin not in allowed list403 Forbidden- Method not allowed (preflight)403 Forbidden- Headers not allowed (preflight)
Preflight Responses
Returns 204 No Content with:
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Max-AgeVary: 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
| Property | Type | Default | Description |
|---|---|---|---|
ttl | integer | 300 | Cache duration (seconds) |
vary | array | [] | Headers that vary cache key |
methods | array | ["GET", "HEAD"] | HTTP methods to cache |
cacheable_status | array | [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 cachedno-cache- Cache but revalidatemax-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
| Property | Type | Default | Description |
|---|---|---|---|
header | string | X-Request-ID | Header name |
generate_if_missing | boolean | true | Generate UUID if not present |
idempotency
Ensures idempotent processing.
x-barbacane-middlewares:
- name: idempotency
config:
header: Idempotency-Key
ttl: 86400
Configuration
| Property | Type | Default | Description |
|---|---|---|---|
header | string | Idempotency-Key | Header containing key |
ttl | integer | 86400 | Key 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