docs(readme): surface MCP-grade security highlights#191
Merged
Conversation
- Add an MCP / multi-resource security callout at the end of "Why AuthGate?" highlighting the JWT type-claim refresh-as-access guard, mandatory device-code resource confirmation, and RFC compliance baseline - Extend the "What AuthGate Protects" checklist with refresh-token confusion, device-flow phishing, and cross-resource replay coverage - Add an "MCP & multi-resource hardening" subsection under Security with the JWT_AUDIENCE operational constraint and the full RFC stack (8628 / 6749 / 7636 / 8707 / 8414 / 7591 / 7009 / 7662) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This docs-only PR surfaces MCP and multi-resource security guidance in the README so evaluators can find key hardening properties without digging into deep-dive docs.
Changes:
- Adds a README callout for MCP / multi-resource security highlights.
- Expands the security protection list with refresh-token confusion, device-flow confirmation, and cross-resource replay mitigations.
- Adds a new “MCP & multi-resource hardening” subsection with standards references and links.
Comments suppressed due to low confidence (2)
README.md:575
- This should not say “every JWT” because AuthGate’s OIDC ID tokens are JWTs without a
typeclaim. Please scope this to access/refresh token JWTs to keep the protection list accurate.
- ✅ Refresh-token-as-access-token confusion (mandatory `type` claim on every JWT)
README.md:581
- This paragraph has two inaccuracies: AuthGate-issued OIDC ID tokens are JWTs but intentionally have no
typeclaim, and/oauth/tokeninforejects refresh tokens rather than returning a response withaudstripped. Please narrow thetypestatement to access/refresh token JWTs and describe tokeninfo as rejecting non-access tokens before emitting an audience.
**Refresh tokens cannot masquerade as access tokens.** Refresh JWTs are signed with the same key as access JWTs, so a resource server that only verifies signature/`iss`/`exp`/`aud` would silently accept a refresh token as a valid access token whenever `JWT_AUDIENCE` happens to match its resource identifier. AuthGate hard-codes the distinction: every issued JWT carries a `type` claim (`access` or `refresh`), `ValidateToken` rejects any token whose `type` is not `"access"`, and `/oauth/tokeninfo` strips `aud` from refresh-token introspection so it cannot be mistaken for an access-token response. **Operational constraint: `JWT_AUDIENCE` MUST be either unset or set to an AS-only identifier — never a resource server's `aud`.** See [docs/MCP.md → Configuration checklist](docs/MCP.md#configuration-checklist) and [docs/CONFIGURATION.md](docs/CONFIGURATION.md#environment-variables).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
- Scope the `type`-claim guarantee to OAuth bearer tokens (access / refresh); OIDC ID tokens are JWTs without a `type` claim and follow a separate code path in `internal/token/idtoken.go` - Correct the `/oauth/tokeninfo` description — it returns 401 for refresh tokens via `ValidateToken` rather than stripping `aud`; note that `/oauth/introspect` is the RFC 7662 endpoint that omits `aud` on refresh tokens - Qualify the Dynamic Client Registration mention with the `ENABLE_DYNAMIC_CLIENT_REGISTRATION=true` opt-in (default is false in `config.go:586`) Addresses Copilot review on #191. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
README.md:576
- This list item is broader than the behavior documented below: AuthGate forces confirmation for resource-bound device codes, which mitigates silent resource binding, but it does not protect against all device-flow phishing. Please scope this bullet to the specific mitigation to avoid promising a stronger security guarantee than the implementation provides.
- ✅ Device-flow phishing (forced confirmation page for resource-bound device codes)
- Clarify the AS / RS division of responsibility for the `type` claim: AuthGate enforces it at its own endpoints, but resource servers validating JWTs locally must also check `type == "access"` (otherwise a refresh token can be accepted if `JWT_AUDIENCE` collides with the RS audience) - Narrow the device-code callout from "phishing surface eliminated" to "silent resource binding blocked" — the confirmation page only fires for resource-bound device codes (`handlers/device.go:266-280`), so the broader phishing claim overstated the mitigation - Apply the same scope correction to the "What AuthGate Protects" bullet Addresses Copilot review on #191. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Surfaces three security properties of AuthGate that were previously only documented in
docs/MCP.mdand other deep-dive guides, making them visible to README readers — especially MCP / multi-resource integrators evaluating AuthGate:typeclaim (accessvsrefresh); a refresh token cannot be replayed as an access token by a resource server that only checks signature /iss/exp/aud. Operational constraint surfaced:JWT_AUDIENCEmust be unset or AS-only, never a resource server'saud.verification_uri_completeor manual user-code entry. Prevents silent resource binding by an attacker-controlled MCP server.No code change —
README.mdonly (+26 / -0).AI Authorship
/simplifyreview passREADME.md(the only file in the diff)README.md— reviewed via plan-mode approval gate (plan saved at~/.claude/plans/refresh-as-access-token-distributed-lamport.md) and again through the/simplifyreview pass (which flagged and removed an overclaim, a duplicate link, and converted an 87-word RFC sentence to a bulleted list)Change classification
Verification
docs/MCP.md,docs/CONFIGURATION.md, and confirmingdocs/JWT_VERIFICATION.mdexists. Two anchors I originally planned (#token-verification,#device-code-resource-confirmation) did not exist indocs/MCP.md; replaced with the real anchors#audience-binding-via-resource-indicators-rfc-8707and#configuration-checklist.rfc6749,rfc7009,rfc7591,rfc7636,rfc7662,rfc8414,rfc8628,rfc8707) confirmed defined in the README footer.#mcp--multi-resource-hardeningmatches the GitHub-generated anchor for the new### MCP & multi-resource hardeningheading (GitHub strips&and collapses spaces; the double-dash is intentional).Reviewer manual check: render this PR's README diff in GitHub preview, confirm the callout blockquote renders as a single yellow block (not three plain bullets), and click each new internal doc link to confirm it resolves to the intended section.
Risk & rollback
git revert <merge-commit>— single content commit.Reviewer guide
README.md:141-145) and the new### MCP & multi-resource hardeningsubsection (README.md:579-593). These are the new claims about AuthGate's security posture that should be sanity-checked againstdocs/MCP.md:101-148, 212-236and the implementation ininternal/token/local.goandinternal/handlers/device.go:266-280.🤖 Generated with Claude Code