[BUG] Step-up authorization not working for scope elevation in MCP TypeScript SDK

Resolved 💬 4 comments Opened Feb 24, 2026 by gurudatta-stripe Closed Mar 25, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

File: packages/client/src/client/auth.ts in modelcontextprotocol/typescript-sdk
The MCP client fails to obtain elevated scopes during step-up authorization (403 insufficient_scope) because the authInternal() function refreshes tokens without requesting the new scopes.

https://github.com/modelcontextprotocol/typescript-sdk/blob/0096e4a1d30334188619f686d8fbc97245e7f410/packages/client/src/client/auth.ts

What Actually Happens (if refresh_token exists)

  1. ✅ Client makes request with token
  2. ✅ Server returns 403 insufficient_scope with scope="products:write"
  3. ✅ Client calls auth(authProvider, { scope: "products:write", ... })
  4. Auth function refreshes token WITHOUT passing new scopes
  5. ❌ Gets same token back with original scopes (missing products:write)
  6. ❌ Retry fails with same 403
  7. ✅ Infinite loop protection aborts: "Server returned 403 after trying upscoping"

The Bug

Location

File: packages/client/src/client/auth.ts
Function: authInternal() at lines 560-573

Problem

  1. Missing scope parameter: Line 563-570 doesn't pass the scope parameter to refreshAuthorization(), so it refreshes with the same scopes as the original token
  2. No fallback to user consent: If refresh succeeds but doesn't grant new scopes, the function never falls through to start a new OAuth flow (lines 584-595)

What Actually Happens (if refresh_token is not there)

  1. ✅ Client makes request with token
  2. ✅ Server returns 403 insufficient_scope with scope="products:write"
  3. ✅ Client calls auth(authProvider, { scope: "products:write", ... })
  4. ✅ Client invokes pkce flow and sends redirect url
  5. ❌ Auth Client provider disabled redirect_url, and doesn't show to user to consent for step-up authz
  6. ❌ Retry fails with 401 (token expired)

What Should Happen?

  1. Client makes request with token
  2. Server returns 403 Forbidden with:

``http
WWW-Authenticate: Bearer error="insufficient_scope",
scope="products:write"
``

  1. Client detects insufficient scope and calls auth() with elevated scopes
  2. Auth function should either:
  • Refresh token WITH new scopes, OR
  • Start new OAuth PKCE flow for user consent
  • Auth client provider enables redirect and shows to user for consent
  1. Client gets new token with elevated permissions
  2. Request succeeds

Error Messages/Logs

below are logs (by having 403 from mcp server)

### refresh token as null, to trigger auth flow 

2026-02-24T17:06:42.643Z [DEBUG] MCP server "dev-mcp-server-1": Found client info
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Returning tokens
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Token length: 90
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Has refresh token: false
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Expires in: 2835s
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Saving code verifier
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Authorization URL: https://masked-server/mcp/oauth2/authorize?response_typ
e=code&client_id=oacli_U2TpyjRx44Ascs&code_challenge=%5BREDACTED%5D&code_challenge_method=S256&redirect_uri=http%3A%2F%2Flocalhost%3A3118%2
Fcallback&state=%5BREDACTED%5D&scope=product:write&resource=https%3A%2F%2Fmasked_resource%2F
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Scopes in URL: product:write
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Captured scopes from authorization URL: product:write
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Redirection handling is disabled, skipping redirect
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": HTTP connection dropped after 764s uptime
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Connection error: Unauthorized
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Tool 'create_product' failed after 0s: Unauthorized
2026-02-24T17:06:42.644Z [DEBUG] MCP server "dev-mcp-server-1": Tool call returned 401 Unauthorized - token may have expired
2026-02-24T17:06:42.644Z [DEBUG] mcp__dev-mcp-server-1__create_product tool error (773ms): MCP server "dev-mcp-server-1" requires re-authorization (tok
en expired)

Steps to Reproduce

Have an mcp tool in mcp server to return
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="products:write",
resource_metadata="https://your-mcp-server.example.com/.well-known/oauth-protected-resource"
Content-Type: application/json

{
"error": "insufficient_scope",
"error_description": "Additional permissions required"
}

Required Header Components

  1. Status Code: 403 Forbidden (NOT 401)
  2. WWW-Authenticate Header with:
  • error="insufficient_scope" (REQUIRED - triggers step-up flow)
  • scope="products:write" (REQUIRED - tells client what scopes to request)
  • resource_metadata="..." (OPTIONAL - helps client discover auth server)

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.39

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

MCP Spec Compliance

According to the MCP Authorization Spec (https://modelcontextprotocol.io/specification/draft/basic/authorization#step-up-authorization-flow):

When a client makes a request with an access token with insufficient scope during runtime operations, the server SHOULD respond with: - HTTP 403 Forbidden status code - WWW-Authenticate header with error="insufficient_scope" - scope parameter specifying the minimum scopes needed
Clients SHOULD respond to these errors by requesting a new access token with an increased set of scopes (from user) via a step-up authorization flow.

Current implementation violates this by not actually requesting elevated scopes during the step-up flow.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