fix: static token rejected by requireBearerAuth (missing expiresAt)#60
Merged
Conversation
The MCP SDK's requireBearerAuth rejects any AuthInfo whose expiresAt is
not a number ("Token has no expiration time" → 401). verifyAccessToken's
static-token fast path returned { token, clientId, scopes } with no
expiresAt, so a static MCP_AUTH_TOKEN bearer was always 401'd at the
Express layer. This went unnoticed because E2E was exercised via OAuth
(JWT tokens carry expiresAt); the static-bearer path — documented for
deploy/local and CLI tools — was never hit by a real client.
Surfaced while bridging the HTTP server to stdio for Glama's introspector:
a direct curl to /mcp with the correct Bearer token returned 401 while
the server's MCP_AUTH_TOKEN was confirmed correct.
Fix: give the static token a far-future numeric expiresAt (now + 10y,
recomputed per request) so it satisfies requireBearerAuth while staying
effectively perpetual. Added a test asserting expiresAt is a future number.
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.
Summary
Real auth bug, surfaced while bridging the HTTP server to stdio for Glama's introspector. A static
MCP_AUTH_TOKENbearer is rejected with401at the Express layer, even when the token is correct.Root cause (confirmed against the SDK source)
@modelcontextprotocol/sdkrequireBearerAuthenforces:But
verifyAccessToken's static-token fast path returned{ token, clientId: "static", scopes: ["vault"] }with noexpiresAt→ always 401 for the static bearer. The JWT/OAuth path setsexpiresAt, so E2E (tested via OAuth) passed and this never showed. The static-bearer path — documented fordeploy/localand CLI tools — was never exercised by a real client.Evidence
A
curlstraight to/mcpwithAuthorization: Bearer <token>(bypassing the bridge) returned401, while the server'sMCP_AUTH_TOKENwas confirmed to be exactly the token sent.Fix
Give the static token a far-future numeric
expiresAt(now + 10y, recomputed per request) so it satisfiesrequireBearerAuthwhile staying effectively perpetual. Added a test assertingexpiresAtis a future number (the old test only checkedclientId/scopes/token, which is exactly why the gap slipped through).Validation
tsc --noEmitcleanImpact / follow-on
deploy/local, CLI), not just Glama.git clone+ checkout latestmain), so once merged a re-run will pick it up.deploy/localusers), this needs a v0.15.5 release to ship the fix into GHCR.Generated by Claude Code