This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git
The following commit(s) were added to refs/heads/main by this push:
new 74e9a9a fix(security): enable JWT audience validation and declare MCP
resource path (#123)
74e9a9a is described below
commit 74e9a9ad3274e6c3f784b13332d52c3ad3a527b0
Author: Aditya Parikh <[email protected]>
AuthorDate: Fri May 8 16:56:26 2026 -0400
fix(security): enable JWT audience validation and declare MCP resource path
(#123)
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
Signed-off-by: adityamparikh <[email protected]>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
.../mcp/server/security/HttpSecurityConfiguration.java | 15 +++++++++++++--
src/main/resources/application-http.properties | 12 ++++++++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git
a/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
b/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
index ac0c3ae..61f5663 100644
---
a/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
+++
b/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
@@ -50,9 +50,20 @@ class HttpSecurityConfiguration {
auth.requestMatchers("/mcp").permitAll();
auth.anyRequest().authenticated();
})
- // Configure OAuth2 on the MCP server
+ // Configure OAuth2 on the MCP server.
+ //
+ // resourcePath: declares "/mcp" as the
canonical resource indicator
+ // for OAuth 2.0 Protected Resource Metadata
(RFC 9728), which is what
+ // MCP clients use to discover the
authorization server.
+ //
+ // validateAudienceClaim: per the MCP
Authorization specification, MCP
+ // servers MUST validate that tokens were
specifically issued for them.
+ // The audience is matched against the resource
indicator (RFC 8707)
+ // configured above. The IdP must populate the
JWT "aud" claim
+ // accordingly — see docs/security/http.md for
IdP configuration notes.
.with(McpServerOAuth2Configurer.mcpServerOAuth2(),
- mcpAuthorization ->
mcpAuthorization.authorizationServer(issuerUrl))
+ mcpAuthorization ->
mcpAuthorization.authorizationServer(issuerUrl).resourcePath("/mcp")
+
.validateAudienceClaim(true))
// MCP inspector
.cors(cors ->
cors.configurationSource(corsConfigurationSource())).csrf(CsrfConfigurer::disable)
.build();
diff --git a/src/main/resources/application-http.properties
b/src/main/resources/application-http.properties
index 04504c0..1364348 100644
--- a/src/main/resources/application-http.properties
+++ b/src/main/resources/application-http.properties
@@ -10,6 +10,18 @@ spring.docker.compose.enabled=true
# For Auth0: https://<your-auth0-domain>/.well-known/openid-configuration
# For Keycloak: https://<keycloak-host>/realms/<realm-name>
# For Okta:
https://<your-okta-domain>/oauth2/default/.well-known/openid-configuration
+#
+# Audience validation: when http.security.enabled=true, the MCP server enforces
+# that incoming JWTs carry an "aud" claim matching its canonical resource URI
+# (per RFC 8707 / the MCP Authorization specification). The IdP must be
+# configured to populate "aud" accordingly:
+# * Auth0 - pass `audience=<MCP server URL>` on the auth request; Auth0
+# reflects it into "aud" automatically.
+# * Okta - configure the audience on the Authorization Server.
+# * Keycloak - add an "Audience" protocol mapper on a client scope and set
+# `Included Custom Audience` to the MCP server URL (Keycloak does
+# not yet honor RFC 8707 `resource=` natively, see
+# docs/security/http.md).
spring.security.oauth2.resourceserver.jwt.issuer-uri=${OAUTH2_ISSUER_URI:https://your-auth0-domain.auth0.com/}
# Security toggle - HTTP mode is secured by default. Set
HTTP_SECURITY_ENABLED=false
# to bypass OAuth2 authentication for local development only. Disabling
security