Skip to content

feat(oauth): add MCP / RFC 8707 + RFC 8414 compatibility#187

Open
appleboy wants to merge 19 commits into
mainfrom
worktree-oauth
Open

feat(oauth): add MCP / RFC 8707 + RFC 8414 compatibility#187
appleboy wants to merge 19 commits into
mainfrom
worktree-oauth

Conversation

@appleboy
Copy link
Copy Markdown
Member

@appleboy appleboy commented May 14, 2026

Summary

Adds OAuth 2.1 / MCP authorization spec compatibility to AuthGate so it can act
as a drop-in authorization server for any Model Context Protocol
deployment. Three gaps closed: RFC 8707 Resource Indicators (audience binding),
RFC 8414 /.well-known/oauth-authorization-server metadata, and CORS on the
/.well-known/* group for browser-based MCP clients.

No new env vars; behaviour is backward-compatible for OAuth clients that don't
supply resource (JWT_AUDIENCE config remains the fallback). See the
Breaking changes section below for source-level and operational concerns.

Scope note (post-review): during Copilot review, the resource-binding
surface was extended end-to-end to close audience-confusion gaps. In addition
to AuthorizationCode and AccessToken, both DeviceCode and
UserAuthorization are now resource-aware, and the device flow gained a
dedicated consent page. See Post-review revisions below.

Breaking changes

End-user OAuth clients (CLIs, web/mobile apps using the documented HTTP
surface) are unaffected — every new parameter is optional and behaviour
without resource is unchanged. The following items only matter for forks,
custom integrations, and operators with non-default deployments.

Source-level (forks / out-of-tree implementations)

  • core.TokenProvider interface signature change (internal/core/token.go).
    Any out-of-tree implementation will fail to compile:
    • GenerateToken, GenerateRefreshToken, GenerateClientCredentialsToken
      each gain a trailing audience []string parameter. Pass nil for the
      pre-PR behaviour.
    • RefreshAccessToken splits its single audience parameter into
      accessAudience, refreshAudience []string — keeping refresh JWTs from
      carrying a per-request RS aud. Pass nil, nil for the pre-PR
      behaviour.
    • New required method: ValidateRefreshToken(ctx, tokenString) (*TokenValidationResult, error).
    • Mocks regenerated in internal/mocks/mock_token.go; vendored copies
      must be refreshed.
  • Caller-supplied aud inside extra_claims is now unconditionally stripped
    at sign time. Callers that previously smuggled audience via extra_claims
    must move to the new resource parameter.

Database

  • New nullable Resource StringArray JSON column on AccessToken,
    DeviceCode, AuthorizationCode, and UserAuthorization. GORM
    AutoMigrate handles the migration on startup; existing rows have empty
    Resource. No data backfill required.
  • AccessToken.Resource has dual semantics — for access-token rows it
    is the JWT aud snapshot taken at issuance; for refresh-token rows it is
    the original grant's resource set (NOT the refresh JWT's aud). Anything
    reading this column directly (custom analytics, external migrations) must
    branch on TokenCategory. See the field's docstring for the full
    rationale.

Operational — JWT_AUDIENCE semantics

Refresh JWTs are signed with the static JWT_AUDIENCE as their aud claim
(unchanged from pre-PR). The PR explicitly tightens the constraint:
deployments MUST point JWT_AUDIENCE at an AS-only value, or leave it
unset. If a deployment currently uses JWT_AUDIENCE=<resource-server-id>,
change it before upgrading — otherwise a refresh JWT could be silently
accepted as an access token by a resource server that only verifies
signature/iss/exp/aud. Access tokens issued without a per-request resource
will then carry no aud (and resource servers should reject tokens whose
aud does not match their own identifier — RFC 8707 §2.2).

AI Authorship

  • No AI was used in this PR
  • AI was used. Details:
    • Tool / model: Claude Opus 4.7 (1M context) via Claude Code
    • AI-authored files: all 51 changed files
    • Human line-by-line reviewed: pending — author directed implementation
      via a written plan, three independent review passes (two /simplify
      cycles and one /security-review), and 7 follow-up Copilot review rounds.
      Each review's findings were fixed in the same branch before this PR was
      marked ready. Reviewers should still expect to read carefully — see
      "Reviewer guide" below.

Change classification

  • Core code — touches authentication, token signing, JWT audience binding,
    and all four OAuth grant flows. Failure is system-wide.
  • Leaf node

Plan reference

/Users/mtk10671/.claude/plans/authgate-mcp-mutable-nebula.md — scope, allowed
files, three required e2e tests, and done-definition all defined there.

Verification

  • Unit tests — internal/util/resource_test.go, internal/util/slice_test.go
  • Integration tests — internal/services/token_resource_test.go (covers
    all four grants end-to-end with resource binding, subset acceptance on
    refresh, superset rejection, cascade-revoke linkage, and JWT-audience
    snapshot fallback)
  • At least 3 e2e tests (1 happy path + 2 errors) — and many more added
    during review:
    • TestAuthCodeFlow_WithResource_PropagatesToAud (happy path)
    • TestAuthorize_RejectsResourceWithFragment (RFC 8707 §2.1 validation)
    • TestRefresh_RejectsResourceSupersetOfOriginal (RFC 8707 §2.2 widening)
    • TestClientCredentials_WithResource_PropagatesToAud
    • TestDeviceCode_WithResource_PropagatesToAud
    • TestDeviceCode_RejectsResourceSupersetOfGrant
    • TestDeviceCode_RejectsResourceWhenNoneGranted
    • TestDeviceCode_LinksAuthorizationIDForCascadeRevoke
    • TestClientCredentials_NoResource_SnapshotsJWTAudience
    • TestRefresh_NarrowsResource_Subset
    • TestAuthorize_InvalidResource_RedirectsAfterValidation
    • TestAuthorize_UnsupportedResponseType_UnregisteredRedirectURI_NotReflected
    • TestAuthorize_InvalidResource_UnregisteredRedirectURI_NotReflected
    • TestDeviceCodeRequest_WithResource_PersistsOnDeviceCode
    • TestDeviceCodeRequest_InvalidResource_ReturnsInvalidTarget
    • TestIntrospectAudience
  • Discovery regression test pins existing OIDC field set so
    /.well-known/openid-configuration shape cannot drift
    (TestOIDCDiscovery_UnaffectedByOAuthMetadataAddition)
  • CORS tests (allow/reject/disabled) on the .well-known group
  • Stress / soak test: N/A — /.well-known/* responses are cached
    (Cache-Control: public, max-age=3600) and the token endpoint is unchanged
    in hot-path shape
  • Manual verification: run ./bin/authgate server then:
    # 1. RFC 8414 metadata
    curl -s http://localhost:8080/.well-known/oauth-authorization-server | jq
    
    # 2. CORS preflight on .well-known
    curl -i -H "Origin: https://allowed.example.com" \
      http://localhost:8080/.well-known/oauth-authorization-server \
      | grep -i access-control-allow-origin
    
    # 3. Auth-code flow with resource (decode the access_token, assert
    #    "aud": "https://mcp.example.com")
    curl -X POST http://localhost:8080/oauth/token \
      -d grant_type=authorization_code -d "code=$CODE" -d "redirect_uri=$RURI" \
      -d "client_id=$CID" -d "code_verifier=$CV" \
      -d "resource=https://mcp.example.com"
    
    # 4. Refresh that tries to widen audience — expect 400 invalid_target
    curl -X POST http://localhost:8080/oauth/token \
      -d grant_type=refresh_token -d "refresh_token=$RT" -d "client_id=$CID" \
      -d "resource=https://forbidden.example.com"

Verifiability check

  • Inputs and outputs are documented — docs/MCP.md and inline godoc
  • Reviewer can judge correctness without reading every line — RFC §
    citations appear at every enforcement point
  • Failures will surface in monitoring — invalid_target errors flow through
    the existing OAuth error response path (counted by existing metrics)

Security check

  • No secrets in code
  • All external inputs validated — ValidateResourceIndicators rejects
    non-http(s) schemes (blocks javascript:/data:/file: aud values),
    empty strings, relative URIs, fragments, and caps the list at 10 entries
    to prevent DoS amplification
  • Permission checks tested — RFC 8707 §2.2 subset rule enforced on refresh,
    on authorization_code → token exchange, on device_code → token
    exchange (against the device-time grant), and on client_credentials
    (against JWT_AUDIENCE snapshot when no resource supplied); tested
    with dedicated tests
  • Open-redirect closed — POST deny path on /oauth/authorize and the
    device-flow consent page both honour the same redirect-URI allowlist
    check; invalid response_type / resource on an unregistered
    redirect_uri are not reflected back
  • Rate limits applied — existing per-endpoint limits on /oauth/* unchanged
  • Errors don't leak internals — invalid_target paths use generic RFC error
    descriptions
  • aud cannot be smuggled via extra_claims — explicit delete(claims, "aud") runs before the audience override applies (defense in depth)
  • Refresh aud is bound at issuance — snapshotted onto the access token
    and re-applied on refresh; refresh cannot widen aud beyond the original
    grant
  • Introspection aud is bound — /oauth/tokeninfo reflects the per-token
    aud rather than the static JWT_AUDIENCE config
  • Cascade-revoke linkage — device-code-issued tokens carry the originating
    authorization ID so admin revoke cleanly reaches all descendants
  • Templ auto-escaping on hidden <input name="resource"> rendering prevents
    reflected XSS

Risk & rollback

  • Riskcore.TokenProvider interface gained a trailing audience []string
    parameter on four methods (GenerateToken, GenerateRefreshToken,
    GenerateClientCredentialsToken, RefreshAccessToken). LocalTokenProvider
    is the only in-tree implementer; any external implementation would break
    at compile time. Mocks were updated in lockstep.

  • Risk — Four models gained nullable resource columns:

    • AuthorizationCode — captures the resource from /oauth/authorize
    • AccessToken — snapshots the issued aud so refresh cannot widen it
    • DeviceCode — captures the resource from /oauth/device/code
    • UserAuthorization — per-resource consent grants (one record per
      user+app+resource tuple, so granting two distinct resources to the same
      app no longer collapses into a single consent record)

    GORM AutoMigrate adds these transparently on Postgres and SQLite; no
    backfill required. Existing rows default to NULL/empty and fall back to
    JWT_AUDIENCE.

  • Riskinternal/templates/device_confirm_page.templ is new (device-flow
    consent screen). Existing device-flow users will see one extra confirm step
    on first authorization; subsequent uses re-use the stored UserAuthorization.

  • Rollback — reverting the PR returns aud to the static-config path.
    Existing tokens remain valid (the new columns default to NULL/empty, and
    the audience source falls back to JWT_AUDIENCE). The new templ template
    is embedded — no asset migration needed.

Reviewer guide

  • Read carefully:
    • internal/util/resource.go — RFC 8707 §2.1 validation (security-critical)
    • internal/handlers/authorization.go — POST deny path's redirect-URI
      allowlist check (open-redirect closure)
    • internal/handlers/token.go:handleAuthorizationCodeGrant — subset rule
      between authorize-time and token-time resource
    • internal/services/token_refresh.go — RFC 8707 §2.2 subset on refresh,
      plus aud snapshot re-application
    • internal/services/token_exchange.go — device-code → token resource
      subset check against the device-time grant
    • internal/services/device.go + internal/models/device_code.go — device
      resource persistence and consent-page rendering
    • internal/services/authorization.go + internal/models/user_authorization.go
      — per-resource consent grants (composite key changed)
    • internal/token/local.go:generateJWTaud source precedence (override
      vs config), explicit strip of caller-supplied aud
    • internal/handlers/oidc.go — new oauthASMetadata shape vs
      discoveryMetadata, plus introspection aud fix
  • Spot-check OK:
    • The services/token_*.go files threading resource end-to-end
      (mechanical change; tests cover the wiring)
    • Test-file mechanical updates that just append nil to call sites
    • Mock regeneration in internal/mocks/mock_token.go
    • internal/templates/device_confirm_page.templ rendering (templ
      auto-escaping handles the only user-controlled field)
  • MCP spec reference:

Post-review revisions

The branch went through 7 Copilot review rounds after the initial implementation
commit. The fixes landed in these commits (oldest → newest):

  • a4926d3 — first round of Copilot findings
  • b765220 — second round
  • 4e10389 — open-redirect closed, refresh-aud snapshot, audience invariants
  • 66e61d0 — device-code resource binding, introspection aud fix
  • eb92f3aUserAuthorization made resource-aware (composite key change)
  • b634503 — device confirm page, cascade-revoke linkage, swagger/docs
  • e41d8d2 — POST deny redirect, resource validation tighten, race fix,
    client-credentials snapshot aud

Net delta vs. initial implementation commit: +13 files, +1776 lines (mostly
test coverage and the device confirm template).

- Add RFC 8707 Resource Indicators across authorization_code, device_code,
  refresh_token, and client_credentials grants
- Bind issued JWT aud to the requested resource and persist resource on
  auth codes and access/refresh token rows
- Enforce subset rule on refresh and token exchange per RFC 8707 §2.2
- Add /.well-known/oauth-authorization-server endpoint (RFC 8414) with
  curated OAuth-only metadata
- Apply CORS middleware to /.well-known/* for browser-based MCP clients
- Reject non-http(s) schemes and cap resource-list size in the validator
- Add docs/MCP.md integration guide

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 02:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds MCP-oriented OAuth compatibility by supporting RFC 8707 resource indicators, publishing OAuth AS metadata, and enabling CORS for well-known discovery endpoints.

Changes:

  • Threads resource indicators through authorization, token issuance, refresh, and JWT audience generation.
  • Adds OAuth AS metadata and well-known CORS coverage.
  • Adds persistence fields, tests, mocks, and MCP integration documentation.

Reviewed changes

Copilot reviewed 37 out of 38 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
internal/util/slice.go Adds string-slice subset helper for resource narrowing checks.
internal/util/slice_test.go Covers subset helper behavior.
internal/util/resource.go Adds resource indicator validation.
internal/util/resource_test.go Covers resource validation cases.
internal/token/local.go Adds audience override support to JWT generation and refresh.
internal/token/local_test.go Updates token provider call sites for audience parameter.
internal/token/local_extra_claims_test.go Updates extra-claims tests for new provider signature.
internal/templates/props.go Adds resource values to authorize page props.
internal/templates/authorize.templ Preserves resources through consent POST.
internal/services/token.go Persists resource values on issued token pairs.
internal/services/token_uid_test.go Updates service tests for resource parameter.
internal/services/token_test.go Updates existing token tests for resource-aware signatures.
internal/services/token_resource_test.go Adds resource/audience integration tests.
internal/services/token_refresh.go Enforces refresh-time resource subset checks.
internal/services/token_profile_test.go Updates token profile test call sites.
internal/services/token_private_claim_prefix_test.go Updates private-claim tests for new signatures.
internal/services/token_introspect_test.go Updates client credentials issuance call site.
internal/services/token_exchange.go Threads resource through auth-code and device-code exchanges.
internal/services/token_domain_test.go Updates domain-claim tests for resource parameter.
internal/services/token_client_credentials.go Threads resource into client credentials access tokens.
internal/services/token_client_credentials_test.go Updates client credentials tests for resource parameter.
internal/services/token_cache_test.go Updates cache tests for new token provider signature.
internal/services/token_cache_bench_test.go Updates benchmark token generation call.
internal/services/authorization.go Persists authorize-time resources on authorization codes.
internal/services/authorization_test.go Updates authorization request validation tests.
internal/models/token.go Adds persisted token resource field.
internal/models/authorization_code.go Adds persisted authorization-code resource field.
internal/mocks/mock_token.go Regenerates token provider mock signatures.
internal/handlers/token.go Parses resource parameters and applies grant-specific behavior.
internal/handlers/token_introspect_test.go Updates token generation call site.
internal/handlers/oidc.go Adds OAuth AS metadata and shared discovery metadata construction.
internal/handlers/oidc_test.go Adds OAuth metadata and OIDC regression tests.
internal/handlers/authorization.go Validates and preserves authorize-time resource indicators.
internal/handlers/authorization_test.go Adds invalid-target mapping and authorize rejection test.
internal/core/token.go Extends token provider interface with audience parameter.
internal/bootstrap/wellknown_cors_test.go Adds well-known CORS tests.
internal/bootstrap/router.go Groups well-known endpoints and applies CORS when enabled.
docs/MCP.md Documents MCP integration and resource indicator behavior.
Files not reviewed (1)
  • internal/mocks/mock_token.go: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/util/resource.go
Comment thread internal/util/resource.go
Comment thread internal/handlers/token.go Outdated
Comment thread internal/services/token_refresh.go
Comment thread internal/handlers/oidc.go Outdated
Comment thread internal/handlers/oidc.go
Comment thread internal/handlers/oidc.go
Comment thread internal/bootstrap/router.go
Comment thread internal/handlers/token.go
Comment thread docs/MCP.md Outdated
- Cap per-resource URI at 1024 chars and require non-empty host for
  http(s) values
- Move RFC 8707 §2.2 subset check into ExchangeCode so a rejected resource
  does not burn the single-use authorization code
- Preserve the rotated refresh token's audience as the original grant so
  narrowing once does not permanently shrink the refresh token's resource
- Restrict introspection endpoint auth methods to exclude `none` since
  /oauth/introspect rejects unauthenticated requests
- Advertise device_authorization_endpoint and resource_indicators_supported
  in the OAuth AS metadata
- Register OPTIONS handlers on /.well-known/* so browser CORS preflights
  reach the CORS middleware instead of 405-ing first
- Update docs/MCP.md preflight example to use OPTIONS with
  Access-Control-Request-Method

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 38 changed files in this pull request and generated 12 comments.

Files not reviewed (1)
  • internal/mocks/mock_token.go: Language not supported

Comment thread internal/handlers/oidc.go
Comment thread internal/handlers/authorization.go Outdated
Comment thread internal/handlers/oidc_test.go
Comment thread internal/handlers/oidc.go
Comment thread internal/handlers/token.go Outdated
Comment thread internal/handlers/oidc_test.go
Comment thread internal/services/token_client_credentials.go
Comment thread internal/handlers/authorization_test.go Outdated
Comment thread internal/handlers/authorization.go
Comment thread internal/templates/authorize.templ
- Reorder /authorize validation so redirect_uri is proven registered before
  the resource parameter is parsed, closing an open-redirect window where
  an invalid resource on an unregistered redirect_uri could be reflected
- Apply strict RFC 8707 §2.2 subset rule at /token: reject any token-time
  resource when /authorize bound none, mirroring the refresh-grant rule
- Preserve the full /authorize-time grant on the refresh token issued via
  authorization_code so future refreshes can re-narrow against the original
  audience rather than the access token's narrowed set
- Include `aud` in the RFC 7662 introspection response so resource servers
  that authorize via introspection can enforce audience binding
- Skip ConsentRemember when the request includes resource indicators, so a
  user must explicitly approve each new audience their tokens bind to
- Render requested resources visibly on the consent page next to scopes
- Emit `resource_parameter_supported` alongside `resource_indicators_supported`
  in the OAuth AS metadata for both naming conventions in the wild
- Add a client_credentials + resource integration test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 38 changed files in this pull request and generated 9 comments.

Files not reviewed (1)
  • internal/mocks/mock_token.go: Language not supported
Comments suppressed due to low confidence (1)

internal/handlers/token.go:440

  • This emits aud for every active token row, including refresh tokens whose Resource is now persisted. Since the introspection response still reports refresh tokens as active with token_type: "Bearer" and does not expose the access/refresh category, a resource server that relies on RFC 7662 plus aud cannot distinguish a refresh token from an access token. Restrict this audience-bearing response to access tokens or include/enforce the token category so refresh tokens are not accepted as resource-server credentials.
	// Audience binding: prefer the RFC 8707 resource set persisted at
	// issuance; fall back to the static JWTAudience config when no resource
	// was requested. Resource servers rely on this to enforce that a token
	// minted for service A cannot be replayed against service B.
	if aud := introspectAudience(tok, h.config.JWTAudience); aud != nil {
		resp["aud"] = aud

Comment thread internal/handlers/token.go Outdated
Comment thread internal/services/token_exchange.go Outdated
Comment thread internal/handlers/authorization_test.go Outdated
Comment thread internal/services/authorization.go
Comment thread internal/services/token_refresh.go Outdated
Comment thread internal/handlers/token.go Outdated
Comment thread docs/MCP.md Outdated
Comment thread internal/services/token.go Outdated
Comment thread internal/services/token_refresh.go Outdated
…ariants

- Render an error page (not a redirect) when ValidateAuthorizationRequest
  fails with ErrInvalidRedirectURI or ErrUnauthorizedClient, per RFC 6749
  §3.1.2.4. Closes the open-redirect path where an unregistered redirect_uri
  was still reflected as the OAuth error redirect target
- Split provider RefreshAccessToken into accessAudience and refreshAudience;
  refresh tokens no longer carry a resource-server `aud` claim (they go to
  the AS, not the RS, so emitting an RS audience risked confusing JWT
  validators that only check signature/iss/exp/aud)
- Eliminate the wasted second access-token mint in token rotation: with the
  two-audience provider signature the access token is issued with the
  narrowed audience and the refresh token with no resource audience in a
  single round-trip
- Add a service-boundary RFC 8707 §2.2 subset check in
  ExchangeAuthorizationCode so any future call path bypassing the handler
  still enforces the audience invariant
- Drop the JWTAudience config fallback in the introspection response so
  rotating JWT_AUDIENCE cannot diverge introspect `aud` from the JWT
- Document that resource servers MUST reject non-access tokens by checking
  the `type` claim, otherwise a refresh token could be mistaken for one
- Add tests for ExchangeCode subset rule (allowed subset, rejected superset
  with code remaining unconsumed, rejected request against empty grant)
- Add a handler-integration test asserting the open-redirect mitigation:
  invalid resource on an unregistered redirect_uri is NOT reflected

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@appleboy appleboy requested a review from Copilot May 14, 2026 06:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

- Stop reusing the green scope-check circle for resource indicators
  so the page no longer reads "permission granted" for an audience target
- Replace the arrow glyph with a bullseye SVG icon that conveys the
  audience-target meaning without depending on font emoji support
- Introduce dedicated resource list classes with a blue left-border
  accent and tinted hover so resources and scopes form two distinct
  visual categories at a glance
- Render the resource URI in a code element so screen readers announce
  it in monospace voice and long values wrap inside the row
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

- Advertise MCP, multi-resource-server, RFC 8707 Resource Indicators, and
  RFC 8414 AS metadata in the readme tagline, Perfect-for list, Key
  Features, advanced-topics index, endpoints table, and references
- Document the JWT_AUDIENCE operational constraint in the configuration
  guide: refresh JWTs are signed with this value so it MUST be AS-only
  or unset, never a resource server identifier
- Update the CORS section to cover the well-known route group used by
  browser-based MCP and SPA clients, and add an internal-network caveat
- Add an Audience Binding section to the JWT verification guide that
  walks resource servers through validating aud and type, the snapshot
  semantics of introspection, and refresh-token-as-access-token
  confusion mitigation
- Add WithAudience to the Go verification example so readers do not
  ship a sample that skips the aud check
- Cross-link the MCP guide to JWT verification and configuration and
  repeat the JWT_AUDIENCE warning at the end of the checklist
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

- Add the resource parameter to the authorize and token tables in the
  authorization code flow guide, with a public-client PKCE example that
  binds two audiences and a note on the strict consent-match rule
- Add the resource parameter to the device code request and poll tables
  with an MCP example, and explain that resource-bound device codes
  route through an explicit confirmation page before authorization
- Add the resource parameter to the client credentials token request
  table with a multi-RS example and a callout that the M2M grant has no
  per-client allowlist, so resource servers must validate the
  (client_id, sub, aud) tuple against their own policy
- Add audience binding and refresh-token-as-access-token mitigation
  entries to the security threat model and production checklist,
  including the JWT_AUDIENCE constraint and the type-claim requirement
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…source indicators

- Add the AS metadata endpoint to the architecture endpoints table,
  document the new resource column on four tables with the dual access
  vs refresh semantics, and flag the TokenProvider interface signature
  change as a breaking surface for out-of-tree implementations
- Add two new use cases: AuthGate as the authorization server for an
  MCP deployment (with the PRM + AS discovery + token verification
  trifecta) and multi-resource-server audience binding with a
  side-by-side comparison against scope-only isolation
- Add troubleshooting entries for the invalid_target error with a
  diagnose table covering each rejection reason, the refresh-as-access
  token-confusion failure mode with both AS-side and RS-side fixes,
  and the consent-re-prompt-on-resource-set-mismatch behaviour
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

appleboy added 2 commits May 17, 2026 10:55
Apply the project's markdown formatter to the documentation files
updated in the recent resource-indicator documentation rounds. The
formatter rewrites asterisk-style emphasis to underscore-style for
consistency with the rest of the docs tree; no prose, examples, or
links change.
Apply the project's markdown formatter to the key-endpoints table. The
formatter pads every row to the width of the widest cell so the column
borders line up. Pure cosmetic — no endpoint content or wording changes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants