Skip to content

fix(security): enable JWT audience validation and declare MCP resource path#123

Merged
epugh merged 1 commit into
apache:mainfrom
adityamparikh:fix/security-jwt-audience-validation
May 8, 2026
Merged

fix(security): enable JWT audience validation and declare MCP resource path#123
epugh merged 1 commit into
apache:mainfrom
adityamparikh:fix/security-jwt-audience-validation

Conversation

@adityamparikh
Copy link
Copy Markdown
Contributor

Summary

Per the MCP Authorization specification:

MCP servers MUST validate that tokens presented to them were specifically issued for their use … MUST reject tokens that do not include them in the audience claim.

Without audience validation, any valid JWT from the same IdP issued for any sibling application is accepted by this server — a classic token-confusion pivot (CWE-345).

This PR wires the existing `McpServerOAuth2Configurer` with:

  • `.resourcePath("/mcp")` — declares the canonical resource indicator surfaced via OAuth 2.0 Protected Resource Metadata (RFC 9728).
  • `.validateAudienceClaim(true)` — enforces that the JWT `aud` claim matches that resource indicator (RFC 8707).

These options are already provided by the upstream `spring-ai-community/mcp-server-security` library; this PR just turns them on.

Operator impact

When `http.security.enabled=true`, the IdP must populate the JWT `aud` claim with the MCP server's URL. The expanded comment block in `application-http.properties` documents the per-IdP setup:

IdP Configuration
Auth0 Client passes `audience=` on auth request → reflected into `aud` automatically
Okta Configure the audience on the Authorization Server
Keycloak Add an Audience protocol mapper on a client scope (Keycloak does not yet support RFC 8707 `resource=` natively)

`http.security.enabled=false` (current default) is unaffected.

Test plan

  • `./gradlew spotlessApply` clean
  • `./gradlew build` passes (full test suite, 37s)
  • Manual verification with an IdP issuing tokens carrying the correct `aud`

Note on PR ordering

Touches `HttpSecurityConfiguration.java` and `application-http.properties`, which overlap with PR #121 (CORS allowlist). Whichever lands second will need a small rebase.

🤖 Generated with Claude Code

…e path

Per the MCP Authorization specification, MCP servers MUST validate that
tokens were specifically issued for them — otherwise any valid JWT from
the same IdP for any sibling application is accepted, enabling
token-confusion pivots (CWE-345).

Wires the existing McpServerOAuth2Configurer with:
  - resourcePath("/mcp") — declares the canonical resource indicator
    surfaced via OAuth 2.0 Protected Resource Metadata (RFC 9728), which
    MCP clients use to discover the authorization server.
  - validateAudienceClaim(true) — enforces that the JWT "aud" claim
    matches that resource indicator (RFC 8707).

Operators must configure their IdP to populate "aud" with the MCP
server's URL. The application-http.properties comment block documents
the required setup for Auth0, Okta, and Keycloak (which does not yet
honor RFC 8707 `resource=` natively and needs an Audience protocol
mapper on a client scope).

Refs:
- MCP Authorization spec, "Token Audience Binding and Validation":
  https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization
- RFC 8707 (Resource Indicators):
  https://www.rfc-editor.org/rfc/rfc8707.html
- RFC 9728 (Protected Resource Metadata):
  https://www.rfc-editor.org/rfc/rfc9728.html
- Keycloak MCP integration docs (Audience mapper workaround):
  https://www.keycloak.org/securing-apps/mcp-authz-server

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
@epugh epugh merged commit 74e9a9a into apache:main May 8, 2026
7 of 8 checks passed
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