Skip to content

[BUG] Microsoft Entra MFA fails with 0xwindow_handle_required — WAM needs a parent window handle #425

Closed

Description

@erikdarlingdata

Component

App (Avalonia) + Core

Describe the Bug

Microsoft Entra MFA authentication cannot work in Performance Studio on Windows. Reported against Performance Monitor Lite by @joshdbe as erikdarlingdata/PerformanceMonitor#2184, who confirmed the same failure in Studio 1.19.1 while 1.4.3 works — that version boundary is the diagnostic key.

The error:

Failed to authenticate the user in Active Directory (Authentication=ActiveDirectoryInteractive).
Error code 0xwindow_handle_required
Failed to acquire access token for ActiveDirectoryInteractive: A window handle must be configured.
See https://aka.ms/msal-net-wam#parent-window-handles

Cause. Current Microsoft.Data.SqlClient (this repo pins 7.0.2 in src/PlanViewer.Core/PlanViewer.Core.csproj) uses the Windows WAM broker for Authentication=ActiveDirectoryInteractive, and WAM requires the calling application to supply a parent window handle so it can own and position the account picker. An app that never supplies one gets this error instead of a prompt.

ServerConnection.BuildConnectionString sets SqlAuthenticationMethod.ActiveDirectoryInteractive (src/PlanViewer.Core/Models/ServerConnection.cs:88), but nothing anywhere in the repo registers an auth provider — grep finds zero references to ActiveDirectoryAuthenticationProvider, SqlAuthenticationProvider or SetParentActivityOrWindowFunc. So this is not environment-specific: Entra MFA is broken for every Windows user on a current build.

1.4.3 works because it predates the SqlClient version that made WAM the default; interactive auth fell back to a system/embedded browser, which needs no handle.

Expected Behavior

Selecting Microsoft Entra MFA prompts and connects.

Additional Context

This is shaped differently here than in the Lite fix, and the differences are the whole design question:

  1. Avalonia, not WPF. No WindowInteropHelper. The handle comes from TopLevel.TryGetPlatformHandle()?.Handle, and it must be re-resolved per call rather than captured once (it isn't valid until the window is sourced).
  2. Windows-only. WAM doesn't exist on macOS or Linux, where interactive auth uses a browser and needs no handle. Registering a handle-supplying provider unconditionally would be wrong on the platforms Avalonia exists to serve, so the registration has to be OS-gated.
  3. The CLI has no window at all. PlanViewer.Cli resolves "entra" => AuthenticationTypes.EntraMFA (CliConnectionResolver.cs:32) and there is no top-level window to hand MSAL. That needs a deliberate answer — most likely device-code flow, or documenting Entra-interactive as unsupported from the CLI and pointing at a service principal. Filing that as the open question rather than guessing.
  4. The SSMS extension hosts inside SSMS's own window and could supply that handle, but it's a separate wiring point.

So the shape is: a Core seam that takes a handle provider, each host registering its own, OS-gated, with the CLI answered explicitly rather than by omission.

Cross-repo: erikdarlingdata/PerformanceMonitor#2184 tracks the same defect in Lite. Reporter has offered to test a build against a real Entra-MFA-on-Azure-VM environment, which is the case that cannot be reproduced synthetically — "the dialog appears on my machine" is not "it authenticates against a real tenant."

Activity

  1. erikdarlingdata commented on Aug 11, 2026

    @erikdarlingdata
    OwnerAuthor

    PR #426 is up for this.

    Also worth noting for anyone running the suite locally while working on it: PlanViewer.Core.Tests can wedge in a CoreCLR GC-suspension livelock on macOS ARM64 and spin a full core indefinitely — it bit twice today. #427 adds an out-of-process watchdog so that self-terminates. Nothing to do with this change, but it's the reason the local full-suite run isn't the regression gate here; CI is.

  2. erikdarlingdata commented on Aug 12, 2026

    @erikdarlingdata
    OwnerAuthor

    @joshdbe ready for you.

    Build: https://github.com/erikdarlingdata/PerformanceStudio/releases/tag/pr426-entra-test — grab the win-x64 zip, unzip anywhere, run PlanViewer.App.exe. No install; it runs from wherever you unzip it. SHA256 is in the attached SHA256SUMS.txt. SmartScreen may fuss about an unsigned exe — More info, then Run anyway.

    The test: add your server exactly the way that failed before — Microsoft Entra MFA, your username — and connect.

    What should happen now: the Windows account picker appears in front of Studio, MFA completes, and the connection opens.

    What I'd like back, either way:

    1. Did the account picker appear at all?
    2. Did it show up in front of the window you were using, or behind it?
    3. Did the connection open after auth?
    4. If anything failed: the exact error text.

    If it authenticates, I merge and it ships in the next release, with the Lite fix following on the same seam. If it doesn't, the exact error text tells me which assumption was wrong — and that's worth more to me than all the green checks on the PR.

  3. joshdbe commented on Aug 12, 2026

    @joshdbe

    Good morning Erik.
    Opened the app. Clicked Connect. All boxes was prefilled already and all the servers that I previously connected with old version showed up in Servers drop down. Clicked Test Connection and it was successful.
    It did not open window account picker for me to approve, just showed that it was successful. Clicked Connect and it connected.
    Clicked on Query Store > It showed up. Opened new query. Entered simple sql > Actual Plan > Showed up.
    Tried with multiple servers, same results.
    So far no errors, everything looks good.

    Opened old one and it did open auth browser window when clicked Test Connection. Not sure if its matter.
    Thank you.
    Josh

  4. added a commit that references this issue on Aug 12, 2026
  5. erikdarlingdata commented on Aug 12, 2026

    @erikdarlingdata
    OwnerAuthor

    Morning Josh — that's a pass, and thank you for the multi-server sweep.

    The part that looked odd is the two auth stacks behaving exactly as designed:

    • The new build goes through the Windows broker (WAM). Your work account is already signed into Windows, so the broker satisfied Entra MFA silently from that session — no picker needed. WAM only paints UI when it genuinely needs input (no cached account, or more than one to choose between). Silent success is its best case — and on 1.19.1 that same click died with 0xwindow_handle_required before the broker could do anything at all.
    • 1.4.3 predates WAM being SqlClient's default, so it bounces through a browser window instead. Same identity, older plumbing. Nothing wrong on your end in either case.

    So: same machine, same servers, and the exact failure from your screenshot is gone against a real tenant. That's the verification I couldn't manufacture locally — genuinely useful, twice now, counting the version boundary that located the bug in the first place.

    Merged in #426; it ships in the next release. The test build did its job, so I'm retiring that download — keep running your copy as long as you like, but the next stable release is the one to move to. Lite gets the same fix ported next (erikdarlingdata/PerformanceMonitor#2184) and I'll post there when it's in a build.

  6. joshdbe commented on Aug 12, 2026

    @joshdbe

    Great, thank you. Please let me know when monitor is done and I will test it

  7. added a commit that references this issue on Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions