OngoingAIOngoingAI Docs

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.md
  • internal/auth/auth.go (AuthorizationMatrix() and middleware logic)

Required config fields

  • auth.enabled: enables gateway key authorization when true.
  • auth.header: gateway key header name.
  • auth.keys: static keys, unless Postgres-backed key resolution is used.

Default header:

  • X-OngoingAI-Gateway-Key

Permissions

PermissionMeaning
proxy:writeSend requests through provider proxy routes (/openai/*, /anthropic/*).
analytics:readRead traces and analytics APIs.
keys:manageList, create, rotate, and revoke gateway keys.

Role defaults

RoleDefault permissions
ownerproxy:write, analytics:read, keys:manage
adminproxy:write, analytics:read, keys:manage
developerproxy:write, analytics:read
memberproxy:write, analytics:read
vieweranalytics:read

Unknown roles receive no implicit permissions. Explicit permissions values on a key are additive.

Route-to-permission matrix

ResourceActionScopeMethodsRoutesRequired permission
healthreadpublicGET, HEAD/api/healthnone
tracesreadworkspaceGET, HEAD/api/traces, /api/traces/:idanalytics:read
analyticsreadworkspaceGET, HEAD/api/analytics/*analytics:read
gateway_keysmanageworkspaceGET, POST, DELETE/api/gateway-keys*keys:manage
proxyforwardworkspace*/openai/*, /anthropic/*proxy:write

Authorization decision rules

  1. Middleware resolves required access from request method and path.
  2. OPTIONS preflight on protected prefixes is bypassed.
  3. If route is protected, middleware authenticates gateway key from auth.header.
  4. Middleware checks required permission for the resolved route.
  5. On proxy routes, middleware also requires provider credentials from client request: Authorization or X-API-Key.
  6. Middleware strips the gateway key header before upstream forwarding.
  7. If route is under protected prefixes but has no explicit policy mapping, middleware denies with 403.

Identity and tenant scope

Resolved identity fields:

  • key_id
  • org_id
  • workspace_id
  • role

Default fallback scope:

  • Missing org_id resolves to default.
  • Missing workspace_id resolves to default.

Backward-compatible alias:

  • team can supply workspace scope when workspace_id is 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 from auth.keys.
  • storage.driver=postgres and auth.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

StatusError messageTypical cause
401missing or invalid gateway keyMissing key header or unknown key token.
403gateway key does not have required permissionKey lacks required permission for route.
403missing provider API key — pass your provider key via Authorization or X-API-Key headerProxy request missing provider credential.
403request is not authorized by gateway policyProtected route/method is unmapped (deny-by-default).
429gateway usage limit exceeded or policy-specific messageProxy limit policy rejected request.
503gateway key verification unavailableDynamic authorizer unavailable or stale fail-closed path.
503gateway usage limit check unavailableProxy limit backend unavailable.

Notes:

  • Gateway key must be sent in configured gateway header.
  • Sending the gateway key only in Authorization is not accepted.

Audit deny fields

On deny outcomes, middleware emits an audit event with:

  • audit_action (gateway_auth)
  • audit_outcome (deny)
  • audit_reason
  • status_code
  • path
  • audit_resource
  • audit_resource_action
  • audit_scope
  • provider
  • required_permission
  • key_id, org_id, workspace_id when identity is known
  • limit_code for limit rejects

Verification workflow

  1. Enable auth and start gateway:

    Bash
    ongoingai config validate --config ongoingai.yaml
    ongoingai serve --config ongoingai.yaml
  2. Verify public route access without key:

    Bash
    curl -i "http://localhost:8080/api/health"
  3. Verify protected route rejects missing key:

    Bash
    curl -i "http://localhost:8080/api/traces?limit=1"
  4. Verify viewer cannot proxy:

    Bash
    curl -i "http://localhost:8080/openai/v1/models" \
      -H "X-OngoingAI-Gateway-Key: VIEWER_KEY" \
      -H "Authorization: Bearer OPENAI_API_KEY"
  5. Verify key manager can list gateway keys:

    Bash
    curl -i "http://localhost:8080/api/gateway-keys" \
      -H "X-OngoingAI-Gateway-Key: MANAGER_KEY"

Placeholders:

  • VIEWER_KEY: Gateway key token with analytics:read only.
  • MANAGER_KEY: Gateway key token with keys:manage.
  • OPENAI_API_KEY: Upstream provider API key.

You should see:

  • 200 for /api/health without a key.
  • 401 for protected route without key.
  • 403 for viewer proxy attempt.
  • 200 for 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:write permission.

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 Authorization or X-API-Key with 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.

Next steps