Octopus
1.x
Docs/Octopus/Authentication gateway
Open

Reading4 min
Updated31 Jul 2026
Sourcev1/middleware/authentication.mdx

Authentication gateway

The auth gateway (AuthGatewayMiddleware) is one of only two middleware Octopus adds to its global chain from configuration. When active, it runs on every request before proxying, authenticating the caller and enforcing authorization.

This page focuses on the middleware behavior. Provider configuration (auth_providers) and authorization rules (auth.authz) are documented in full under Authentication & authorization; the overall trust model is in Security.

When it is active01

The middleware is added to the chain only when at least one of these holds:

If neither holds, no auth middleware is in the chain and all requests pass through unauthenticated.

When active, the server also instantiates each configured provider (JWT, OIDC, API key, forward-auth, mTLS), builds the authorization evaluator from auth.authz, and spawns a background task that prunes the token cache every 120 seconds.

Request flow02

For each request the middleware runs the following steps:

1

Skip OPTIONS

OPTIONS (CORS preflight) requests bypass auth entirely and pass through.

Skip configured paths

If the request path matches any pattern in auth.skip_paths, auth is skipped. Patterns ending in * are treated as prefix matches; otherwise an exact match is required.

Honor per-route skip_auth

If the matched route has skip_auth: true, auth is skipped. The route context is injected into request extensions by the handler before the chain runs.

Select a provider

The provider is the route's auth_provider if set, otherwise auth.default_provider. If neither resolves to a provider:

  • with global_enforce: true, the request fails with 500 configuration_error;

  • otherwise it passes through.

Authenticate

The selected provider validates the request (bearer token, API key, client cert, or external subrequest). Validated tokens are cached for auth.token_cache_ttl.

Authorize

On success, role/scope/authz_rule checks run against the matched route via the authorization evaluator (Rhai and/or OPA per auth.authz.engine). A deny produces 403 forbidden.

Inject principal headers and proxy

The authenticated principal's id, roles, and scopes are written to upstream request headers (see below), the principal is stored in request extensions, and the request continues to the proxy.

Injected upstream headers03

On successful authentication the middleware adds these headers to the request forwarded upstream. The header names are configurable via auth.*:

Header (config key)DefaultContents
auth.principal_headerX-Auth-PrincipalThe principal id
auth.roles_headerX-Auth-RolesComma-separated roles (omitted if empty)
auth.scopes_headerX-Auth-ScopesComma-separated scopes (omitted if empty)

The middleware also stores an identity-based rate-limit key (user:<principal-id>) in request extensions for a downstream rate limiter.

Warning

The auth gateway populates an AuthRateLimitKey extension, but the rate-limit middleware that would read it is not in the runtime chain. So identity-based rate limiting is plumbed but not active. See Rate limiting.

Error responses04

Errors are returned as JSON with an error and message field (provider name included when known):

StatuserrorCause
401unauthorizedFailed credentials, or missing credentials while global_enforce: true
403forbiddenAuthenticated but authorization denied (roles/scopes/rule)
500auth_errorThe provider errored while validating
500configuration_errorglobal_enforce on, but no provider could be selected

For 401 responses, a WWW-Authenticate challenge is added based on the provider type: Bearer for JWT/OIDC/forward-auth, ApiKey for API-key providers, and none for mTLS.

Note

With global_enforce: false, a request that presents no credentials passes through unauthenticated (the upstream sees no principal headers). Only invalid credentials produce 401. Set global_enforce: true to require authentication on every non-skipped route.

Configuration05

The middleware is driven by the auth block and the auth_providers map. A minimal example that activates the gateway with global enforcement and a single JWT provider:

auth_providers:
  primary:
    type: jwt
    secret: ${JWT_SECRET}
    algorithm: HS256
    issuer: https://issuer.example.com
    audience: octopus

auth:
  default_provider: primary
  global_enforce: true
  skip_paths:
    - /health
    - /public/*
  token_cache_ttl: 60s

See Authentication & authorization for the full schema of every provider type, the authz engine options, and per-route overrides on Routes.