Skip to content

auth: check token expiry before required scopes - #1350

Merged
guglielmo-san merged 1 commit into
modelcontextprotocol:mainfrom
ilaigold:auth-verify-expiry-before-scope
Oct 7, 2026
Merged

guglielmo-san merged 1 commit into
modelcontextprotocol:mainfrom
ilaigold:auth-verify-expiry-before-scope

Conversation

@ilaigold

@ilaigold ilaigold commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

While reading RequireBearerToken I noticed that verify checks required scopes before expiry. A token that's expired and also missing a scope gets 403 insufficient scope instead of 401 token expired, and a token with no expiration gets the same 403 when AllowMissingExpiration is false. Repro is in #1349.

An expired token is invalid_token (RFC 6750 §3.1), so the client should be told to get a new token, not to ask for more scopes. I moved the expiry check, including the missing-expiration case, above the scope loop. No API change.

This touches the same lines in verify and TestVerify as #1135. If that one lands first I'll rebase on it.

Tested with go1.27.1 on darwin/arm64:

  • The two new TestVerify cases ("expired and missing scope", "no expiration and missing scope") fail on main with got ("insufficient scope", 403) and pass with the fix.
  • gofmt -l auth and go vet ./... are clean, and go test -count=1 ./... passes.
  • The repro from the issue now prints 401 token expired.

Fixes #1349

AI assistance: I used Claude Code to help write this fix and the tests.

🤖 Generated with Claude Code

verify checked the required scopes before the token's expiration, so a
token that was both expired and missing a required scope was rejected
with 403 "insufficient scope" instead of 401 "token expired".

Per RFC 6750 section 3.1, an expired token is invalid_token (401). It
grants nothing, so it cannot have insufficient scope (403). The 403
tells the client to request more scopes when it needs a new token.

Check expiration, including a missing expiration, before scopes.

Fixes modelcontextprotocol#1349

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@guglielmo-san
guglielmo-san merged commit 8dd5d6a into modelcontextprotocol:main Oct 7, 2026
9 checks passed
@guglielmo-san

Copy link
Copy Markdown
Contributor

@ilaigold thank you for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

auth: RequireBearerToken returns 403 instead of 401 for an expired token missing a scope

2 participants