0014. The OAuth 2.0 / OIDC surface and flow set
Context
ADR-0003 fixed the external token as OIDC-conformant JWT: the surface conforms rather than innovates, because relying parties speak OIDC and verify with stock libraries. What it did not fix is which flows and endpoints Vord ships, and that choice is not purely additive. Some OAuth features are structural — they change the token's shape or the request's binding — and so, like the biscuit attenuation reserved in ADR-0003, their seat must be reserved now or they cannot be added later without breaking verifiers. The flow set is also the competitive surface: matching Entra/Okta (workforce) and Auth0 (CIAM) means a credible, certifiable set (ADR-0025), not a grab-bag.
Decision
-
v1 flows and endpoints (the issuer the walking skeleton grows into):
- Authorization Code + PKCE — the primary interactive flow and the M0 slice; PKCE mandatory for all clients (public and confidential).
- Client Credentials — service-to-service, no user.
- Refresh tokens — with rotation and reuse detection; the only long-lived artifact, balancing the short access-token TTL (ADR-0005). Each use rotates the token and invalidates its predecessor; presenting an already-rotated (consumed) refresh token is a theft signal — the whole token family is revoked through the status-list + NATS path (ADR-0005) and the event feeds the risk pipeline (ADR-0031). The refresh token is the one long-lived bearer the short-TTL model does not otherwise bound, so reuse detection here is mandatory, not optional. Family storage, the legitimate-retry grace window, and idle/absolute lifetimes are ADR-0044.
- Token revocation (RFC 7009) —
/revoke, in v1 for refresh tokens: a client can end its own token family (ADR-0044); revoking a short-TTL access token is accepted but is honestly a near-no-op. - Discovery + JWKS —
/.well-known/openid-configurationand the JWKS endpoint, the public material Vör and every relying party read (ADR-0010, ADR-0023). Confidential clients authenticate to the token endpoint withclient_secret_basic(primary) orclient_secret_post(compat), advertised as such in discovery metadata;private_key_jwtand mTLS are the reserved stronger methods (ADR-0038). - UserInfo — served from the eventually-consistent profile projection (ADR-0011), not a core read; its staleness bound is the ordinary replication bound (ADR-0004 decision 2).
- RP-initiated logout — the
end_session_endpointentry into the session model (ADR-0016); the complete v1 logout endpoint set (including back-channel logout, and the declined front-channel variant) is pinned in ADR-0044.
-
Reserved with seats — designed-for now, built later, because each is structural or a named bridge:
- Token Exchange (RFC 8693) — the bridge by which a SAML or upstream-IdP login at Modgud mints a Vord OIDC token (ADR-0017, ADR-0019). Reserved as the mechanism that keeps the edge path always JOSE/Vör.
- DPoP (sender-constrained tokens) — structural: it changes token shape and the verification check, so the seat is reserved now exactly as biscuit attenuation was (ADR-0003); building it later then only strengthens binding.
- PAR (pushed authorization requests) and the device authorization grant — reserved; PAR is a FAPI prerequisite (ADR-0025), device grant a later CLI/TV surface.
-
Explicitly declined. The resource-owner password credentials grant (ROPC) — it trains clients to handle passwords and defeats MFA and federation; the implicit flow — superseded by code+PKCE and deprecated; and token introspection (RFC 7662) — a per-request network oracle that would quietly reintroduce the central read rule 1 exists to refuse (the reasoning is ADR-0044 decision 6). None is a gap; all are anti-patterns Vord refuses, the way ADR-0003 refused inline keys.
-
Every issued token is edge-verifiable with no core read (ADR-0001 rule 1, ADR-0010): the surface conforms to OIDC, but conformance never reintroduces a core dependency on the verification path. Where a grant result must support read-your-writes (a just-changed scope), it carries the consistency token of ADR-0004; the default path does not.
-
The protocol endpoints are rate-limited as a named surface.
/authorize,/token,/revoke, and UserInfo sit before or at authentication, so they are the asymmetric-cost surface; the layered limits (per-IP, per-client, per-principal, per-tenant fairness) are owned by the resource-exhaustion hardening umbrella (proposal 0006, ADR-0027), applied here rather than re-specified here.
Consequences
- A relying party integrates Vord as a standard OIDC provider on day one — stock libraries, the discovery document, the JWKS — which is the interoperability the whole external-surface thesis (ADR-0003) rests on.
- Reserving DPoP and PAR now keeps the high-security (FAPI) future open at no v1 cost and prevents a token-shape break later; declining ROPC/implicit keeps the surface small and safe.
- Token Exchange is the single seam that makes SAML and upstream federation additive rather than architecture-changing — it is why those ADRs (0017, 0019) can be roadmap items without re-opening this one.
- Refresh-token rotation with reuse detection closes the one gap the short access-token TTL leaves open: the refresh token is the only long-lived bearer, so a stolen one is caught the moment either the legitimate client or the attacker replays a consumed token — turning refresh-token theft into a detected, self-revoking event rather than a silent long-lived compromise.
- The client object itself — types, authentication, secret rotation, and the exact-match redirect-URI validation this flow set depends on — is settled in ADR-0038, so this ADR fixes flows and that one fixes clients.
- The v1 set is exactly the OpenID Provider profile Vord targets for certification (ADR-0025), so "what we ship" and "what we certify" are the same list.