Releases: acartag7/mcp-sso
Release list
v0.2.3
✨ npx mcp-sso init
Scaffolds a working zero-setup MCP server into a new directory:
npx mcp-sso init my-server && cd my-server && npm install && npm start
# → prints a one-time code; pair with: claude mcp add --transport http my-bridge http://127.0.0.1:3000/mcpGenerates package.json (exact-pinned deps, Node ≥24), server.ts (the recommended Origin-gate + console-pairing composition root), .gitignore, .npmrc (ignore-scripts=true), README.md. The bin is dep-free (node builtins only); fail-closed filesystem trust (atomic O_NOFOLLOW|O_EXCL|O_CREAT writes; target dir + ancestor chain validated no-follow).
Consumer-facing exports
NormRequest, NormResponse, assertCallbackPath, ensureStateDir (atomic — not the raw ensureGitignore), assertRealDir are now root-exported from mcp-sso — so a package consumer replicating the recommended patterns imports them instead of reimplementing them.
Fixes
- destroySoon test-isolation flake (#66) — SDK
/mcpcalls in 3 test files switched from the inject-mock socket to a real loopback socket (the root cause, gone by construction). - CodeQL ReDoS hardening (#65) — two
js/polynomial-redosregexes restructured; 15js/missing-rate-limitingalerts triaged as false positives (the bridge rate-limits register/token viaRateLimitPort/Bridge.guard).
Infrastructure
- Renovate for github-actions updates —
minimumReleaseAge: 15 daysauto-enforced (the floor Dependabot can't do natively). Install the Renovate app to activate. - gitignore session-artifact dirs.
Full changelog: v0.2.2...v0.2.3
v0.2.2
v0.2.2
mcp-sso v0.2.2 brings the Google + generic-OIDC identity ports (live-verified), a security-hardening pass, the isMcpPath root-export, and live verification across three identity providers — plus an exchange-error diagnosability fix. Published from GitHub Actions via OIDC Trusted Publishing with Sigstore provenance — verify with npm audit signatures. No new runtime dependencies (still jose only).
New
- Google + generic-OIDC identity ports —
createGoogleRedirectIdentityandcreateGenericOidcRedirectIdentity(RedirectIdentityPorts, §17.6) for the upstream redirect orchestrator: discovery + manual endpoints, multi-audience id_token rejection,at_hashwhen present,iatrequired. The Google preset is pinned tohttps://accounts.google.comwithhd(hosted-domain) gating checked against the signedhdclaim (never the email domain) andemailsurfaced only whenemail_verified === true; the bridge subject is Google's stable numericsub, never the email. Live-verified against Google sign-in (Claude Code + official SDK). (Shipped ahead of the earlier "v0.3" note.) isMcpPathroot-export —import { isMcpPath } from "mcp-sso". The canonical/mcpStreamable-HTTP path check for the recommendedonRequestOrigin-gate (DNS-rebinding protection that runs before the bearer check, for every method). Handles the absolute-form request-target (POST http://host/mcp) that a raw=== "/mcp"misses.
Hardened
- Security hardening (two-pass assessment, no crit/high): the
/mcpOrigin-gate absolute-form bypass in fastify-sqlite → centralizedisMcpPath;createBridgeConfigvalidate-then-spread TOCTOU/unknown-key leak → snapshot-once (#50); Cloudflare Access non-emptyaudienceguard + token audit-after-mint ordering (#26). - Exchange-error diagnosability — the §17.11 upstream exchange-error cause (previously swallowed at a bare
catch {}) is now logged operator-side on stderr (the client still gets genericserver_error), via a hardenedredactForStderr: never-throws, redacts-before-bounding, strips all line-breaking/terminal-control chars (C0, DEL, C1 incl. NEL/CSI, U+2028/U+2029), full credential-form redaction (Bearer,Authorization:scheme+creds,token=/secret=/code=/code_verifier=, opaque runs), and ReDoS-free. - Doc overclaims corrected (claims-vs-enforcement): access tokens are short-TTL ES256 valid until
exp(not "single-use"); the api-key-gateway is a transparent proxy that can't stop a backend that echoes its own credential; the pairing code binds the session/operator; the JSONL audit sink doesn'tfsync.
Live verification status (honest)
Production identity legs now live-verified: Cloudflare Access across five clients (Claude Code CLI, Codex CLI, claude.ai, ChatGPT, official SDK); Entra ID redirect flow (Claude Code, Claude Desktop); and Google sign-in (Claude Code + official SDK — bridge sub = Google's numeric sub; tokenless /mcp → 401 + WWW-Authenticate: resource_metadata; GOOGLE_HOSTED_DOMAIN outside-domain rejection → google_bad_hosted_domain, no token minted). Open owner-run rows (live Entra tenant deny-path, the gateway example against a real IdP, fresh-machine first-stranger run) are tracked in docs/live-verification.md. Not claimed: production-ready, audited, or a generic-OIDC second-provider live pass.
CI & supply chain
CodeQL SAST + Dependabot; OpenSSF Best Practices (passing) badge; repo onboarded to Engineering OS (tier S) with the process-guard artifact chain.
Moved to v0.3
GitHub identity preset · device authorization flow (RFC 8628) · CIMD (client_id_metadata_document — resequenced up: DCR is deprecated in the 2026-07-28 spec draft and the major clients already ship CIMD) · npx mcp-sso init.
Verify this release
npm i mcp-sso@0.2.2
npm audit signatures # Sigstore provenance + registry signatures(v0.2.2 supersedes v0.2.1 — same content plus the diagnosability fix; v0.2.1 published to npm fine but its GitHub Release page was lost to an immutable-releases tag conflict.)
Full changelog: v0.2.0...v0.2.2
v0.2.0
v0.2.0 is the largest release since launch: eight feature sessions plus a security-hardening review cycle, every PR independently reviewed before merge. Published from GitHub Actions via OIDC Trusted Publishing with Sigstore provenance — verify with npm audit signatures.
New
client_credentialsgrant (the official MCP extensionio.modelcontextprotocol/oauth-client-credentials) for headless / machine-to-machine callers: machine clients are provisioned out-of-band (provisionMachineClient— no HTTP provisioning surface by design), secrets stored as SHA-256 only and verified with uniform work,client_secret_basic+client_secret_postauth, no refresh tokens. The reservedmcc_subject namespace is enforced across the whole token lifecycle — grant creation, code exchange, refresh, and verification (machine tokens mint agty: "client_credentials"marker; the verifier requiressub == client_idand the marker). See the Machine-to-machine guide.- Upstream redirect orchestrator (
createUpstreamRedirectFlow) — the full browser leg for redirect-based OIDC IdPs (Entra today): signed single-use flow cookie (__Host-profile), strict callback validation with fixed error mappings (IdP error text is never echoed), mandatory upstream PKCE, same-browser consent delivery. You no longer hand-write the riskiest OAuth code per deployment. - Entra group→scope authorization — map Entra group membership (GUID-keyed, display names rejected) to a per-user scope ceiling; group overage fails closed. The two-gate authorization model is documented in
docs/authorization.md. allowedScopesceiling (IdP-agnostic) — identity-derived scope narrowing, intersected at consent preparation AND approval, so accumulated prior grants can never resurrect a revoked scope./store/mysql— pooledmysql2store (row-locked rotation, fail-closed boot asserts for strict mode/collation/engine) and/rate-limit/redis(fixed-window, fail-open so a limiter outage never locks out auth). Both optional peer deps.- Audit reference sinks —
JsonlFileAudit(0600, append-only, log-injection-safe), https-only no-redirectWebhookAudit,combineAuditfan-out; nine new event names including failed-login visibility (identity.verify). Deployment guide:docs/audit-deployment.md. - Zero-setup quickstart —
loadOrCreateQuickstartSecretsauto-generates and persists signing material (0700/0600, fail-closed, never a silent ephemeral fallback) and console pairing (/identity/console-pairing) gives you real OAuth with no IdP: paste a one-time code from the server console. The oldDEV_STUB_SUBJECTbypass is gone. examples/api-key-gateway— a worked example of the most common production shape: SSO in front of a backend that only understands a static API key, with the key injected server-side and never visible to any MCP client. Rules documented indocs/gateway-deployment.md.
Hardened
- Consent approval is fail-closed at both the adapter and the core: only an explicit
approved=trueapproves — an absent or malformed field denies (#21, #23) - The hono adapter no longer reads
X-Forwarded-For: a deployer-suppliedclientIpextractor keys the rate limiter, so an attacker-chosen header can't select its own bucket (#21) MemoryStorenow matches sqlite/mysql on token-hash collisions (no silent overwrite), and the collision tests moved into the shared conformance suite all stores must pass (#21)createBridgeConfigrejects unknown keys, so a secret parked on the config input can never reach the frozen public config (#13)- The example
/mcpvalidatesOriginpre-parse and method-agnostic (spec-MUST DNS-rebinding defense) (#15) - The
identityHeader-mode nonce residual is now documented at every surface a deployer reads: in header mode the fronting proxy owns id_token replay protection (#22)
Live verification status (honest)
DCR/OAuth mechanics are verified against curl, the official MCP SDK client, Claude Code, and claude.ai. The production identity legs (live Entra tenant, real-browser redirect flow, the gateway example against a real IdP) are tracked as open owner-run checklists in docs/live-verification.md — rows flip only when actually run.
Moved to v0.3
Device authorization flow (RFC 8628), CIMD, generic-OIDC/GitHub/Google identity presets, npx mcp-sso init.
Verify this release
npm i mcp-sso@0.2.0
npm audit signatures # Sigstore provenance + registry signaturesFull changelog: v0.1.1...v0.2.0
v0.1.1
v0.1.1 — adapter identity-error-mapping hotfix
Patch release. No new features, no port or contract changes — this ships the
identity-error-mapping fix from #1
to npm users on 0.1.0.
What's in this release
When an identity port rejects a subject during /authorize (Entra / Cloudflare
Access allowlist miss, or any throw from identity resolution), the response is
now a spec-correct OAuth error across fastify, express, and hono:
- Identity rejection ⇒
401 access_deniedwith the §9.5 body (was a 500
internal_errorenvelope on Express/Hono, and a 401 with a framework-shaped
body on Fastify). - Non-OAuth throws ⇒
500with a non-leaking top-levelerrorbody. The
thrown message is no longer echoed — a thrownError("…")during identity
resolution previously surfaced in the response body on Express/Fastify. - Identity-error redirects are ignored pre-validation — the response is
delivered directly, with noLocationheader.
The shared adapter test matrix now covers all three cases (HF.1 rejected
identity, HF.2 OAuthError throw, HF.3 non-OAuth Error throw → non-leaking
500) across every framework. Full matrix documented in
docs/verification.md.
Should you upgrade?
Yes if any of your /authorize requests can be rejected by your identity port
(allowlists, Conditional Access, IdP-side gating). If identity resolution can
never throw in your deployment, behavior is unchanged. Contracts touched:
§9.3, §9.5, §9.6 in
docs/contracts.md.
npm i mcp-sso@0.1.1
Not in this release
Phase 5 (/store/mysql, Redis-backed RateLimitPort, Captatum dogfood) and the
v0.2 feature set (Entra groups, client_credentials, device flow, generic OIDC,
CIMD, audit sinks, console pairing) remain on the roadmap — see the
README.
Full changelog: v0.1.0...v0.1.1
v0.1.0
OAuth in your MCP server, instead of an API key in your client's config
First release. mcp-sso is a spec-correct OAuth 2.1 resource-server verifier plus a small AS-lite bridge for remote MCP servers: it speaks DCR + PKCE + consent to MCP clients (Claude Code, claude.ai, ChatGPT, Cursor) while your identity provider — Cloudflare Access, Microsoft Entra ID, or any OIDC provider — stays the identity source of truth. The bridge mints its own audience-bound tokens; upstream IdP tokens never pass through.
npm install mcp-ssoWhat's in v0.1.0
- Resource-server verifier — RFC 9728 Protected Resource Metadata (root + path-inserted),
WWW-Authenticatechallenges, scope step-up, fail-closed audience validation. - AS-lite bridge — RFC 7591 DCR (stateless or stored-client mode), PKCE S256, consent (approve/deny), refresh-token rotation with family replay-detection, RFC 6749 §5.2 wire errors, RFC 9207
iss. - Identity ports — Cloudflare Access, Microsoft Entra ID (multi-tenant, nonce-bound), and a dev-only stub for local testing.
- Framework adapters —
/fastify,/express,/hono. - Stores —
node:sqlite(recommended zero-ops production default) and an in-memory store, sharing one conformance suite. - Supply chain —
joseis the only runtime dependency; every pin is ≥15 days old; CI actions are SHA-pinned; this release itself is published via npm Trusted Publishing (OIDC, no token) with--provenance. - A published threat model (
docs/threat-model.md) and full contract surface (docs/contracts.md). - Verified end-to-end: the official MCP SDK client, Claude Code, and a claude.ai custom connector have all completed the full register → authorize → consent → token → call → refresh → revoke flow against the runnable example in
examples/fastify-sqlite.
Not yet in this release
Console-pairing identity (zero-IdP-setup onboarding), GitHub/Google identity presets, quickstart auto-keygen, CIMD, and the client_credentials grant are tracked for a future release — see the Roadmap section in the README.
Docs
- README — quickstart, alternatives comparison, live-client conformance matrix
- Contracts — full port/schema/error surface
- Threat model
- Dependency ledger
Full Changelog: https://github.com/acartag7/mcp-sso/commits/v0.1.0