Skip to content

Outpost OAuth scope enforcement

The Outpost supports path-based OAuth scope enforcement via OAuthScopeEnforcer. When enabled, each request’s JWT scp or scope claim is checked against the scopes required for the requested path. Requests that pass JWT validation but lack the required scope receive HTTP 403 with an X-Scope-Required header identifying the missing scope.

Scope enforcement is opt-in — it is disabled by default to preserve backward compatibility with environments that use API key authentication or do not include scope claims in their tokens.


Scope enforcement runs after JWT signature validation. The enforcement flow for each request:

  1. JWT validator authenticates the token (JWTValidator.validate()).
  2. If OAUTH_SCOPE_ENFORCEMENT=true and an OAuthScopeEnforcer is configured, enforce() is called with the decoded JWT claims and the request path.
  3. The enforcer looks up the required scopes for the path in the oauth_scopes.path_required_scopes map.
  4. If required scopes are found, the enforcer checks that each required scope is present in the token’s scp or scope claim.
  5. On mismatch: raise ScopeEnforcementError → HTTP 403 + X-Scope-Required: <scope>.
  6. If no required scopes are configured for the path, the request passes.
  7. API-key-authenticated requests bypass scope enforcement entirely.

Set the environment variable on the Outpost:

Terminal window
OAUTH_SCOPE_ENFORCEMENT=true
VariableDefaultDescription
OAUTH_SCOPE_ENFORCEMENTfalseSet to true to enable path-based scope enforcement. When false, JWT validation still runs but scope claims are not checked.

Scope enforcement requires JWT validation to be active (OAUTH_JWT_PUBLIC_KEY or OAUTH_JWKS_URL must be set). If JWT validation is disabled, scope enforcement has no effect.


Scope requirements are configured in the oauth_scopes block of the policy bundle. Set these values in the Admin Portal and they propagate to all Outposts on the next policy sync (default 60-second interval).

{
"oauth_scopes": {
"allowed_scopes": ["api:read", "api:write", "admin:read", "admin:write"],
"path_required_scopes": {
"/v1/chat/completions": ["api:write"],
"/v1/models": ["api:read"],
"/admin/api/*": ["admin:read"]
}
}
}

An optional list of scopes that the Outpost recognises as valid. This field is informational — the enforcer does not reject tokens for having scopes outside this list, only for lacking required scopes on specific paths.

A map from path pattern to a list of required scope strings. The enforcer checks all listed scopes; the token must include every scope in the list for the matched path.

{
"path_required_scopes": {
"/v1/chat/completions": ["api:write"],
"/admin/api/budget/status": ["admin:read"],
"/admin/api/*": ["admin:read"]
}
}

The request path is matched against the map keys in order. The first matching entry is used.


Path keys support a trailing * wildcard:

PatternMatches
/v1/chat/completionsExact path only
/admin/api/*Any path under /admin/api/
*All paths

The wildcard matches any suffix. It does not support mid-path wildcards (e.g. /v1/*/completions is not supported — use exact paths or suffix wildcards only).


The enforcer reads scopes from the JWT payload in two formats:

String format (space-delimited, RFC 6749 standard):

{
"scp": "api:read api:write audit:read"
}

Array format:

{
"scope": ["api:read", "api:write", "audit:read"]
}

Both scp and scope claim names are checked. The enforcer first checks scp; if absent, it checks scope. If both are absent, scope enforcement treats the token as having no scopes.


When a request is denied for insufficient scope:

HTTP/1.1 403 Forbidden
X-Scope-Required: api:write
Content-Type: application/json
{"detail": "Insufficient scope. Required: api:write"}

The X-Scope-Required header contains the first scope that was missing. If multiple scopes are required and several are absent, only the first missing scope is reported in the header (the full list is included in the JSON body).


Scope enforcement applies only to JWT-authenticated requests. Requests authenticated via Authorization: Bearer <api-key> (Outpost HMAC API key) bypass scope enforcement entirely. This allows existing API key integrations to continue operating without modification when scope enforcement is enabled.


The Arbitex Platform uses these scope constants for M2M OAuth tokens:

ScopeDescription
api:readRead access to the chat completions and models endpoints
api:writeWrite access to the chat completions endpoint
admin:readRead access to admin API endpoints
admin:writeWrite access to admin API endpoints
audit:readRead access to audit log endpoints
dlp:readRead access to DLP rule and result endpoints

Configure path_required_scopes using these constants for compatibility with Platform-issued M2M tokens.


Lock the chat completions endpoint to api:write and allow all other paths for any valid JWT:

{
"oauth_scopes": {
"path_required_scopes": {
"/v1/chat/completions": ["api:write"]
}
}
}

With OAUTH_SCOPE_ENFORCEMENT=true, requests to /v1/chat/completions without api:write in the token scope receive HTTP 403. All other paths remain accessible to any authenticated JWT.