Commit graph

1 commit

Author SHA1 Message Date
d16b18ea38 feat(oauth): authorization-server endpoints (forgejo-mcp-broker-pur)
Implements internal/oauth, the broker's OAuth 2.1 AS surface that
Claude.ai (and other MCP clients) talk to. User authentication is
delegated to upstream Forgejo via internal/forgejo.

Endpoints:
  POST /oauth/register   — RFC 7591 dynamic client registration
  GET  /oauth/authorize  — RFC 6749 + 7636 PKCE (S256 only)
  GET  /oauth/callback   — Forgejo redirects back here after consent
  POST /oauth/token      — authorization_code + refresh_token grants
  POST /oauth/revoke     — RFC 7009

Security model:
- PKCE required, S256 only — plain method rejected per OAuth 2.1
- Every broker-issued access/refresh token stored as hex(sha256(plain));
  plaintext leaves the broker exactly once in the /token response body
- Refresh-token rotation: each refresh issues a new token pair and
  revokes the old refresh (RFC 6749 §10.4)
- Auth-code single-use enforced atomically via UPDATE...WHERE used_at IS
  NULL with rows-affected check, blocking the concurrent-replay race
- Issuer URL sourced from cfg.Issuer at construction time, never from
  inbound headers — prevents host-header injection on /.well-known
  metadata that ships in 2d
- redirect_uri must match a registered URI exactly (no prefix/wildcard)

Pending-authorization state (between /authorize and /callback) lives in
an in-memory sync.Map with a 1-minute reaper goroutine. A broker restart
drops them, forcing the user to re-authorize — acceptable trade-off
versus introducing a fifth table.

Tests: 81.0% coverage with ~20 cases across happy paths, every required-
field error, PKCE failure, code-replay, refresh expiry/revocation,
client-id and redirect-uri mismatches, Forgejo-side errors, and the
reaper logic itself (internal test).

Closes forgejo-mcp-broker-pur. The OAuth keystone is in place; phase 2c
unblocks discovery (2d) and security review (2e), and combined with the
existing supervisor + bridge it unblocks the session glue work in
phase 5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:04:34 +02:00