Skip to content

API Authentication & Authorization

Document ID: API-002
File Path: docs/09-api/authentication.md
Version: 1.1.0
Status: Draft — this document describes the target-state design (full OAuth2/OIDC flows, refresh tokens, per-key IP allowlists, mTLS, org-custom roles). What wovyr-server actually implements today (RM-GA-P1 SEC-101, added 2026-07-07): a bearer credential verified by an auth::authenticate middleware before any handler runs, selected via WOVYR_AUTH_MODEjwt (HS256 via WOVYR_JWT_HS_SECRET or RS256 via WOVYR_JWT_RS_PUBLIC_KEY, with optional issuer/audience checks; the verified sub claim becomes the principal) or apikey (a bearer token SHA-256-hashed and looked up in a file-backed store, minted via wovyr auth create-key). API keys now have a full lifecycle (RM-AIM-P1 SRV-104, 2026-07-14): create-with-TTL, list, revoke, and rotate-with-grace (wovyr auth create-key --ttl-days | list-keys | revoke <id> | rotate <id> --grace-hours), with revocation and expiry enforced on every lookup and last_used tracking. Still not implemented: OAuth2 authorization flows, refresh tokens, mTLS, per-key IP allowlists, and any revocation mechanism for JWTs (a JWT stays valid to expiry) — a verified credential simply overwrites the request’s X-Wovyr-Principal header before RBAC runs. See crates/wovyr-server/src/auth.rs and phase1-security-floor-tickets.md.
Owner: AI Platform Team
Last Updated: 2026-07-15


This document defines how API callers authenticate (prove identity) and how requests are authorized (granted access). It covers credential types, token formats, RBAC scopes, and policy enforcement at the API Gateway.

Authentication establishes who; authorization decides what they may do; the Policy Engine enforces contextual rules.


CredentialUseCarried as
OAuth2 / OIDC access token (JWT)Interactive users, SSOAuthorization: Bearer <jwt>
API keyServices, CI, CLIAuthorization: Bearer <key> or Wovyr-Api-Key
Service token (short-lived JWT)Internal service-to-serviceAuthorization: Bearer <jwt>
mTLS client certInternal, zero-trust networksTLS handshake

All external traffic is TLS; internal service traffic uses mTLS.


The platform supports standard OAuth2 flows via an external or built-in IdP:

FlowUse
Authorization Code + PKCEWeb/desktop user login
Client CredentialsMachine-to-machine
Device CodeCLI / headless
Refresh TokenSession renewal

Access tokens are JWTs validated at the Gateway (signature, exp, aud, iss). SSO via OIDC lets enterprises bring their own IdP.


{
"sub": "user_01H...",
"tenant": "acme",
"org": "acme-eu",
"projects": ["support-bot"],
"roles": ["workflow.editor", "agent.operator"],
"scopes": ["agents:read", "agents:run", "workflows:write"],
"exp": 1750003600,
"iss": "https://auth.wovyr.example.com",
"aud": "wovyr-api"
}

The token binds the principal to a tenant, roles, and scopes; the Gateway derives the effective permission set from these.


  • Created per project or service account; see Users API §6.
  • Prefixed and partially shown once (apx_live_…), stored hashed.
  • Carry a fixed scope set and optional IP allowlist and expiry.
  • Revocable instantly; usage is audited.

Authenticated principal
RBAC: roles → scopes (coarse: may call this endpoint?)
Resource scoping (tenant/project/ownership match?)
ABAC via Policy Engine (contextual: data class, region, time, risk)
├── allow → proceed
└── deny → 403 + audit

RBAC gates which operations; resource scoping ensures the principal acts within its tenant/project; ABAC applies fine-grained, attribute-based rules. Enforcement is fail-closed.


Scopes follow resource:action:

agents:read agents:write agents:run
workflows:read workflows:write workflows:run workflows:cancel
memory:read memory:write
tools:read tools:invoke
plugins:read plugins:admin
projects:admin users:admin

A token’s effective scopes are the union granted by its roles, intersected with any API-key scope restriction.


Roles bundle scopes; built-in roles include:

RoleGrants
viewer*:read
operatorreads + *:run
editorreads + writes
project.adminfull within a project
org.adminfull within an organization
platform.adminfull across the deployment

Custom roles can be defined per organization (see Users API).


  • Every request resolves to exactly one tenant; cross-tenant access is impossible.
  • Project-scoped tokens are confined to their project(s).
  • Resource ownership is checked on every read/write so principals see only authorized resources (consistent with Memory scopes).

ConcernBehavior
Access token TTLShort (minutes)
Refresh tokenLonger; rotated on use
RevocationToken blocklist + key revocation, effective immediately
RotationAPI keys and signing keys rotate on schedule

API credentials and provider secrets are stored in the secret vault as references, never returned in responses, and rotated per policy (aligned with Provider SDK §20 and Tool Runtime secrets).


Every authentication and authorization decision is audited:

{
"event": "api.authz.denied",
"principal": "user_01H...",
"tenant": "acme",
"endpoint": "POST /api/v1/workflows:run",
"scope_required": "workflows:run",
"reason": "missing_scope",
"request_id": "req_01H...",
"timestamp": "2026-06-27T10:00:00Z"
}

CodeStatusMeaning
unauthenticated401Missing/invalid/expired credential
forbidden403Authenticated but not authorized
token_expired401Access token expired (refresh)
key_revoked401API key revoked

These use the standard error envelope.




VersionDateDescription
1.2.02026-07-15Status note refreshed for RM-AIM-P1 SRV-104: API keys now have TTL/revoke/rotate-with-grace lifecycle; clarified the remaining gap is JWT revocation + OAuth2/mTLS/IP allowlists. No design content changed
1.1.02026-07-07Added a top note distinguishing this doc’s target-state design from what’s actually implemented (RM-GA-P1 SEC-101: JWT/API-key bearer auth, no OAuth2 flow/mTLS/refresh tokens yet). Found during a project-wide status review; no design content changed
1.0.02026-06-27Initial API Authentication & Authorization specification