Skip to content

Resolve JWKS keys in-process for embedded auth server (MCP server)#4502

Merged
tgrunnagle merged 3 commits intomainfrom
issue_4466_self-ref-jwksurl
Apr 2, 2026
Merged

Resolve JWKS keys in-process for embedded auth server (MCP server)#4502
tgrunnagle merged 3 commits intomainfrom
issue_4466_self-ref-jwksurl

Conversation

@tgrunnagle
Copy link
Copy Markdown
Contributor

@tgrunnagle tgrunnagle commented Apr 2, 2026

Summary

When the embedded auth server is enabled, token validation currently fails silently because the token validator fetches JWKS keys over HTTP from the proxy's own endpoint. This self-referential HTTP call requires operators to set insecureAllowHTTP and/or jwksAllowPrivateIP flags — insecure workarounds that are difficult to debug when missing.

This PR eliminates the self-referential HTTP fetch by wiring the embedded auth server's KeyProvider directly into the token validator. When both components run in the same process, JWKS keys are resolved in-memory with a graceful fallback to HTTP for cases where the local provider cannot satisfy the request.

Note: this only addresses the issue for the runner and proxy runner - vMCP wiring will come in a separate PR.

Related to #4466

Type of change

  • New feature

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)

Changes

File Change
pkg/auth/token.go Add keyProvider field to TokenValidator, WithKeyProvider option, getKeyFromLocalProvider method that resolves keys in-process before falling back to HTTP, and relax the JWKS URL requirement when a local provider is present
pkg/auth/middleware.go Pass KeyProvider from runner into token validator options during middleware creation
pkg/authserver/runner/embeddedauthserver.go Store and expose keyProvider from the auth server's key material
pkg/runner/runner.go Capture KeyProvider from the embedded auth server during startup and expose it via GetKeyProvider()
pkg/transport/types/transport.go Extend MiddlewareRunner interface with GetKeyProvider()
pkg/transport/types/mocks/mock_transport.go Regenerated mock for updated MiddlewareRunner interface
pkg/auth/token_test.go Tests for getKeyFromLocalProvider: kid match, kid miss (fallback), provider error (fallback), unsupported signing method, missing kid
pkg/authserver/runner/embeddedauthserver_test.go Test that KeyProvider() returns a functional provider after construction
pkg/auth/middleware_test.go Updated existing middleware tests for new GetKeyProvider() expectation

Does this introduce a user-facing change?

Yes. Operators using the embedded auth server no longer need to configure jwksUrl, insecureAllowHTTP, or jwksAllowPrivateIP for token validation to work. The embedded auth server's signing keys are resolved in-process automatically.

Special notes for reviewers

  • getKeyFromLocalProvider returns (nil, nil) to signal "fall back to HTTP" — this keeps the existing HTTP-based JWKS path untouched as a fallback and avoids breaking external OIDC providers.
  • The resolveClientSecret helper was extracted as a standalone function during the refactor; the logic is unchanged.
  • The ErrMissingIssuerAndJWKSURL validation is now relaxed when a keyProvider is present, since keys can be resolved without a JWKS URL.

Generated with Claude Code

The token validator previously fetched JWKS keys over HTTP from the
proxy's own endpoint, requiring insecureAllowHTTP and jwksAllowPrivateIP
workarounds. When the embedded auth server is active, resolve keys
directly from the in-memory KeyProvider, falling back to HTTP for
external issuers.

Closes #4466

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added the size/M Medium PR: 300-599 lines changed label Apr 2, 2026
Deduplicate signing method validation and kid extraction used by both
getKeyFromLocalProvider and getKeyFromJWKS into a single helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Apr 2, 2026
@tgrunnagle tgrunnagle marked this pull request as ready for review April 2, 2026 16:21
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

❌ Patch coverage is 75.47170% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.13%. Comparing base (26f6e1e) to head (a87e9ae).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
pkg/auth/token.go 77.27% 5 Missing and 5 partials ⚠️
pkg/auth/middleware.go 0.00% 1 Missing and 1 partial ⚠️
pkg/runner/runner.go 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4502      +/-   ##
==========================================
- Coverage   69.31%   69.13%   -0.19%     
==========================================
  Files         502      502              
  Lines       51632    51959     +327     
==========================================
+ Hits        35791    35922     +131     
- Misses      13075    13250     +175     
- Partials     2766     2787      +21     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tgrunnagle tgrunnagle changed the title Resolve JWKS keys in-process for embedded auth server Resolve JWKS keys in-process for embedded auth server (MCP server) Apr 2, 2026
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Apr 2, 2026
Copy link
Copy Markdown
Contributor

@jerm-dro jerm-dro left a comment

Choose a reason for hiding this comment

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

Thanks for picking this up — the overall design is great. Clean fallback behavior, solid test coverage, and this solves a real pain point for operators. I'm just being a stickler on the interface boundary to keep private key access intentional.

@tgrunnagle tgrunnagle requested a review from jerm-dro April 2, 2026 19:09
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Apr 2, 2026
@tgrunnagle tgrunnagle merged commit 6adbe2b into main Apr 2, 2026
40 checks passed
@tgrunnagle tgrunnagle deleted the issue_4466_self-ref-jwksurl branch April 2, 2026 20:10
MatteoManzoni pushed a commit to DocPlanner/toolhive that referenced this pull request Apr 4, 2026
…tacklok#4502)

When the embedded auth server is enabled, token validation currently fails silently because the token validator fetches JWKS keys over HTTP from the proxy's own endpoint. This self-referential HTTP call requires operators to set `insecureAllowHTTP` and/or `jwksAllowPrivateIP` flags — insecure workarounds that are difficult to debug when missing.

This PR eliminates the self-referential HTTP fetch by wiring the embedded auth server's `KeyProvider` directly into the token validator. When both components run in the same process, JWKS keys are resolved in-memory with a graceful fallback to HTTP for cases where the local provider cannot satisfy the request.

Note: this only addresses the issue for the runner and proxy runner - vMCP wiring will come in a separate change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Medium PR: 300-599 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants