Skip to content

v0.2.0 — Scope narrowing, single-use CSRF nonces + security hardening

Choose a tag to compare

@ricbwood ricbwood released this 05 Jun 09:55
· 29 commits to main since this release
fe44997

A substantial security release bundling everything since 0.1.7 (#38#41). Upgrade recommended.

✨ Features

  • Per-scope capability narrowing (#35). Access tokens are now granted only the MCP capabilities their OAuth scope maps to (<slug>:read|write|delete), instead of always the full operator grant. The operator-configured set remains the hard ceiling; an empty scope still grants the full set. An invalid/no-longer-grantable scope is rejected with invalid_scope (no silent escalation).
  • Single-use, time-bound CSRF nonces (#27). Consent CSRF tokens are session-bound, expiring, and now single-use, backed by the new oauth-csrf-nonces collection (with an efficient bulk sweep).

🔒 Security hardening

  • Consent endpoint is session-bound — identity is derived from req.user, never the request body; a mismatching body user_id is rejected (#38).
  • CSRF/consent input hardened — non-string token guard, numeric user_id coercion (#39).
  • IP-only rate-limit key — rotating client_id/client_name can no longer mint fresh buckets and bypass the per-IP limit; empty/whitespace IPs coalesced (#40, #41).
  • /register: reject redirect URIs with a fragment (RFC 6749 §3.1.2), allow IPv6 loopback [::1], and cap software_id/software_version (#40, #41).
  • Closed backlog issues #26 #28 #29 #30 #31 and reconciled the threat model (#40).

Upgrade

pnpm add @brainwebuk/payload-plugin-mcp-oauth@0.2.0

No config changes required. If you adopt scoped tokens, requested scopes use <collectionOrGlobalSlug>:read|write|delete.