feat: macOS brokered authentication support#453
Merged
Conversation
* feat: add macOS brokered authentication support 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> * feat: add Company Portal version check before broker auth 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> * feat: make broker opt-in on macOS, error on missing CP 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> * improve: CP diagnostics logging and error messages - 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> * fix: dispatch broker interactive calls to main thread on macOS 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> * feat: add SSO Extension registration pre-flight check 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> * chore: move macOS test scripts to bin/mac/ 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> * improve: configurable verbosity in test script, disable SSO check - 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> * docs: add usage examples to test script header Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: broker falls through to next auth flow when unavailable on macOS 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> * test: comment out web mode tests, add broker re-prompt after clear 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> * fix: address PR review comments - 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> * docs: update usage.md to reflect broker opt-in on macOS - 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> * Remove unused SSO Extension check code 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> * Fix async deadlock in PersistDefaultAccount 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> * Remove DefaultAccountStore — rely on MSAL cache instead 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> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n Windows tests The AuthFlowFactory now checks IsMacOSBrokerAvailable() and IsMacOS() in the broker code path. Windows tests with strict mocks that set IsWindows10Or11(false) need these additional setups since the || and else-if branches evaluate the macOS checks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
siddharthpaudwal
previously approved these changes
Apr 23, 2026
kyle-rader-msft
requested changes
Apr 23, 2026
ChristopherJMiller
previously approved these changes
Apr 23, 2026
- AuthFlowFactory: collapse nested broker if/else into single condition (IsWindows10Or11 || IsMacOSBrokerAvailable) with one flows.Add call - Broker.cs: remove FallbackToBrowserAuthAsync — browser fallback is already handled by AuthFlowExecutor iterating to the next flow (Web) - Broker.cs: remove broad catch(Exception) for macOS — let MSAL exceptions propagate to AuthFlowBase which handles them properly - Update AuthFlowFactoryTest to match simplified factory logic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3e79984
kyle-rader-msft
approved these changes
Apr 23, 2026
kyle-rader-msft
previously requested changes
Apr 23, 2026
Collaborator
kyle-rader-msft
left a comment
There was a problem hiding this comment.
Also please update the CHANGELOG.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d74a4c9 to
b458e26
Compare
siddharthpaudwal
approved these changes
Apr 24, 2026
I have addressed Kyle's feedback and concerns, he will be out for a few hours, so we will get this in and if there are any other concerns I will open follow-up items and tackle them immediately.
ChristopherJMiller
approved these changes
Apr 24, 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.
Add support for macOS Enterprise SSO Extension brokered authentication, bringing feature parity with Windows WAM broker support.
Key changes:
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.
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).
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.
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:
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.
docs: add usage examples to test script header
fix: broker falls through to next auth flow when unavailable on macOS
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.
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.
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.
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.
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: