Skip to content

Enable WAM Broker support for Entra ID Auth modes#4288

Merged
cheenamalhotra merged 46 commits into
mainfrom
dev/cheena/entra-wam-broker
Jun 19, 2026
Merged

Enable WAM Broker support for Entra ID Auth modes#4288
cheenamalhotra merged 46 commits into
mainfrom
dev/cheena/entra-wam-broker

Conversation

@cheenamalhotra

@cheenamalhotra cheenamalhotra commented May 14, 2026

Copy link
Copy Markdown
Member

Enable WAM Broker support for Entra ID Auth modes

Summary

Enables Windows Account Manager (WAM) broker for interactive Entra ID auth in Microsoft.Data.SqlClient.Extensions.Azure (Integrated, Password [deprecated], Interactive, DeviceCodeFlow). WAM gives Windows users SSO, Windows Hello and conditional-access support.

Behavior

  • WAM is always on when the driver's built-in client id is used.
  • With a custom client id, WAM is opt-in via ActiveDirectoryAuthenticationProviderOptions.UseWamBroker (default off, backward compatible) or via the new useWamBroker attribute on the <SqlClientAuthenticationProviders> config section (parsed as bool; unset/invalid leaves the default untouched).
  • Non-Windows: broker is never used. SetParentActivityOrWindowFunc is now forwarded to MSAL on all platforms so Android/iOS/MAUI callers can supply a parent Activity / UIViewController.
  • Device Code Flow uses its own CancellationTokenSource (capped at the typical AAD user-code lifetime of 3 mins) instead of inheriting Connect Timeout, fixing the "The operation was canceled." failure when the user takes longer than 15s to enter the code.
  • Fed-auth retry hardening: on an Entra ID login-timeout retry, _fedAuthToken and _newDbConnectionPoolAuthenticationContext are now cleared along with _dbConnectionPoolAuthenticationContextKey; otherwise the second OnFedAuthInfo call trips Debug.Assert(_fedAuthToken == null). WAM Interactive surfaces this easily because the broker prompt routinely outlives Connect Timeout.

New Public APIs

In Microsoft.Data.SqlClient.Extensions.Azure:

// New: options-bag overload (the only way to opt into WAM with a custom client id)
public ActiveDirectoryAuthenticationProvider(ActiveDirectoryAuthenticationProviderOptions options);

// New: options bag (top-level type, sibling to ActiveDirectoryAuthenticationProvider)
public sealed class ActiveDirectoryAuthenticationProviderOptions
{
    public Func<DeviceCodeResult, Task>? DeviceCodeFlowCallback { get; set; }
    public string? ApplicationClientId { get; set; }
    public bool UseWamBroker { get; set; }
}

// New: cross-platform parent-window setter (replaces the Win32-typed helper on
// non-Windows; on Windows still accepts IntPtr/IWin32Window, on Android an
// Activity, on iOS a UIViewController).
public void SetParentActivityOrWindowFunc(Func<object>? parentActivityOrWindowFunc);

Existing constructors ((), (string applicationClientId), (Func<DeviceCodeResult, Task>, string? applicationClientId = null)) are unchanged and remain source- and binary-compatible.

Configuration

<configuration>
  <configSections>
    <section name="SqlClientAuthenticationProviders"
             type="Microsoft.Data.SqlClient.SqlClientAuthenticationProviderConfigurationSection, Microsoft.Data.SqlClient" />
  </configSections>
  <!-- both attributes are optional and independent -->
  <SqlClientAuthenticationProviders applicationClientId="<your_client_id>"
                                    useWamBroker="true" />
</configuration>

Usage

var provider = new ActiveDirectoryAuthenticationProvider(new ActiveDirectoryAuthenticationProviderOptions
{
    ApplicationClientId = "<application_client_id>",
    UseWamBroker = true,
});
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, provider);

Changes

  • WAM broker wiring + Windows parent-window handling (ActiveDirectoryAuthenticationProvider.Windows.cs, new Interop.GetAncestor / Interop.GetConsoleWindow). Updated WAM (Windows) and system-browser (Unix) redirect-URI handling.
  • New ActiveDirectoryAuthenticationProviderOptions type and matching constructor overload. All new knobs live on the options bag so future additions don't require new overloads.
  • SetParentActivityOrWindowFunc is now forwarded to MSAL on non-Windows targets (Android/iOS/MAUI) in addition to Windows.
  • useWamBroker config attribute on <SqlClientAuthenticationProviders> for declarative opt-in.
  • Hardened the reflection bootstrap in SqlAuthenticationProviderManager's static initializer:
    • When no applicationClientId and no useWamBroker are configured, calls the parameterless constructor; otherwise explicitly resolves the (ActiveDirectoryAuthenticationProviderOptions) constructor via Type.GetConstructor and reflectively sets only the properties the app actually configured. This avoids the AmbiguousMatchException that Activator.CreateInstance(type, new object?[] { null }) previously raised once the type had two 1-arg constructors ((string) and (ActiveDirectoryAuthenticationProviderOptions)) that both accept null.
    • Catch list expanded with AmbiguousMatchException and TypeInitializationException so a broken/older Azure extension never throws TypeInitializationException out of GetProvider and bricks every SqlClient connection (including non-AD ones).
  • SqlConnectionInternal.AttemptRetryADAuthWithTimeoutError now clears _fedAuthToken and _newDbConnectionPoolAuthenticationContext before retrying; dropped the unused connectionOptions parameter while in there.
  • AcquireTokenInteractiveDeviceFlowAsync allocates its own CancellationTokenSource for the DCF branch instead of inheriting the outer per-request CTS — fixes the Connect Timeout-cancels-DCF bug.
  • Bumped Microsoft.Identity.Client to 4.84.2 and added Microsoft.Identity.Client.Broker 4.84.2 on the Azure extension so MSAL can light up WAM.
  • Updated XML docs / snippets for WAM behavior and defaults.
  • New WamBrokerTests.cs (16 tests) covering built-in vs. custom client id, UseWamBroker true/false, options-bag precedence, and the new constructor path; plus a GetProvider_ForActiveDirectoryMethod_DoesNotThrow regression test in SqlAuthenticationProviderManagerTests pinning the static-initializer fix.
  • New WinForms sample doc/apps/AzureSqlConnector (multi-targets net481 and net10.0-windows) with a mode selector that launches either MainForm (OpenAsync() on the UI thread) or MainFormWorker (sync Open() on a worker, parent HWND captured up-front on the UI thread). The sample's DCF callback surfaces the user code three ways — log textbox via BeginInvoke, default browser via Process.Start, and a modal MessageBox on the MSAL callback thread — so the code is visible even when a sync Open() blocks the UI thread.

Testing

  • Unit tests added (WamBrokerTests.cs + SqlAuthenticationProviderManager bootstrap regression test)
  • Public API changes documented (doc XML + snippets)
  • No breaking changes — the new constructor overload preserves binary compatibility; existing ClearUserTokenCache() behavior is unchanged
  • Manually validated DCF end-to-end via the sample (code visible in log/browser/modal; MSAL waits for the user instead of cancelling at Connect Timeout)
  • Manually validated WAM Interactive sync (Open() on a worker) and async (OpenAsync() on the UI thread) via the sample's mode selector

Copilot AI review requested due to automatic review settings May 14, 2026 02:31
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board May 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds WAM broker support to the Azure extension authentication provider for Entra ID public-client authentication flows on Windows.

Changes:

  • Adds Microsoft.Identity.Client.Broker dependency and broker configuration during PCA creation.
  • Adds parent-window callback API and Windows interop helpers for WAM prompt parenting.
  • Adds basic tests and a draft feature spec for WAM broker support.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Directory.Packages.props Adds centralized MSAL broker package version.
src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj References the broker package from the Azure extension.
src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs Makes the provider partial, adds parent-window callback API, and configures WAM broker on Windows.
src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.Windows.cs Adds Windows-specific parent-window discovery for broker UI.
src/Microsoft.Data.SqlClient.Extensions/Azure/src/Interop/Interop.GetAncestor.cs Adds user32.GetAncestor interop helper.
src/Microsoft.Data.SqlClient.Extensions/Azure/src/Interop/Interop.GetConsoleWindow.cs Adds kernel32.GetConsoleWindow interop helper.
src/Microsoft.Data.SqlClient.Extensions/Azure/test/WamBrokerTests.cs Adds basic tests for the new parent-window setter.
specs/002-wam-broker/spec.md Adds the draft WAM broker feature specification.
Comments suppressed due to low confidence (5)

