[Regression in 1.121.0] MCP auth broken: microsoft-authentication extension (MSAL+WAM broker) ships device-bound (cnf) tokens as plain Bearer -> 401
@TylerLeonhardt is already working on this.
Since Jun 3, 2026.
Assessment
This issue has not been assessed yet.
Description
Scenario
You install VS Code on a corp Windows machine (Entra-joined to your tenant), open a folder, and configure an MCP server backed by Microsoft Entra OAuth (for example, the Microsoft MCP Server for Enterprise at https://mcp.svc.cloud.microsoft/enterprise). Starting the server prompts you to sign in with the work account your machine is joined to. The connection fails. Depending on whether your tenant enforces Conditional Access Token Protection, you hit one of two failure modes:
- No Token Protection (most customer tenants): sign-in succeeds, every MCP request returns 401, the connection dies in a retry loop.
- Token Protection enforced (Microsoft tenant and other dogfood tenants): sign-in itself is rejected by Entra with
AADSTS530084and a "Sorry, a security policy is preventing access" dialog.
Both failures share the same root cause. Token Protection moves the failure from the MCP server back to Entra, and kills the client-side workaround.
Steps to reproduce
Setup:
- Windows 10 or 11 with a TPM.
- Device Entra-joined (or Hybrid-joined / Intune-enrolled) to a corp tenant where you are a member, not a guest.
- VS Code 1.121.0 or later, stock install.
microsoft-authenticationextension at defaults (broker on). - Any Entra-protected MCP server. The Microsoft MCP Server for Enterprise reproduces it.
Steps:
- Add the server to
mcp.json:{ "servers": { "mcp-enterprise": { "type": "http", "url": "https://mcp.svc.cloud.microsoft/enterprise" } } } - Start the server from the MCP view.
- At the account prompt, pick the corp account whose tenant matches the tenant your device is joined to.
- Watch the MCP output channel.
Expected: server connects and tools are listed.
Actual on tenants without Token Protection:
Received 401 status with Authorization header, retrying with new auth registration
Error getting token from server metadata: Error: Cannot force new registration for a non-dynamic authentication provider.
Connection state: Error 401 status sending message to https://mcp.svc.cloud.microsoft/enterprise
Actual on tenants with Token Protection (Microsoft tenant): the sign-in window shows "Sorry, a security policy is preventing access" with Error Code: 530084 and App name: Visual Studio Code / App id: aebc6443-996d-45c2-90f0-388ff96faa56. No AT is issued, the MCP server is never called.
Confirming the cause locally
- Broker proof / workaround on non-Token-Protection tenants: set
"microsoft-authentication.implementation": "msal-no-broker"insettings.json, runDeveloper: Reload Window, repeat steps 2–4. Server connects. - Token inspection: proxy the request (Fiddler / mitmproxy), grab the
Authorizationheader, decode the JWT at https://jwt.ms. The claim list containscnf. - Regression bracket: install VS Code 1.120.0 from https://code.visualstudio.com/updates, repeat steps 2–4. Server connects, no
cnfon the AT.
Root cause
VS Code's built-in microsoft-authentication extension uses MSAL Node with the WAM broker on by default on Windows. On an Entra-joined or Intune-managed Windows device, the broker holds a transport-key-bound PRT for the target tenant. The broker hands that PRT to Entra, and Entra returns a sender-constrained access token stamped with a cnf claim. VS Code then sends the AT to the MCP server as plain Authorization: Bearer per the MCP spec, which defines no PoP / SHR profile. The MCP server sees cnf, takes the PoP validation path, finds no proof of possession on the request, and returns 401.
Tenants that enforce Conditional Access Token Protection (https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-token-protection) hit the same flaw earlier. Token Protection requires a bound sign-in token; if VS Code asks for an unbound AT for an in-scope resource, Entra returns AADSTS530084 and issues no token. The screenshots from jpaiva@microsoft.com and kudagdel@microsoft.com (both on Compliant Windows 10 devices) are this case.
The msal-no-broker workaround flips the failure mode on Token Protection tenants from "bound AT rejected by MCP" to "unbound sign-in rejected by Entra". That's why Microsoft-tenant users have no working client-side workaround today.
Regression in VS Code 1.121.0
90 days of MCP traffic across all VS Code builds, token-bearing requests only (tokenFormat == 'AT', discovery 401s excluded):
| VS Code build | First seen | Total | cnf=true | cnf=false |
|---|---|---|---|---|
| 1.107.0 → 1.120.0 (11 builds) | back to 2026-05-13 | thousands | 0 | all |
| 1.121.0 | 2026-05-19 | 2,505 | 1,503 (60%) | 1,002 |
| 1.122.0 | 2026-05-27 | 548 | 352 | 196 |
| 1.122.1 | 2026-05-29 | 1,409 | 524 | 885 |
| 1.123.0 | 2026-06-02 | 278 | 216 | 62 |
Eleven prior releases emitted zero cnf-bearing tokens. The first 1.121.0 request in telemetry carries cnf, and 60% of 1.121.0 traffic does. Bisect changes to extensions/microsoft-authentication/ and any MSAL Node version bump between 1.120.0 and 1.121.0.
The other 40% of 1.121.0 traffic succeeds because the broker can't bind those ATs: macOS / Linux, non-Entra-joined device, no TPM, guest in the tenant the user signs into, or msal-no-broker set on a tenant without Token Protection.
Evidence: the discriminator is per-(user × device × tenant)
The bijection holds across 47 tenants and 100 distinct users on VS Code 1.122.1: cnf present ↔ 401, zero counterexamples.
Tenant alone doesn't partition outcomes:
- 9 tenants emit both shapes.
- 14 succeed for every request.
- 22 fail for every request.
Microsoft tenant on VS Code 1.122.1: 24 users fail for every request, 6 users succeed for every request, 1 user (oid c3b93d1b-…) flipped both ways across launches in the same week (2 cnf-true/401 and 5 cnf-false/200 between 2026-06-01 and 2026-06-03). A second tenant: one user on VS Code 1.122.0 produced 28 cnf-true/401 and 9 cnf-false/200 inside a single 3-hour session. Both shapes for one OID inside one window. Tenant policy and VS Code build are constants; the broker's per-launch state on that device is the only variable left.
Same user, same tenant, same week: GitHub Copilot CLI (MSAL Node, no broker) succeeds with cnf-free ATs while VS Code (MSAL + broker) fails with cnf-bearing ATs.
Per-tenant variation is a sampling artifact
The WAM broker holds one PRT per (account × tenant) on the device. Binding shape is fixed at PRT issuance time:
- PRT bound: device is Entra-joined or Intune-enrolled to that tenant, or the user added the account on a TPM-equipped Windows box via Settings → Access work or school.
- PRT unbound: user is a guest in that tenant (B2B), added the account via browser-only flow, has no TPM, or signed in from macOS / Linux.
A tenant with two users on corp Entra-joined Windows looks like "always fails". A tenant with two guest users on Macs looks like "always succeeds". The Microsoft tenant is large enough that both populations show up. The impacted set across the fleet is Entra-joined Windows users signing into MCP against the tenant their device is joined to. That covers most corp users.
Fix direction
The MCP spec today defines Authorization: Bearer only. No PoP / SHR profile, no way for a server to advertise sender-constrained support. Until the spec grows one, VS Code must not hand MCP servers a sender-constrained access token, because no MCP server can validate one.
Concrete change inside extensions/microsoft-authentication/ for MCP resource scopes:
- Don't request bound tokens. Pass the MSAL Node flag that suppresses transport-key binding, or disable the WAM broker for these scopes, so the AT comes back without
cnf. - Defense in depth: if a
cnf-bearing AT does come back, don't send it asBearer. Fail fast with an actionable error in the MCP output channel instead of looping on 401.
Bisect whatever change in 1.121.0 turned binding on for MCP resources and gate it. Eleven prior releases shipped without it; restoring that behavior unblocks every tenant in the bottom three rows of the table above.
This does not unblock tenants that enforce Conditional Access Token Protection on the MCP resource. Token Protection requires a bound sign-in token; if VS Code asks for an unbound AT for an in-scope resource, Entra returns AADSTS530084 and VS Code never calls the MCP server. Two ways to clear that: the tenant admin excludes the MCP app from the Token Protection policy, or the MCP spec adds a PoP / SHR profile and the server implements it. Both sit outside this issue. This issue is the bug below them: VS Code requesting bound tokens for a Bearer-only resource.
Secondary issue: the recovery loop. _fetchWithAuthRetry retries every 401 with forceNewRegistration: true without checking the failure mode. mainThreadMcp rejects that for the built-in (non-dynamic) Entra provider with Cannot force new registration for a non-dynamic authentication provider. VS Code logs the error as a warning, the connection dies, and the user sees no actionable signal.
Workaround
Works only on tenants without Conditional Access Token Protection. In VS Code settings.json:
"microsoft-authentication.implementation": "msal-no-broker"
Then Developer: Reload Window. Broker off, MSAL takes a non-binding flow, the next AT has no cnf, MCP works. You lose native SSO / WAM UX.
On Token-Protection tenants this setting makes it worse: sign-in fails with AADSTS530084 ("An organization security policy requiring token protection is preventing this application from accessing the resource"). Those users have no client-side workaround until VS Code stops requesting bound tokens for MCP resources.
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.9k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/vscode
-
testplan-item
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
new release
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
testplan-item
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
testplan-item
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
All issues in microsoft/vscode
Similar issues
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster no-stale P3
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 76/100
-
code-quality refactoring
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
github/gh-aw-firewall#8816 ·
-
integration:quickjs org:external priority:backlog topic:code-interpreter topic:middleware type:feature
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
langchain-ai/deepagents#6450 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
vercel/react-tweet#225 ·