Give the WAM broker a parent window handle so Entra MFA works (#2184) - #2217
Conversation
Current Microsoft.Data.SqlClient (7.0.2) routes ActiveDirectoryInteractive through the Windows WAM broker, which requires the application to supply the HWND that will own its account picker. Lite set the authentication mode but never registered an auth provider, so every Entra MFA connection failed with 0xwindow_handle_required instead of prompting. Register a process-wide provider at startup (covers the Add/Edit dialog's Test Connection and every collector loop), resolving the owning window per prompt - the active window first, so the picker lands in front of the dialog rather than behind it - marshaled through the dispatcher because MSAL calls from whatever thread the connection open runs on, and WPF throws on off-thread Window property reads. The blocking Invoke is safe: no UI-thread path blocks on a SQL connection open. Direct port of the Studio fix (PerformanceStudio#426), verified there against a real Entra-MFA tenant by this issue's reporter, including the silent-SSO case (broker satisfies MFA from the Windows session and no picker appears - that is WAM working). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| private static IntPtr ActiveWindowHandle() | ||
| { | ||
| try | ||
| { | ||
| var dispatcher = Current?.Dispatcher; | ||
| if (dispatcher is null) | ||
| return IntPtr.Zero; | ||
|
|
||
| return dispatcher.CheckAccess() | ||
| ? ActiveWindowHandleOnUIThread() | ||
| : dispatcher.Invoke(ActiveWindowHandleOnUIThread); | ||
| } | ||
| catch |
There was a problem hiding this comment.
ActiveWindowHandle() swallows every exception with no logging before falling back to IntPtr.Zero. Every other catch block in this file (webhook/SMTP credential lookups, DataRootMigration, the unhandled-exception handlers, etc.) logs via AppLogger before degrading — this is the one exception that degrades silently.
That matters more than usual here: IntPtr.Zero is exactly the input that reproduces the original symptom this PR fixes (0xwindow_handle_required). If ActiveWindowHandleOnUIThread() ever throws for a real reason (a future WPF/dispatcher change, app.Windows mutating mid-enumeration, etc.), the user is back to a bare MSAL failure with zero trace of why the handle resolution failed — the same silent-failure shape as #2184 itself, just one layer down.
Consider at least an AppLogger.Warn("App", ...) in the catch before returning IntPtr.Zero, so a future regression here is diagnosable instead of silent.
There was a problem hiding this comment.
Fixed in ec03916 — the catch now logs through AppLogger.Warn before degrading to Zero, guarded like the file's other late-lifecycle log calls since handle resolution can fire during shutdown after the logger is gone. The irony was the convincing part: a silent fallback here would be #2184's own failure shape one layer down.
|
Reviewed the diff (CHANGELOG.md, Parity check (Lite vs Darling): No drift. Confirmed Darling has no analog to flag — Deadlock claim, verified: The PR body asserts no UI-thread path in Lite blocks on a SQL connection open, which is what makes the blocking Auth wiring: One finding posted inline: the catch-all in No SQL changed in this PR, so the T-SQL style guide doesn't apply here. |
… silently Zero is exactly the input that reproduces 0xwindow_handle_required, so a throw inside ActiveWindowHandle must leave a trace in the log - a silent fallback would be the #2184 bug's own shape one layer down, as the review put it. Guarded like the file's other late-lifecycle log calls, because handle resolution can fire during shutdown after the logger is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Reviewed. This is a clean, well-scoped fix — no blocking issues found. What I checked closely:
No security concerns (no new input surface — this only threads a window handle through, not user data), no SQL touched, no correctness bugs found. |
What does this PR do?
Fixes #2184.
Microsoft Entra MFA could not work in Lite at all. Current
Microsoft.Data.SqlClient(7.0.2 here) routesAuthentication=ActiveDirectoryInteractivethrough the Windows WAM broker, and WAM requires the application to supply the HWND that will own its account picker.ServerConnection.ApplyAuthenticationset the authentication mode but nothing registered an auth provider, so users got0xwindow_handle_requiredinstead of a prompt. Not tenant-specific - broken for every user on a current build; old builds predate WAM being the default and fell back to a browser.This is the direct port of the Studio fix (PerformanceStudio#426), which @joshdbe - this issue's reporter - verified yesterday against his real Entra-MFA-on-Azure-VM environment. Same three decisions, one WPF-specific difference:
SqlAuthenticationProvider.SetProviderinstalls against the authentication method, so one call inOnStartupcovers the Add/Edit dialog's Test Connection and every collector loop without per-site wiring anyone could forget.IntPtr.Zero, which MSAL treats as no handle: auth fails with its normal message rather than the app crashing.Windowproperty reads where Avalonia merely races. The blockingDispatcher.Invokecannot deadlock: no UI-thread path in Lite blocks on a SQL connection open (all opens are async; the.Resultreads in ServerTab partials are post-WhenAllcompleted-task reads).No Lite analog of Studio's CLI refusal exists - Lite has no headless entry point that accepts Entra interactive - and no OS gate is needed on a
net10.0-windowsWPF app.How was this tested?
Lite.Testssuite: 2,197 passed, 0 failed locally on Windows, including two new tests pinning the wiring contracts (null provider rejected at wiring time; registration is idempotent so a later caller can't silently replace the provider, made order-deterministic via an internal test-only reset).WindowInteropHelper) is the documented pattern for exactly this registration.🤖 Generated with Claude Code