src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs:766

  • The parent-window delegate is captured on the cached PublicClientApplication, but the static PCA cache key does not include the new SetParentActivityOrWindow callback. A later provider instance with the same authority/client/redirect can reuse an app built with an earlier provider's delegate, causing WAM prompts to be parented to the wrong window (or to ignore the later callback). Include the callback identity in the cache key or avoid capturing provider instance state in the cached app.
                builder.WithParentActivityOrWindow(GetBrokerParentWindow);
            }
            #else
            builder.WithParentActivityOrWindow(GetBrokerParentWindow);

src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs:752

  • The tests added for this feature only exercise the setter and do not verify that Windows PCA construction actually enables the broker or wires the parent-window callback. Please add unit coverage around CreateClientAppInstance (or an observable wrapper) so regressions in the WAM configuration are caught without relying solely on manual integration testing.
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            builder.WithBroker(new BrokerOptions(BrokerOptions.OperatingSystems.Windows));

specs/002-wam-broker/spec.md:70

  • This API description says Func can return IWin32Window, but the implementation only accepts IntPtr from this callback and silently ignores any other object type before falling back to console detection. Please either document only the supported return type or handle IWin32Window here as described.
        // Cross-platform API to set the parent window/activity for WAM dialog
        // On Windows: accepts IntPtr (window handle) or IWin32Window via Func<object>
        // On Unix: no-op (WAM not available)
        public void SetParentActivityOrWindow(Func<object> parentActivityOrWindowFunc);
    

    specs/002-wam-broker/spec.md:131

    • This rollout note is inaccurate: SetIWin32WindowFunc is not changed to delegate to SetParentActivityOrWindow; it still stores a separate _iWin32WindowFunc and the new callback is independent. Please update the spec or implementation so consumers and maintainers do not rely on a delegation behavior that is not present.
    - WAM broker is **always enabled** on Windows when using PCA flows
    - No opt-in connection string keyword needed (aligns with MSAL PCA compliance requirements)
    - Existing `SetIWin32WindowFunc` remains as a backward-compatible API on .NET Framework, delegating to `SetParentActivityOrWindow`
    

    src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs:749

    • This comment overstates the behavior: the code still uses AcquireTokenByIntegratedWindowsAuth, AcquireTokenByUsernamePassword, and AcquireTokenWithDeviceCode for those modes, so broker configuration does not make every supported authentication mode a WAM flow. Please narrow the comment to the modes MSAL will actually broker or update the acquisition paths accordingly.
            // Enable WAM broker on Windows for all supported authentication modes.
            // The broker provides enhanced security by enabling device-based Conditional Access
            // policies through the Windows Account Manager (WAM).
    

Comment thread specs/002-wam-broker/spec.md Outdated
Copilot AI review requested due to automatic review settings June 6, 2026 03:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)

doc/apps/AzureSqlConnector/README.md:110

  • The last two dotnet run lines appear duplicated and are not formatted as part of the Notes list or a code block. This looks like leftover copy/paste noise and should be removed (or moved into the earlier Build & Run section as a single example).
  developer overrides).
- This is a sample / diagnostic tool, **not** a product. It does not persist credentials.
dotnet run --project .\doc\apps\AzureSqlConnector\AzureSqlConnector.csproj
dotnet run --project .\doc\apps\AzureSqlConnector\AzureSqlConnector.csproj

Comment thread doc/apps/AzureSqlConnector/README.md
Comment thread doc/apps/AzureSqlConnector/MainForm.cs
Comment thread doc/apps/AzureSqlConnector/MainForm.cs Outdated
Copilot AI review requested due to automatic review settings June 6, 2026 03:36
@cheenamalhotra cheenamalhotra added this to the 7.1.0-preview2 milestone Jun 6, 2026
@cheenamalhotra cheenamalhotra added the Hotfix 7.0.2 PRs targeting main that should be backported to release/7.0 for the 7.0.2 release. label Jun 6, 2026
@cheenamalhotra cheenamalhotra moved this from To triage to In progress in SqlClient Board Jun 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Language not supported

