Authorization reference
Use this page to understand exactly how gateway authorization decisions are made. It documents permissions, route policy mapping, and deny behavior.
Policy scope
- Authorization is machine-to-machine and key-based.
- Policy applies to proxy routes and protected
/api/...routes. - Health route is public by policy.
- Unmapped actions on protected prefixes are denied.
Canonical policy source:
AUTHORIZATION.mdinternal/auth/auth.go(AuthorizationMatrix()and middleware logic)
Required config fields
auth.enabled: enables gateway key authorization whentrue.auth.header: gateway key header name.auth.keys: static keys, unless Postgres-backed key resolution is used.
Default header:
X-OngoingAI-Gateway-Key
Permissions
| Permission | Meaning |
|---|---|
proxy:write | Send requests through provider proxy routes (/openai/*, /anthropic/*). |
analytics:read | Read traces and analytics APIs. |
keys:manage | List, create, rotate, and revoke gateway keys. |
Role defaults
| Role | Default permissions |
|---|---|
owner | proxy:write, analytics:read, keys:manage |
admin | proxy:write, analytics:read, keys:manage |
developer | proxy:write, analytics:read |
member | proxy:write, analytics:read |
viewer | analytics:read |
Unknown roles receive no implicit permissions.
Explicit permissions values on a key are additive.
Route-to-permission matrix
| Resource | Action | Scope | Methods | Routes | Required permission |
|---|---|---|---|---|---|
health | read | public | GET, HEAD | /api/health | none |
traces | read | workspace | GET, HEAD | /api/traces, /api/traces/:id | analytics:read |
analytics | read | workspace | GET, HEAD | /api/analytics/* | analytics:read |
gateway_keys | manage | workspace | GET, POST, DELETE | /api/gateway-keys* | keys:manage |
proxy | forward | workspace | * | /openai/*, /anthropic/* | proxy:write |
Authorization decision rules
- Middleware resolves required access from request method and path.
OPTIONSpreflight on protected prefixes is bypassed.- If route is protected, middleware authenticates gateway key from
auth.header. - Middleware checks required permission for the resolved route.
- On proxy routes, middleware also requires provider credentials from client
request:
AuthorizationorX-API-Key. - Middleware strips the gateway key header before upstream forwarding.
- If route is under protected prefixes but has no explicit policy mapping,
middleware denies with
403.
Identity and tenant scope
Resolved identity fields:
key_idorg_idworkspace_idrole
Default fallback scope:
- Missing
org_idresolves todefault. - Missing
workspace_idresolves todefault.
Backward-compatible alias:
teamcan supply workspace scope whenworkspace_idis empty.
Tenant scoping effect:
- Trace and analytics reads are filtered to identity workspace scope.
- Gateway key management routes apply identity workspace scope filters.
Key source behavior
storage.driver=sqlite: auth keys come fromauth.keys.storage.driver=postgresandauth.enabled=true: gateway keys are loaded from Postgres config store.- Postgres fallback:
if store has zero active keys, gateway falls back to YAML
auth.keys.
Error semantics
| Status | Error message | Typical cause |
|---|---|---|
401 | missing or invalid gateway key | Missing key header or unknown key token. |
403 | gateway key does not have required permission | Key lacks required permission for route. |
403 | missing provider API key — pass your provider key via Authorization or X-API-Key header | Proxy request missing provider credential. |
403 | request is not authorized by gateway policy | Protected route/method is unmapped (deny-by-default). |
429 | gateway usage limit exceeded or policy-specific message | Proxy limit policy rejected request. |
503 | gateway key verification unavailable | Dynamic authorizer unavailable or stale fail-closed path. |
503 | gateway usage limit check unavailable | Proxy limit backend unavailable. |
Notes:
- Gateway key must be sent in configured gateway header.
- Sending the gateway key only in
Authorizationis not accepted.
Audit deny fields
On deny outcomes, middleware emits an audit event with:
audit_action(gateway_auth)audit_outcome(deny)audit_reasonstatus_codepathaudit_resourceaudit_resource_actionaudit_scopeproviderrequired_permissionkey_id,org_id,workspace_idwhen identity is knownlimit_codefor limit rejects
Verification workflow
-
Enable auth and start gateway:
Bashongoingai config validate --config ongoingai.yaml ongoingai serve --config ongoingai.yaml -
Verify public route access without key:
Bashcurl -i "http://localhost:8080/api/health" -
Verify protected route rejects missing key:
Bashcurl -i "http://localhost:8080/api/traces?limit=1" -
Verify viewer cannot proxy:
Bashcurl -i "http://localhost:8080/openai/v1/models" \ -H "X-OngoingAI-Gateway-Key: VIEWER_KEY" \ -H "Authorization: Bearer OPENAI_API_KEY" -
Verify key manager can list gateway keys:
Bashcurl -i "http://localhost:8080/api/gateway-keys" \ -H "X-OngoingAI-Gateway-Key: MANAGER_KEY"
Placeholders:
VIEWER_KEY: Gateway key token withanalytics:readonly.MANAGER_KEY: Gateway key token withkeys:manage.OPENAI_API_KEY: Upstream provider API key.
You should see:
200for/api/healthwithout a key.401for protected route without key.403for viewer proxy attempt.200for gateway-key list with manager key.
Troubleshooting
Protected routes return 401
- Symptom: Response says
missing or invalid gateway key. - Cause: Missing key header, wrong header name, or invalid key token.
- Fix: Send gateway key in configured
auth.header.
Viewer key cannot proxy provider routes
- Symptom: Proxy request returns
403. - Cause: Viewer role does not include
proxy:write. - Fix: Use a key with
proxy:writepermission.
Proxy returns 403 for missing provider key
- Symptom: Error says provider API key is missing.
- Cause: Gateway key is valid, but provider credential is absent.
- Fix: Send
AuthorizationorX-API-Keywith provider token.
Protected routes return 503
- Symptom: Error says gateway key verification is unavailable.
- Cause: In Postgres mode, authorizer refresh failed or cache is stale.
- Fix: Restore config-store connectivity and verify key refresh logs.