mcp-oauth-kit implements OAuth 2.1 / PKCE, token issuance, and rate limiting —
security is the point of the library. Please report vulnerabilities responsibly.
Do not open a public issue for security reports.
Preferred: use GitHub's private vulnerability reporting — open the repository's Security tab → Report a vulnerability. This keeps the report private until a fix is released.
Alternatively, email [email protected] with:
- a description of the issue and its impact,
- steps to reproduce (a minimal proof-of-concept if possible),
- the affected version(s) / commit.
You can expect an acknowledgement within 5 business days. We aim to ship a fix or mitigation for confirmed high-severity issues within 30 days, and will credit reporters who wish to be named once a fix is published.
This project is pre-1.0. Security fixes land on the latest published 0.x release.
Pin a version and watch releases for security updates.
| Version | Supported |
|---|---|
latest 0.x |
✅ |
| older | ❌ |
The following are documented design limitations, not vulnerabilities. They follow from the default Cloudflare-KV-style storage backend, which has no compare-and-swap:
- Idempotency / single-use is best-effort under concurrency. Authorization-code redemption, refresh-token rotation, and the two-phase confirm flow narrow but do not fully close double-execute windows. True exactly-once requires a strongly-consistent store (e.g. a Durable Object). See the README's two-phase and OAuth sections.
- Per-IP rate limiting trusts the configured client-IP source. The default reads
CF-Connecting-IP(authoritative on Cloudflare). Off-Cloudflare deployments must pass a customipExtractorthat derives the IP from a trusted source, or the brute-force guards can be bypassed with a spoofed header. - In-memory storage is for tests only.
createMemoryStorage()is not persistent or shared across isolates; never use it in production. - Storage keys are not namespaced by
baseUrl. If two servers built with this kit share one KV namespace, a token/client/code issued by one is resolvable by the other (all keys aremcp:<kind>:<hash>, with no per-deployment prefix). Use a separate KV namespace/database per deployment, or don't share storage across distinctbaseUrls. - The CIMD redirect-URI cache lifetime is attacker-influenced within a bounded range.
When
allowClientIdMetadataDocumentsis enabled, the cache TTL for a fetched document honors that document's ownCache-Control: max-age(clamped to 60s–24h) instead of a fixed 1-hour TTL. This is a deliberate spec-compliant tradeoff, not a bug — but it means the party publishing the CIMD document (also the party whoseredirect_urisyou'd want to revoke quickly after a compromise) can elect a cache lifetime up to 24x longer than the previous fixed default.
Reports that depend on misconfiguring these (e.g. using memory storage in production, or
running off-Cloudflare without an ipExtractor) are out of scope.