feat: JWT auth middleware + /.well-known/oauth-protected-resource (Dex OIDC) #5
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
auth.d-ma.benow runs Dex (OIDC/OAuth 2.0 AS). All MCP servers should accept JWTs issued by Dex or a static bearer token (backward compat). The currentBearerMiddlewareininternal/auth/bearer.govalidates the bearer as a Gitea PAT via the Gitea API — that model breaks when clients authenticate via claude.ai's OAuth flow instead of supplying a PAT.Goal
Replace the PAT-passthrough auth with a JWT-or-static chain, and expose the RFC 9728 protected resource metadata endpoint.
Changes required
1.
internal/auth/bearer.go— replaceBearerMiddlewareCurrent behaviour: extracts bearer token, calls
GET /api/v1/useron the Gitea host, rejects if non-200.New behaviour:
JWT validation uses JWKS auto-refresh (see §3 below). Do not call the Gitea API on the hot path.
The validated token (JWT subject or static sentinel) can still be stored in context via
auth.StoreTokenInContextfor any downstream use.2.
internal/gitea/client.go— fix service PAT usagedoOnceanddoRawcurrently callauth.TokenFromContext(ctx)first and fall back toc.token. Since the caller's JWT is no longer a Gitea PAT, remove the context-token lookup. Always usec.token(the service PAT configured viaGITEA_MCP_DEFAULT_TOKEN).Verify no other callers of
auth.TokenFromContextrely on per-request PAT injection; if they exist, update them too.3. JWT library + env vars
Add dependency:
github.com/lestrrat-go/jwx/v2New env vars:
DEX_ISSUER_URL— e.g.https://auth.d-ma.be(required when JWT auth is enabled)GITEA_MCP_STATIC_TOKEN— optional static bearer for backward compat / service-to-serviceJWKS endpoint is
{DEX_ISSUER_URL}/.well-known/openid-configuration→jwks_uri. Usejwk.NewCachewith a reasonable refresh interval (1 h).Validation requirements:
iss==DEX_ISSUER_URLaudcontainsgitea-mcp(or the configured client ID)expnot expired4.
/.well-known/oauth-protected-resourceendpoint (RFC 9728)Register a new HTTP handler (outside the MCP router, on the root mux):
Values should come from env vars (
MCP_RESOURCE_URL,DEX_ISSUER_URL) so they're configurable.k3s manifest updates (in
mathias/infra)After the code changes are merged and image rebuilt:
DEX_ISSUER_URL: https://auth.d-ma.betok3s/apps/gitea-mcp/deployment.yamlenvGITEA_MCP_STATIC_TOKENas a reference to the existing bearer secret (or a new one)Testing
/.well-known/oauth-protected-resource, assert JSON shapeRelated
auth.d-ma.beviak3s/apps/auth/inmathias/inframathias/hyperguild(supervisor + brain MCP servers)Shipped in v0.1.x. Closing during cleanup pass.