Skip to content

Opt-in scope on OAuth token refresh via the SDK's scopeForRefresh (Entra ID AADSTS90009) #2805

Closed

Description

@cliffhall

Problem

Some authorization servers reject a refresh-token request that carries no scope. Microsoft Entra ID does when the client and the resource are the same app registration: it answers AADSTS90009 (upstream modelcontextprotocol/typescript-sdk#2718). The Inspector refreshes through the SDK's auth(), which by default sends no scope on a refresh. So against such a server, every token refresh fails and the user has to re-authorize from scratch.

What the SDK now offers

MCP TypeScript SDK 2.4.0 (adopted in #2784 / #2799, upstream typescript-sdk#3085) adds an opt-in:

  • OAuthClientProvider gains an optional scopeForRefresh(tokens). When it is implemented, auth() (and so the transports and withOAuth) sends the string it returns as scope on the refresh request.
  • refreshAuthorization() gains an optional scope.
  • When a scope is sent and the token response has none, the sent scope is kept on the returned tokens.

Without either option, the refresh request and the saved tokens are unchanged.

What to do

Implement scopeForRefresh on BaseOAuthClientProvider (core/auth/providers.ts), opt-in per server, so the default refresh request stays exactly as it is today:

  • Add a per-server setting (for example "Send scope on token refresh"), alongside the existing OAuth settings (oauthScopes, oauthOnInsufficientScope) in the server settings form and in the config file, and pass it through to the provider.
  • When the setting is on, decide which scope to return and record why: the granted scope stored with the tokens, the configured scope (configuredScope / grantedScope in core/auth/types.ts), or the scope of the original authorization request. Keep this consistent with the rule in scopeForDeclinedRefreshGrant and with the step-up scope handling in core/auth/mcpAuth.ts.
  • Check the EMA wrapper (core/auth/ema/transportProvider.ts, which implements OAuthClientProvider by delegating to the base provider) forwards the new method.
  • When a refresh fails with an error naming a missing scope, consider pointing the user at the setting.

Out of scope

Changing the default refresh request for everyone. The SDK keeps its default unchanged on purpose, because sending scope on refresh can narrow or alter the grant on other authorization servers.

Activity

  1. added this to the v2.14.0 milestone on Oct 25, 2026
  2. cliffhall commented on Oct 25, 2026

    @cliffhall
    MemberAuthor

    Triage: Priority Low (total 5)

    • Severity 3: token refresh fails outright against an affected Entra ID setup; the workaround (re-authorize) is annoying
    • Urgency 2: no reports from Inspector users yet; wanted eventually
    • Bonuses: none (milestone set at create time, which this rubric does not count)

    Milestone v2.14.0: v2.13.0 is due 2026-10-28.

  3. modified the milestones: v2.14.0, v2.13.0 on Oct 25, 2026
  4. self-assigned this
    on Oct 25, 2026
  5. cliffhall commented on Oct 25, 2026

    @cliffhall
    MemberAuthor

    Shipped in #2809.

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature requestv2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions