Skip to content

auth: advertise RFC 6750 error code in WWW-Authenticate challenge - #1135

Open
PratikDhanave wants to merge 1 commit into
modelcontextprotocol:mainfrom
PratikDhanave:fix/bearer-www-authenticate-error-code
Open

auth: advertise RFC 6750 error code in WWW-Authenticate challenge#1135
PratikDhanave wants to merge 1 commit into
modelcontextprotocol:mainfrom
PratikDhanave:fix/bearer-www-authenticate-error-code

Conversation

@PratikDhanave

Copy link
Copy Markdown

Summary

RequireBearerToken never added an error= auth-param to the WWW-Authenticate header it emits. On insufficient scope it returned 403 with resource_metadata/scope but no error="insufficient_scope".

The SDK's own client gates step-up re-authorization on exactly that value (AuthorizationCodeHandler.Authorize):

if resp.StatusCode == http.StatusForbidden && errorFromChallenges(wwwChallenges) != "insufficient_scope" {
    return nil // skip step-up
}

errorFromChallenges reads Params["error"]; since the server never set it, it returned "", the condition was true, and step-up never fired. A client hitting an SDK-based server that needs additional scopes just kept failing with 403 instead of upgrading scopes. It's also an RFC 6750 §3.1 deviation.

Fixes #1134

Change

Thread the RFC 6750 error code out of verify and add the error= auth-param to the challenge:

Failure code error=
insufficient scope 403 insufficient_scope
invalid / expired / missing-exp token 401 invalid_token
ErrOAuth 400 invalid_request
no token (no credentials) 401 (none — per RFC 6750)
5xx 500 (none)

The change only adds a param to the header — it is non-breaking.

Testing

  • Extended TestVerify to assert the error code returned for each failure path.
  • Added TestRequireBearerTokenAdvertisesInsufficientScope, which drives the middleware end-to-end and parses the emitted header with the same oauthex.ParseWWWAuthenticate the client uses, asserting error="insufficient_scope" is visible.
  • Updated TestRequireBearerToken's expected WWW-Authenticate headers to include the new code.
  • Verified the interop test fails without the fix (error param = "", want "insufficient_scope") and passes with it. go vet ./auth/ and the full go test ./auth/ suite pass.

RequireBearerToken never added an error= auth-param to the
WWW-Authenticate header, so on insufficient scope it returned 403 with
resource_metadata/scope but no error="insufficient_scope". The SDK's own
client gates step-up re-authorization on exactly that value
(AuthorizationCodeHandler.Authorize), so errorFromChallenges returned ""
and step-up never fired — a client hitting an SDK server that needs more
scopes just kept failing with 403 instead of upgrading scopes.

Thread the RFC 6750 error code out of verify and add the error= param:
insufficient_scope on the 403 scope check, invalid_token on the
token-invalid 401 paths, invalid_request on the 400. A missing token (no
credentials presented) and 5xx failures advertise no code. The change is
additive to the header, non-breaking.

Fixes modelcontextprotocol#1134
Comment thread auth/auth.go
}
if errors.Is(err, ErrOAuth) {
return nil, err.Error(), http.StatusBadRequest
return nil, err.Error(), http.StatusBadRequest, "invalid_request"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this will not trigger the authorization flow as the code in @authorization_code is just checking for http.StatuForbidden case.
I am not sure tho if we should extend the case for the flow re-trigger

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.

RequireBearerToken omits RFC 6750 error code, so the SDK's own step-up flow never triggers

2 participants