docs(security): make the HTTP + OAuth2 setup walk end to end - #211
Open
adityamparikh wants to merge 2 commits into
Open
adityamparikh wants to merge 2 commits into
adityamparikh wants to merge 2 commits into
Conversation
Someone following the security guides could configure an IdP, start the server and still be unable to call a tool from any MCP client. Two facts the docs got wrong or left out: - The audience the server validates is not a fixed string. mcp-server- security builds it per request from the scheme, host and port the client used plus /mcp, so aud must contain the exact URL the client dials. The Auth0 guide (and the site mirror) told readers to use https://solr-mcp-api, which every token then fails with "The aud claim is not valid". The guide also claimed no audience validation is performed, listed a login/oauth2/code callback the resource server does not have, and showed a stale issuer default. - The server never answers an anonymous /mcp request with 401: the handshake is permitAll and @PreAuthorize denies inside each tool with an HTTP 200 "Access Denied" result. Claude Code, the MCP Inspector and mcp-remote start OAuth only on a 401, so the client guides' "handles the OAuth2 flow automatically" never happens; the client reports connected and every call is denied. The verified path is a bearer header. Changes: - http.md: how the audience is derived, a "Connecting an MCP client" section with the observed status matrix and the header syntax per client, corrected production guidance for the empty issuer default. - keycloak.md: audience spelling note, a "Connecting an MCP Client" section (raise the 300 s token lifetime, Claude Code and Inspector commands, what "Connected" does and does not prove). - auth0.md: rewritten around the resource-URI identifier, with run, verify and connect steps. - Client guides (repo and site mirror): replace the automatic-OAuth claims with the header configuration for Claude Code, Claude Desktop and JetBrains via mcp-remote, VS Code, Cursor and the Inspector. - Site security page, DEPLOYMENT.md and the Auth0 token script comments brought in line. Verified on 2026-09-16 against Keycloak 26.0, Solr 9 and Claude Code 2.1.273: curl, the Inspector CLI and Claude Code with --header all call list-collections; the same token sent to 127.0.0.1 is rejected on aud; without a header Claude Code shows Connected and returns Access Denied. The Auth0 steps follow the same server-side rules but were not re-run against a live tenant. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
…et yields only the token With .env present the script printed "[INFO] Loading configuration from .env file" before --quiet was parsed, so TOKEN=$(... --quiet) as the docs show captured the log line ahead of the token. Route all status output to stderr; stdout now carries the token alone. Also records that the HTTP client-connection matrix was re-verified against an Auth0 tenant: the dashboard accepts http://localhost:8080/mcp as an API identifier, and the resulting client-credentials token (iss with trailing slash, aud = the identifier, 24 h lifetime) passes the server's checks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
adityamparikh
force-pushed
the
docs/http-security-walkthrough
branch
from
September 24, 2026 14:41
42f5209 to
ede695d
Compare
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.
Problem
Someone following the security guides could configure an IdP, start the server in HTTP mode with security on, and still be unable to call a single tool from an MCP client. Two facts the docs got wrong or left out:
McpServerOAuth2Configurerbuilds the expectedaudper request from the scheme, host and port the client used plus/mcp. The Auth0 guide and the site page told readers to usehttps://solr-mcp-apias the API identifier, which produces tokens the server rejects withThe aud claim is not valid. The Auth0 guide also stated that no audience validation is performed, listed alogin/oauth2/code/auth0callback that a resource server does not have, and showed a stale issuer default./mcprequest with 401./mcpispermitAlland@PreAuthorizedenies inside each tool with an HTTP 200Access Deniedresult. Claude Code, the MCP Inspector andmcp-remotestart OAuth only on a 401/403, so the client guides' "handles the OAuth2 flow automatically" never happens: the client reports Connected, lists every tool, and every call is denied, with no browser opening. The path that works is a bearer header.Changes
docs/security/http.md: how the audience is derived and how to read it from the running server; a new Connecting an MCP client to a secured server section with the observed status matrix and the header syntax for Claude Code, Inspector (CLI and web),mcp-remote, VS Code, Cursor and Zed; corrected production guidance for the empty issuer default.docs/security/keycloak.md: audience spelling note (localhostvs127.0.0.1); a Connecting an MCP Client section that raises the 300 s token lifetime for a trial, gives the Claude Code and Inspector commands, and explains thatclaude mcp listsays Connected either way so only a tool call proves the gate.docs/security/auth0.md: rewritten around the resource-URI identifier, with run, verify and connect steps; phantom callback and the "no audience validation" claim removed.docs/clients/*.mdand thedocs/site/.../clients/*.mdmirror: the automatic-OAuth sentences replaced with header configuration per client (VS Codeinputs+headers, Cursor${env:…}, Claude Desktop and JetBrains viamcp-remote --header, Inspector--header).docs/site/content/pages/mcp/security.md,dev-docs/DEPLOYMENT.md,scripts/get-auth0-token.shcomments: brought in line.Verification
Run on 2026-09-16 against Keycloak 26.0 (realm created with the guide's Quick Start block), Solr 9 and Claude Code 2.1.273, server started from the fat JAR with
PROFILES=http HTTP_SECURITY_ENABLED=true OAUTH2_ISSUER_URI=http://localhost:8180/realms/solr-mcp:initialize,tools/listtools/call list-collectionsAccess Denied,isError: truetools/call list-collections["books","films"]http://127.0.0.1:8080/mcpThe aud claim is not validtools/listMalformed token/actuator/metricswith / without token--header/ withoutAccess Deniedclaude mcp add … --header, then alist-collectionscall viaclaude -p["books","films"]✔ Connected, tool call returnsAccess Denied, no OAuth promptPUT /admin/realms/solr-mcp {"accessTokenLifespan":3600}exp - iat = 3600Re-run against an Auth0 tenant on the same day: the dashboard accepts
http://localhost:8080/mcpas an API Identifier, the client-credentials token carriesisswith a trailing slash,audequal to the identifier and a 24 h lifetime, and the same matrix holds (anonymous call denied, token call returns the collections,127.0.0.1rejected onaud, actuator 200/401, Inspector CLI and Claude Code with--headerboth succeed).Second commit:
scripts/get-auth0-token.sh --quietprinted an[INFO]line to stdout before the token when.envwas present, so the documentedTOKEN=$(... --quiet)captured the log line. Status output now goes to stderr.Interactive browser login (
claude mcp login, the Inspector's OAuth flow) needs an IdP client registration and is stated as out of scope rather than described../gradlew build(JDK 25): 421 tests, 0 failures, 0 skipped;ratandspotlessCheckpass (re-run after the script change, same result).Overlap with open PRs
keycloak.md(Spring AI client step, compose-based Quick Start). The section added here sits between them.http.md, which this PR does not touch.🤖 Generated with Claude Code