fix(oidc): atomic code redemption, cap refresh TTL, drop dead client fields (BUNYIP-73) #252
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/bunyip-73-oidc-provider-hardening"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Harden the OIDC provider service against three defects flagged in the audit.
Auth-code double-spend: replace the SELECT-then-UPDATE in consume_authorization_code with a single atomic UPDATE ... WHERE consumed_at IS NULL AND revoked_at IS NULL ... RETURNING. The predicate is the redemption guard and the SET is the redemption, so check and mark happen in one round-trip with no TOCTOU window; concurrent /token requests race on the UPDATE and exactly one wins (RFC 6749 4.1.2). Zero rows are rejected as invalid_grant. A code that fails a binding check (expiry/client_id/redirect_uri/PKCE) is still consumed, so it is never replayable.
Refresh absolute-expiry cap: rotation set new_abs_exp = now + abs_ttl, which reset the absolute deadline on every rotation and let a token refreshed before each idle expiry live forever. Cap it with old.absolute_expires_at.min(now + abs_ttl) so rotation refreshes only the idle window.
Duplicate client SELECT: rotate_refresh_token reuses load_client() instead of an inline 18-column SELECT. The client config is read-only and not part of the rotation atomicity, so a single source of truth for the query is preferable.
Dead client fields: drop allowed_grant_types, token_endpoint_auth_method, require_pkce, and dpop_bound from OAuthClient and load_client. They were loaded but never read; PKCE and grant-type policy is enforced centrally in the authorize/token handlers (S256 + code_challenge always required, grant_type restricted to authorization_code/refresh_token), so the per-client loads were dead.
The auth.refresh_reuse_detected audit event TODO is already resolved: BUNYIP-88 writes a Critical audit-log row on replay; no dangling TODO remains.
#BUNYIP-73