feat: macOS brokered authentication support#452
Merged
dggsax merged 16 commits intoAzureAD:user/danigon/yeehawfrom Apr 23, 2026
Merged
feat: macOS brokered authentication support#452dggsax merged 16 commits intoAzureAD:user/danigon/yeehawfrom
dggsax merged 16 commits intoAzureAD:user/danigon/yeehawfrom
Conversation
Add support for macOS Enterprise SSO Extension brokered authentication, bringing feature parity with Windows WAM broker support. Key changes: - Upgrade MSAL 4.65.0 → 4.83.1, add NativeInterop v0.20.3 - Extend Broker auth flow with macOS-specific PCA config, account resolution, and browser fallback - Add DefaultAccountStore to persist account username for silent auth (OperatingSystemAccount not supported on macOS) - Add MacMainThreadScheduler message loop in Program.cs - Extend AuthMode enum and CLI parsing to support broker on macOS - Update docs with macOS broker prerequisites and redirect URI config Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Skip macOS broker auth if Company Portal is not installed or is below version 2603 (which added redirect_uri validation fix for unsigned apps). Falls back to web auth transparently in those cases. - Add IsMacOSBrokerAvailable() to IPlatformUtils/PlatformUtils - AuthFlowFactory uses IsMacOSBrokerAvailable() as gatekeeper - Broker.cs still uses IsMacOS() for runtime behavior (fallback, persist) - Reads CP version from Info.plist via 'defaults read' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On macOS, broker is no longer included in default auth modes. Users must explicitly pass '--mode broker' to use it. If broker is requested but Company Portal >= 5.2603.0 is not installed, AuthFlowFactory throws a clear InvalidOperationException instead of silently falling through to web auth (which hangs for apps with broker-required CA policies like token protection). - AuthMode.Default on non-Windows: Broker|Web -> Web - AuthFlowFactory: explicit error when broker requested but CP unavailable - New test: BrokerRequested_Mac_CP_Unavailable_Throws Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Include CP path in broker unavailable error message - Add trace-level logging: CP path, raw version output, stderr, parsed version parts (major/release/build) - Expose CompanyPortalAppPath as public const for error messages - Update test script to reflect broker-opt-in behavior: new tests for broker+web combined, trace diagnostics, reordered Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The macOS broker requires AcquireTokenInteractive to run on the main thread. Program.cs starts the MacMainThreadScheduler message loop on main and dispatches CLI work to Task.Run, but the broker interactive calls were still executing on the background thread. Now GetTokenInteractive/WithClaims dispatch through MacMainThreadScheduler.RunOnMainThreadAsync when running on macOS with the scheduler active (IsRunning check prevents deadlock in tests). Also: improved CP diagnostics trace logging and included CP path in the broker unavailable error message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Check 'app-sso -l' for registered Enterprise SSO Extensions before attempting broker auth. If no extensions are registered (MDM profile not applied), gives a clear error instead of a cryptic broker failure. Designed for easy revert: - Set AZUREAUTH_SKIP_SSO_CHECK=1 to bypass the check entirely - If app-sso fails or isn't available, assumes broker may work (non-fatal) - The check is a single method call in CheckMacOSBrokerAvailable() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move test-macos-broker.sh and swap-cp.sh from repo root to bin/mac/ alongside existing macOS build scripts. Update REPO_ROOT to resolve two levels up from the new location. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add AZUREAUTH_TEST_VERBOSITY env var (default: debug) to control log level across all tests (Test 3 always uses trace) - Comment out SSO Extension check (confirmed unnecessary for broker) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the existing Windows pattern where broker is silently skipped on unsupported platforms (e.g., Windows Server). On macOS, if broker is requested but Company Portal is insufficient, log a warning and continue to the next flow (Web, DeviceCode, etc.) instead of throwing. This means '--mode broker --mode web' will fall through to web when CP is unavailable, and '--mode broker' alone will try CachedAuth only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Web auth hangs for this broker-required app (token protection CA policy), so comment out Tests 2 and 5. Add cache clear + broker interactive re-prompt cycle (Tests 6-8) to verify full broker lifecycle: authenticate → clear → re-authenticate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ChristopherJMiller
requested changes
Apr 9, 2026
- Delete swap-cp.sh (reviewer: no need for checked-in CP swap utility) - Remove commented-out web mode tests from test script (reviewer: keep checked-in tests clean) - Add [Platform] attributes to non-Windows AuthModeExtensions tests (reviewer: make OS-specificity explicit) - Fix help text: non-Windows default is 'web' not 'broker, then web' (reviewer: broker may confuse Linux users; --mode is repeatable so --mode broker explicitly opts in) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Broker is opt-in via --mode broker, not automatic - Removed SSO Extension prerequisite (confirmed unnecessary) - Added CP version requirement (>= 5.2603.0) - Added example CLI invocations - Clarified fallthrough behavior when broker unavailable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The IsSSOExtensionRegistered method and its TODO were confirmed unnecessary during testing — broker auth works without SSO Extension registration showing in app-sso output. Removes dead code and the AZUREAUTH_SKIP_SSO_CHECK env var that was only needed for this check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Changed PersistDefaultAccount from sync .Wait()/.Result to async/await. The previous pattern (calling task.Wait() on an async method inside an async context) is a classic .NET deadlock risk, especially dangerous with the macOS MacMainThreadScheduler SynchronizationContext. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DefaultAccountStore persisted account usernames (PII) to plaintext JSON files in ~/.azureauth/ with world-readable permissions. This was only needed to disambiguate multi-account scenarios (TryToGetCachedAccountAsync returns null when >1 accounts are cached). Without it, multi-account users get an extra broker prompt — which is the safe default for a CLI tool and avoids writing PII to disk. Changes: - Delete DefaultAccountStore.cs and DefaultAccountStoreTest.cs - Simplify Broker.ResolveAccountAsync (no more persisted username lookup) - Remove PersistDefaultAccountAsync from Broker.cs - Simplify BrokerMacOSTest.cs (remove store setup/teardown) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Author
|
Re: this comment — good callout. Linux is safe here: The help text already shows |
siddharthpaudwal
approved these changes
Apr 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds macOS brokered authentication support via MSAL.NET, NativeInterop, and the macOS Enterprise SSO Extension (deployed through Company Portal).
What this does
--mode broker. Supports interactive broker (first run) and silent broker (subsequent runs via MSAL cache).MacMainThreadScheduler, as required by MSAL on macOS.--mode broker --mode webare specified, falls back to browser auth if broker fails.Why broker is opt-in on macOS (not the default)
On Windows, the default auth mode is
Broker | Web— broker is tried first, then web as fallback. On macOS, the default remainsWebonly, with broker opt-in via--mode broker.This is intentional because apps configured with broker-required Conditional Access policies (e.g., token protection / compliant device, error 530084) will hang indefinitely if web auth is attempted: the browser shows an error page about needing a compliant device but never redirects back to
localhost, so MSAL's HTTP listener waits forever. Making broker the default would mean that if Company Portal is unavailable and the flow falls through to web, the CLI hangs with no way to recover — a worse experience than a clear error.Since
--modeis repeatable (--mode broker --mode webtries both in order), users and tooling can explicitly opt into the desired combination. This matches the existing pattern where--mode brokeron Windows Server silently skips broker (no WAM) and falls through.Key design decisions
--mode broker) — see above for rationale.OperatingSystemAccounton macOS — this Windows-only concept does not work on macOS. On macOS,TryToGetCachedAccountAsyncresolves accounts from the MSAL Keychain cache for silent auth. If multiple accounts are cached (ambiguous), interactive auth is triggered — the safe default for a CLI tool.app-sso -lreturned empty on test devices yet broker worked fine. The check and associated code were removed entirely as unnecessary.Testing
BrokerMacOSTest(5 tests)bin/mac/test-macos-broker.sh(6 tests)Files changed
New files:
src/MSALWrapper.Test/AuthFlow/BrokerMacOSTest.cs— 5 macOS broker unit testsbin/mac/test-macos-broker.sh— functional test scriptModified files:
src/MSALWrapper/MSALWrapper.csproj— NuGet upgradessrc/MSALWrapper/AuthFlow/Broker.cs— major rewrite for cross-platformsrc/MSALWrapper/AuthFlow/AuthFlowFactory.cs— broker availability gating with fallthroughsrc/MSALWrapper/PlatformUtils.cs— CP version detection, macOS checkssrc/MSALWrapper/AuthMode.cs— broker opt-in on non-Windows (Default = Web)src/MSALWrapper/Constants.cs— macOS broker redirect URIsrc/AzureAuth/Program.cs— main thread scheduler patternsrc/AzureAuth/AuthModeExtensions.cs— preserve broker for silent auth in non-interactive modesrc/AzureAuth/Commands/CommandAad.cs—--mode brokeroption, fixed help text ([default: web]on non-Windows)docs/usage.md— macOS broker prerequisites and redirect URI configREADME.md— feature matrix updatedPrerequisites for broker auth
msauth.com.msauth.unsignedapp://auth