Comment thread doc/apps/AzureSqlConnector/README.md Outdated
Copilot AI review requested due to automatic review settings June 6, 2026 03:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 6 comments.

Files not reviewed (1)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Language not supported
Comments suppressed due to low confidence (2)

doc/apps/AzureSqlConnector/README.md:109

  • These two trailing dotnet run lines are duplicated and not formatted as a code block; the build/run instructions already appear earlier in the README.
dotnet run --project .\doc\apps\AzureSqlConnector\AzureSqlConnector.csproj

specs/002-wam-broker/spec.md:132

  • The spec claims SetIWin32WindowFunc delegates to SetParentActivityOrWindow, but the current code keeps SetIWin32WindowFunc as a separate setting. Either implement that delegation or update this statement to avoid promising behavior that doesn't exist.
- WAM broker is **always enabled** on Windows when using PCA flows
- No opt-in connection string keyword needed (aligns with MSAL PCA compliance requirements)
- Existing `SetIWin32WindowFunc` remains as a backward-compatible API on .NET Framework, delegating to `SetParentActivityOrWindow`

Comment thread doc/apps/AzureSqlConnector/MainForm.cs
Comment thread doc/apps/AzureSqlConnector/MainForm.cs Outdated
Comment thread specs/002-wam-broker/spec.md Outdated
Comment thread specs/002-wam-broker/spec.md Outdated
Copilot AI review requested due to automatic review settings June 6, 2026 03:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Language not supported

Comment thread specs/002-wam-broker/spec.md Outdated
Comment thread doc/apps/AzureSqlConnector/README.md Outdated
Comment thread doc/apps/AzureSqlConnector/MainForm.cs
Copilot AI review requested due to automatic review settings June 18, 2026 17:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 26 changed files in this pull request and generated 2 comments.

Files not reviewed (2)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Generated file
  • doc/apps/AzureSqlConnector/MainFormWorker.Designer.cs: Generated file

mdaigle
mdaigle previously approved these changes Jun 18, 2026
Copilot AI review requested due to automatic review settings June 18, 2026 19:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 31 changed files in this pull request and generated 5 comments.

Files not reviewed (3)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Generated file
  • doc/apps/AzureSqlConnector/MainFormWorker.Designer.cs: Generated file
  • src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs: Generated file

Comment thread src/Microsoft.Data.SqlClient/src/Resources/Strings.resx
mdaigle
mdaigle previously approved these changes Jun 18, 2026

@mdaigle mdaigle left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love what we have to do in the SqlAuthProviderManager, but don't see another easy option right now.

paulmedynski
paulmedynski previously approved these changes Jun 19, 2026

@paulmedynski paulmedynski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving on the condition that all of my open feedback is captured for a followup PR.

I'm a bit nervous about the new reflection in the Azure provider - will that affect AOT apps?

Comment thread src/Microsoft.Data.SqlClient/src/Resources/Strings.resx Outdated
mdaigle
mdaigle previously approved these changes Jun 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 33 changed files in this pull request and generated 2 comments.

Files not reviewed (3)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Generated file
  • doc/apps/AzureSqlConnector/MainFormWorker.Designer.cs: Generated file
  • src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs: Generated file

Comment thread src/Microsoft.Data.SqlClient/src/Resources/Strings.resx Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 33 changed files in this pull request and generated 1 comment.

Files not reviewed (3)
  • doc/apps/AzureSqlConnector/MainForm.Designer.cs: Generated file
  • doc/apps/AzureSqlConnector/MainFormWorker.Designer.cs: Generated file
  • src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs: Generated file

Comment thread src/Microsoft.Data.SqlClient/src/Resources/Strings.resx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hotfix 7.0.2 PRs targeting main that should be backported to release/7.0 for the 7.0.2 release. Public API 🆕 Issues/PRs that introduce new APIs to the driver.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

8 participants