Skip to content

[Windows] WebAuthenticator: add OAuth support via app activation - #34887

Closed
mattleibow wants to merge 20 commits into
windows-app-activation-lifecyclefrom
windows-oauth-activation-event
Closed

[Windows] WebAuthenticator: add OAuth support via app activation#34887
mattleibow wants to merge 20 commits into
windows-app-activation-lifecyclefrom
windows-oauth-activation-event

Conversation

@mattleibow

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

This ports the Windows OAuth/WebAuthenticator work from #30056 onto the new Windows app-activation lifecycle hook from #34883, so protocol callbacks are handled through Essentials instead of special-casing MauiWinUIApplication startup.

  • wires Essentials into OnAppActivation(...) on Windows and forwards protocol activations to WebAuthenticator
  • completes OAuth callbacks with OAuth2Manager.CompleteAuthRequest(...) while preserving response decoding plus code/state values
  • maps cancellation-like auth failures to TaskCanceledException and keeps the unpackaged protocol-registration checks from the original community work
  • updates the existing Windows WebAuthenticator device-test path to use the supported flow

Issues Fixed

Morten Nielsen and others added 9 commits June 18, 2025 10:35
…s_oauth

# Conflicts:
#	src/Essentials/src/WebAuthenticator/WebAuthenticator.windows.cs
Keep the community OAuth commits intact, and wire the Windows callback
flow through the new app activation lifecycle event instead of
special-casing startup. This preserves code/state handling, response
decoding, and cancellation semantics while keeping MauiWinUIApplication
generic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mattleibow
mattleibow force-pushed the windows-oauth-activation-event branch from 45bf1aa to 9dcdd3c Compare April 8, 2026 20:19

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

Ports Windows WebAuthenticator/OAuth handling to the new Windows app-activation lifecycle hook so protocol callbacks are completed inside Essentials without app-specific startup plumbing.

Changes:

  • Implement Windows WebAuthenticator using WinAppSDK OAuth2Manager and handle protocol activations via Platform.OnAppActivation.
  • Wire Essentials into the Windows lifecycle OnAppActivation(...) hook so protocol callbacks can complete pending auth requests.
  • Update Essentials device test packaging (protocol registration) and adjust the sample server response to include code/state.

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
src/Essentials/src/WebAuthenticator/WebAuthenticator.windows.cs Adds Windows OAuth implementation and activation handling for completing auth requests.
src/Essentials/src/Platform/Platform.shared.cs Exposes a Windows Platform.OnAppActivation(...) entrypoint for activation handling in Essentials.
src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs Hooks the Windows lifecycle OnAppActivation event to the Essentials Platform handler.
src/Essentials/src/WebAuthenticator/WebAuthenticator.shared.cs Removes Windows “unsupported” annotations now that Windows implementation exists.
src/Essentials/test/DeviceTests/Tests/WebAuthenticator_Tests.cs Updates WebAuthenticator device tests to run on Windows (no longer expecting PlatformNotSupportedException).
src/Essentials/test/DeviceTests/Platforms/Windows/Package.appxmanifest Registers the xamarinessentials protocol for Windows device tests.
src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs Includes code and state values in callback payload to support code-based auth flows.

Comment on lines +62 to +66
var value = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(callbackUrl.Scheme);
if (value is null || value.GetValue("URL Protocol") is null)
{
throw new InvalidOperationException($"The URI Scheme '{callbackUrl.Scheme}' is not registered. Call ActivationRegistrationManager.RegisterForProtocolActivation to register protocol activation.");
}

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

Registry.ClassesRoot.OpenSubKey(...) returns a RegistryKey that should be disposed/closed. Consider using using var value = ... (or a try/finally) before reading URL Protocol, to avoid leaking registry handles in unpackaged scenarios.

Copilot uses AI. Check for mistakes.
var reader = doc.CreateReader();
var namespaceManager = new XmlNamespaceManager(reader.NameTable);
namespaceManager.AddNamespace("x", PlatformUtils.AppManifestXmlns);
namespaceManager.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10");

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

IsUriProtocolDeclared hard-codes the UAP XML namespace string even though PlatformUtils.AppManifestUapXmlns already defines it (and is used elsewhere, e.g., Permissions). Using the shared constant would reduce duplication and avoid drift if the namespace ever changes.

Suggested change
namespaceManager.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10");
namespaceManager.AddNamespace("uap", PlatformUtils.AppManifestUapXmlns);

Copilot uses AI. Check for mistakes.
{
var docPath = FileSystemUtils.PlatformGetFullAppPackageFilePath(PlatformUtils.AppManifestFilename);
var doc = XDocument.Load(docPath, LoadOptions.None);
var reader = doc.CreateReader();

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

doc.CreateReader() returns an XmlReader that implements IDisposable. Consider disposing it (e.g., using var reader = doc.CreateReader();) to avoid holding onto unmanaged resources longer than necessary when parsing the manifest.

Suggested change
var reader = doc.CreateReader();
using var reader = doc.CreateReader();

Copilot uses AI. Check for mistakes.
@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ⚠️ Tests need improvement

The lifecycle event plumbing is well-tested with a focused unit test, but the substantial new Windows WebAuthenticator implementation (including authentication logic, protocol handling, error detection, and manifest parsing) has no direct test coverage.

👍 / 👎 — Was this evaluation helpful? React to let us know!

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34887 — Windows WebAuthenticator OAuth callbacks via app activation
Test files evaluated: 2
Fix files: 9


Overall Verdict

⚠️ Tests need improvement

The new OnAppActivation lifecycle event is well-covered by a unit test. However, the most complex new code — the Windows WebAuthenticatorImplementation class (~115 lines of new implementation including OAuth flow, protocol handling, error detection, and manifest parsing) — has no direct tests at all.


1. Fix Coverage — ⚠️

The unit test CanAddWindowsOnAppActivationLifecycleEvent verifies that:

  • The new OnAppActivation delegate can be registered
  • Multiple handlers are all called on invocation
  • The wasHandled boolean correctly propagates true if any handler returns true

This covers the lifecycle infrastructure introduced in WindowsLifecycle.cs, WindowsLifecycleBuilderExtensions.cs, and the routing in MauiWinUIApplication.cs.

However, the core feature — the new WebAuthenticatorImplementation on Windows — has no direct tests. The only change to WebAuthenticator_Tests.cs removes the #if WINDOWS block that previously threw PlatformNotSupportedException. Since all tests in that file are tagged [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)], none of them run in CI — meaning Windows WebAuthenticator has zero automated test coverage.

Key untested code paths:

  • WebAuthenticatorImplementation.OnAppActivation(application, args) — the protocol activation handler used to complete OAuth callbacks
  • AuthenticateAsync — the main new Windows authentication flow using OAuth2Manager
  • IsUserCancellation(error, errorDescription) — pure logic, fully unit-testable
  • IsUriProtocolDeclared(scheme) — XML manifest parsing, partially testable

2. Edge Cases & Gaps — ⚠️

Covered:

  • Multiple OnAppActivation handlers all get called
  • wasHandled = true propagates when at least one handler returns true

Missing:

  • wasHandled = false when all handlers return false (the inverse case)
  • OnAppActivation returns false when args is null → defensively handled but not tested
  • OnAppActivation returns false for non-Protocol activations (args.Kind != ExtendedActivationKind.Protocol)
  • OnAppActivation returns false when OAuth2Manager.CompleteAuthRequest returns false
  • OnAppActivation's "no active window" branch: Process.GetCurrentProcess().Kill() is called — this is a significant/dangerous code path with no safety test
  • IsUserCancellation: trivial pure function with three conditions ("access_denied", contains "cancel" in error, contains "cancel" in description) — should have a unit test
  • Packaged app: IsUriProtocolDeclared validates manifest — no test for declared vs. undeclared protocol
  • Unpackaged app: registry check (Registry.ClassesRoot.OpenSubKey) — no test for missing vs. present key
  • No active window during AuthenticateAsync → throws InvalidOperationException
  • AuthRequestResult.Failure path (error, error with description, cancellation keyword detection)
  • callbackUrl using (redacted) or https://` scheme in unpackaged mode → throws

3. Test Type Appropriateness — ✅

  • Lifecycle unit test: ✅ Correct choice — event registration and invocation is pure framework logic, no platform context needed.
  • Device test modification: ✅ Reasonable — actual OAuth with Windows App SDK requires a real platform context. End-to-end OAuth cannot realistically be a pure unit test.
  • Missing unit tests: IsUserCancellation and the null/kind checks in OnAppActivation are pure logic that can and should be unit-tested without any platform context.

4. Convention Compliance — ✅

  • [Fact] attribute used correctly ✅
  • #if WINDOWS guard on the lifecycle test is appropriate (delegate is Windows-only) ✅
  • No anti-patterns detected ✅

5. Flakiness Risk — ⚠️ Medium

  • The device tests all use an external URL ((xamarinessentialsauthsample.azurewebsites.net/redacted) — this is a flakiness risk if the server becomes unavailable. However, since all these tests are [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]`, they don't run in CI, so this is a pre-existing concern rather than a new one introduced by this PR.
  • The new unit test is not flaky.

6. Duplicate Coverage — ✅ No duplicates

No existing tests were found covering the new Windows OnAppActivation lifecycle event or the Windows WebAuthenticator implementation. These are entirely new code paths.

7. Platform Scope — ⚠️

  • Fix files touch Windows-specific code (4 files) and cross-platform shared code (5 files including WebAuthenticator.shared.cs and Platform.shared.cs).
  • The unit test correctly runs only on Windows via #if WINDOWS.
  • Cross-platform changes (removing [UnsupportedOSPlatform("windows")], doc comment updates) don't need separate tests.
  • However, the important cross-platform concern is that the shared Platform.OnAppActivation dispatch chain has no test at any level: EssentialsMauiAppBuilderExtensionsPlatform.OnAppActivationWebAuthenticatorImplementation.OnAppActivation.

8. Assertion Quality — ✅ for existing test

The lifecycle unit test's assertions are specific and meaningful:

  • Assert.True(service.ContainsEvent(...)) — verifies registration
  • Assert.True(firstHandlerCalled) and Assert.True(secondHandlerCalled) — verifies both handlers are invoked
  • Assert.True(wasHandled) — verifies the bool return value propagates correctly

These would catch regressions in multi-handler invocation semantics.

9. Fix-Test Alignment — ⚠️

Fix Area Test Coverage
WindowsLifecycle.OnAppActivation delegate ✅ Unit test
WindowsLifecycleBuilderExtensions.OnAppActivation ✅ Unit test (implicitly)
MauiWinUIApplication.OnAppActivation / RegisterForAppActivation ✅ Unit test (event dispatch)
EssentialsMauiAppBuilderExtensions wiring ❌ None
Platform.OnAppActivation dispatch ❌ None
WebAuthenticatorImplementation.OnAppActivation ❌ None
WebAuthenticatorImplementation.AuthenticateAsync ❌ None (human-only device test)
IsUserCancellation ❌ None
IsUriProtocolDeclared ❌ None

Recommendations

  1. Add a unit test for IsUserCancellation — it's a pure function with clear logic that covers "access_denied", error string containing "cancel", and description containing "cancel". Easily added to an Essentials unit test project.

  2. Add unit tests for WebAuthenticatorImplementation.OnAppActivation — the null check, non-Protocol activation guard, and OAuth2Manager.CompleteAuthRequest returning false are all testable using mocks/stubs without a real Windows activation.

  3. Consider a unit test for the wasHandled = false case — the current unit test only verifies that wasHandled is true when a handler returns true. A complementary test verifying false propagation (all handlers return false) would complete the coverage.

  4. Address the "no active window → kill process" pathSystem.Diagnostics.Process.GetCurrentProcess().Kill() in OnAppActivation is a surprising side effect in a headless activation scenario. Consider documenting this behavior with a comment and, if possible, making the process exit injectable/mockable for testing.

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dc.services.visualstudio.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dc.services.visualstudio.com"

See Network Configuration for more information.

Note

🔒 Integrity filtering filtered 1 item

Integrity filtering activated and filtered the following item during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ⚠️ Tests need improvement

The lifecycle event ordering tests are solid additions, but the Windows WebAuthenticator implementation (the central feature of this PR) has no automated test coverage — all WebAuthenticator device tests require human interaction and cannot run in CI.

👍 / 👎 — Was this evaluation helpful? React to let us know!

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34887 — Windows WebAuthenticator + OnAppInstanceActivated lifecycle event
Test files evaluated: 7 (4 new, 3 modified)
Fix files: 9


Overall Verdict

⚠️ Tests need improvement

The new LifecycleEventOrderTests files are a good addition covering the new OnAppInstanceActivated event. However, the core Windows WebAuthenticator implementation (replacing PlatformNotSupportedException) has zero automated test coverage; every WebAuthenticator_Tests.cs test carries [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)] and requires a real browser OAuth flow.


1. Fix Coverage — ⚠️

Lifecycle path — ✅ covered.
LifecycleEventOrderTests.Windows.cs verifies that OnAppInstanceActivated fires, fires before OnLaunching, and fires exactly once. The MauiProgram.cs hooks log the event. This directly exercises the new lifecycle callback path in MauiWinUIApplication.OnLaunched.

WebAuthenticator Windows path — ❌ not covered automatically.
The entire WebAuthenticator.windows.cs implementation (previously a stub that threw PlatformNotSupportedException) is new. The only tests for it are in WebAuthenticator_Tests.cs, which all require human interaction — a real browser completing an OAuth redirect. These tests will not run in automated CI and therefore cannot catch regressions.


2. Edge Cases & Gaps — ⚠️

Covered (lifecycle):

  • All four Windows startup events fire in the correct order
  • OnAppInstanceActivated fires exactly once at startup

Missing:

  • No automated test for WebAuthenticatorImplementation.OnAppActivation — the static handler that handles protocol-activated AppActivationArguments and calls OAuth2Manager.CompleteAuthRequest. This is the key plumbing for the OAuth callback on Windows.
  • No test for the transient-helper process-kill pathif (WindowStateManager.Default.GetActiveWindow() is null) Process.Kill() in OnAppActivation. This path handles the case where the app is launched headlessly as a redirect target; no test covers it.
  • No test for unpackaged app registration validation — the registry check (Registry.ClassesRoot.OpenSubKey) and the http/https scheme rejection in AuthenticateAsync are untested.
  • No test for OnAppInstanceActivated being invoked on re-activationRegisterForAppInstanceActivated() subscribes to AppInstance.GetCurrent().Activated for future activations, but there's no test that a second activation fires the lifecycle event again.
  • No test for cancellation on WindowsRedirect_WithCancellation is still human-interaction-only and won't run in CI.
  • No test for OAuth failure pathsIsUserCancellation, error propagation from authRequestResult.Failure, are untested.

3. Test Type Appropriateness — ✅

Current: Device Tests (appropriate for the lifecycle tests; appropriate for end-to-end OAuth if they could be automated)
Recommendation: The lifecycle tests are well-chosen as device tests. For the WebAuthenticator.windows.cs logic, a unit test could cover the OnAppActivation method by mocking/stubbing AppActivationArguments with a known IProtocolActivatedEventArgs, testing the return value and the transient-kill branch without needing a full Windows app. The protocol-declaration validation (IsUriProtocolDeclared) could also be unit-tested with a synthetic XDocument. The full end-to-end OAuth flow reasonably stays as a human-interaction test.


4. Convention Compliance — ✅

No issues detected by the automated script (0 convention issues).

  • LifecycleEventOrderTests files use [Fact] (xUnit), [Category(TestCategory.Lifecycle)] on the class. ✅
  • WebAuthenticator_Tests.cs uses [Theory]/[InlineData] (xUnit), [Category("WebAuthenticator")] on the class, and [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)] on each test. ✅
  • New TestCategory.Lifecycle constant added to TestCategory.cs. ✅

5. Flakiness Risk — ⚠️ Medium

  • The LifecycleEventOrderTests read from a static List(string) LifecycleEventLog that is populated once at app startup. If the test runner creates multiple MauiApp instances or if any test causes a restart, the log could contain duplicate entries or be in an unexpected state. This is a subtle design concern — the tests assume the log is only populated once and contains exactly the startup sequence.
  • All WebAuthenticator_Tests.cs tests depend on an external HTTP server (xamarin-essentials-auth-sample.azurewebsites.net) and human interaction; not flaky in CI because they're skipped, but would be fragile if ever run automatically.

6. Duplicate Coverage — ✅

No duplicate coverage found. The lifecycle tests are entirely new. The WebAuthenticator_Tests.cs changes remove old #if WINDOWS blocks that asserted PlatformNotSupportedException, which were made redundant by the new Windows implementation.


7. Platform Scope — ⚠️

The fix touches cross-platform files (WebAuthenticator.shared.cs, Platform.shared.cs, EssentialsMauiAppBuilderExtensions.cs) as well as Windows-specific files. Lifecycle tests are provided for all three platforms (Android, iOS, Windows) which is good. However, the WebAuthenticator_Tests.cs tests appear to target only the Essentials Windows device test project (given the Package.appxmanifest changes). Coverage for the cross-platform API signature changes (AuthenticateAsync with CancellationToken) on non-Windows platforms is not verified by new tests.


8. Assertion Quality — ✅

Lifecycle tests: Assertions are specific and include diagnostic messages with the full event log on failure (e.g., $"Expected OnAppInstanceActivated before OnLaunching. Log: [{string.Join(", ", log)}]"). Excellent quality.

WebAuthenticator tests: Assertions check AccessToken, RefreshToken, ExpiresIn, and ResponseDecoder.CallCount. These are specific enough for their purpose.


9. Fix-Test Alignment — ⚠️

The lifecycle tests align well with WindowsLifecycle.cs, WindowsLifecycleBuilderExtensions.cs, and MauiWinUIApplication.cs changes. However, the most complex new code — WebAuthenticator.windows.cs (OnAppActivation, AuthenticateAsync implementation, IsUriProtocolDeclared, IsUserCancellation) — has no automated test alignment. The test file that exercises this code (WebAuthenticator_Tests.cs) is exclusively human-interaction and cannot be validated by CI.


Recommendations

  1. Add a unit test for WebAuthenticatorImplementation.OnAppActivation — create a test that constructs a mock AppActivationArguments with an IProtocolActivatedEventArgs carrying a known URI, and verify the method returns true when OAuth2Manager.CompleteAuthRequest succeeds. This is the core Windows OAuth plumbing and has no automated coverage.

  2. Add unit tests for input validation in AuthenticateAsync — the packaged-app protocol check, unpackaged http/https scheme rejection, and null argument handling are pure logic that can be tested in isolation without a real OAuth provider.

  3. Consider a test for re-activation via AppInstance.Activated — a device test that verifies OnAppInstanceActivated fires when the Activated event is raised on AppInstance.GetCurrent() after startup would cover the RegisterForAppInstanceActivated path.

  4. Guard the LifecycleEventLog static state — consider clearing/resetting LifecycleEventLog in a [ClassInitialize] or noting explicitly in the test that it assumes single-startup semantics, to prevent potential ordering surprises if multiple MauiApp instances are ever created in the same test process.

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dc.services.visualstudio.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dc.services.visualstudio.com"

See Network Configuration for more information.

Note

🔒 Integrity filtering filtered 2 items

Integrity filtering activated and filtered the following items during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

mattleibow and others added 2 commits April 9, 2026 02:38
Update WebAuthenticator, Platform, EssentialsMauiAppBuilderExtensions,
and PublicAPI.Unshipped.txt to use the renamed lifecycle event.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ⚠️ Tests need improvement

The lifecycle event ordering tests are well-written and directly validate the new OnAppInstanceActivated API. However, key conditional branches in WebAuthenticatorImplementation.OnAppInstanceActivated have no automated coverage, and some potentially unit-testable logic is untested.

👍 / 👎 — Was this evaluation helpful? React to let us know!

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34887 — Adds OnAppInstanceActivated Windows lifecycle event + WebAuthenticator OAuth redirect support
Test files evaluated: 4 (LifecycleEventOrderTests.Windows.cs, .iOS.cs, .Android.cs, WebAuthenticator_Tests.cs)
Fix files: 9 (WebAuthenticator.windows.cs, MauiWinUIApplication.cs, WindowsLifecycle.cs, WindowsLifecycleBuilderExtensions.cs, EssentialsMauiAppBuilderExtensions.cs, WebAuthenticator.shared.cs, Platform.shared.cs + samples)


1. Fix Coverage — ⚠️

The lifecycle ordering tests (LifecycleEventOrderTests.Windows.cs) directly verify that OnAppInstanceActivated fires during startup and before OnLaunching. The MauiProgram.cs infrastructure is correctly wired to log the event. This is good coverage for the lifecycle event routing itself.

However, the core Windows-specific fix — WebAuthenticatorImplementation.OnAppInstanceActivated which handles OAuth protocol redirects — has no automated test coverage. All WebAuthenticator_Tests.cs tests are marked [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)], meaning they require manual browser interaction and will not run in CI. The automated tests verify that the lifecycle event fires, but not that it correctly processes OAuth callbacks.

2. Edge Cases & Gaps — ⚠️

Covered:

  • OnAppInstanceActivated fires during startup
  • OnAppInstanceActivated fires before OnLaunching
  • OnAppInstanceActivated fires exactly once during startup
  • Basic redirect authentication flow (human-interaction tests only)

Missing (untested code paths in WebAuthenticatorImplementation.OnAppInstanceActivated):

  • args is null guard → returns false (null input path not tested)
  • args.Kind != ExtendedActivationKind.Protocol → returns false (non-protocol activation not tested)
  • args.Data is not IProtocolActivatedEventArgs → returns false (unexpected data type not tested)
  • !OAuth2Manager.CompleteAuthRequest(...) → returns false (failed auth completion not tested)
  • WindowStateManager.Default.GetActiveWindow() is null → calls Process.Kill() (headless/transient instance path not tested — this is a critical edge case where the app terminates itself)

Missing (untested code paths in MauiWinUIApplication):

  • The re-launch path (_application != null && _services != null branch in OnLaunched) that handles app re-activation without rebuilding MAUI is not tested
  • _isRegisteredForAppInstanceActivated deduplication guard not directly tested (only indirectly via "fires exactly once" test)

Missing unit-testable logic:

  • IsUserCancellation (detects access_denied and cancel-string patterns) has no unit tests — this is pure string logic that could be a simple [Theory] with multiple cases

3. Test Type Appropriateness — ✅

Current: Device Tests (lifecycle order), Human-interaction Device Tests (WebAuthenticator)
Recommendation: Appropriate for what is tested. Lifecycle ordering inherently requires a running platform to validate actual app startup behavior.

The human-interaction designation for WebAuthenticator_Tests.cs is correct since OAuth flows require a browser and live server. The missing gap is that some purely defensive logic (null guards, IsUserCancellation) could be extracted and unit-tested without platform context.

4. Convention Compliance — ✅

  • Device tests use [Fact] (xUnit) ✅
  • [Category(TestCategory.Lifecycle)] applied at class level ✅
  • New TestCategory.Lifecycle constant added ✅
  • WebAuthenticator_Tests.cs uses [Theory] + [InlineData] correctly ✅
  • Package.appxmanifest updated to declare the xamarinessentials protocol extension for Windows tests ✅

5. Flakiness Risk — ✅ Low

The lifecycle order tests read from MauiProgram.LifecycleEventLog, a static list populated at app startup. Since the app starts once and tests read from a shared log, this should be stable. The LifecycleEventLog.Clear() in CreateMauiApp() runs at startup, before tests execute, so there's no race condition. Error messages include the full log for diagnostics, which is good practice.

6. Duplicate Coverage — ✅ No duplicates

No existing lifecycle event ordering tests were found. The WebAuthenticator_Tests.cs tests add new cancellation token overload coverage (new AuthenticateAsync(options, cancellationToken) API). These are additive, not redundant.

7. Platform Scope — ⚠️

Platform Fix Coverage Test Coverage
Windows ✅ Primary fix target ✅ Lifecycle tests; ⚠️ WebAuthenticator only human-interaction
iOS No change ✅ Baseline lifecycle tests added
Android No change ✅ Baseline lifecycle tests added
MacCatalyst No change ❌ No lifecycle tests for MacCatalyst

The PR adds iOS and Android lifecycle ordering tests as useful baselines. MacCatalyst is absent, though it shares the iOS lifecycle so this is a minor gap.

8. Assertion Quality — ✅

Lifecycle tests have strong, specific assertions:

Assert.True(activatedIndex < launchingIndex,
    $"Expected OnAppInstanceActivated before OnLaunching. Log: [{string.Join(", ", log)}]");

This format — using Assert.True with an explicit failure message containing the full log — is excellent for diagnosing failures. The "fires exactly once" assertion is also specific and regression-proof.

9. Fix-Test Alignment — ⚠️

The lifecycle ordering tests align well with the lifecycle-routing changes in MauiWinUIApplication.cs, WindowsLifecycle.cs, and WindowsLifecycleBuilderExtensions.cs.

However, the core OAuth activation logic in WebAuthenticatorImplementation.OnAppInstanceActivated (which handles the actual bug fix — redirecting protocol activations back to the OAuth flow) is not aligned with any automated test. The tests confirm the event fires, but don't verify it does the right thing when called with real OAuth activation arguments.


Recommendations

  1. Add automated tests for WebAuthenticatorImplementation.OnAppInstanceActivated guard conditions — The null-arg, wrong-kind, and failed-completion paths are straightforward to test as device tests on Windows without human interaction (pass in mock AppActivationArguments data). The WindowStateManager.Default.GetActiveWindow() is null path (process self-termination) warrants a test or at minimum a comment explaining the expected behavior.

  2. Add unit tests for IsUserCancellation — This is pure string matching logic and a prime candidate for a [Theory]-based unit test covering access_denied, various cancel-string patterns, and non-cancellation errors.

  3. Consider testing the re-activation path in MauiWinUIApplication.OnLaunched — The _application != null && _services != null branch that handles app re-activation without rebuilding is a new code path that has no test coverage.

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dc.services.visualstudio.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dc.services.visualstudio.com"

See Network Configuration for more information.

Note

🔒 Integrity filtering filtered 2 items

Integrity filtering activated and filtered the following items during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

mattleibow and others added 4 commits April 9, 2026 03:17
Replace the Startup.cs + Controller pattern with a single Program.cs
using top-level statements and minimal API endpoints. Remove IIS Express
launch profile and unused Logging.Debug package.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the inline version from the sample csproj into eng/Versions.props
and eng/NuGetVersions.targets, matching the pattern used by the other
ASP.NET Core authentication packages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ⚠️ Tests need improvement

The lifecycle ordering tests are well-structured and directly verify the new OnAppInstanceActivated hook. However, the core Windows WebAuthenticator OAuth implementation — the primary motivation for this PR — lacks automated test coverage; all WebAuthenticator_Tests require human interaction and won't run in CI.

👍 / 👎 — Was this evaluation helpful? React to let us know!

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34887 — [Windows] WebAuthenticator: add OAuth support via app activation
Test files evaluated: 7 (3 LifecycleEventOrderTests, 1 MauiProgram, 1 TestCategory, 1 MauiProgramDefaults, 1 WebAuthenticator_Tests)
Fix files: 11


Overall Verdict

⚠️ Tests need improvement

The lifecycle event ordering tests are good and directly cover the MauiWinUIApplication.cs changes. However, WebAuthenticator.windows.cs — which contains the new OnAppInstanceActivated/OAuth2Manager integration, cancellation mapping, and protocol validation logic — has no automated test coverage. All WebAuthenticator_Tests are gated behind [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)] and won't run in CI.


1. Fix Coverage — ⚠️

Lifecycle side (covered): LifecycleEventOrderTests.Windows.cs directly verifies that OnAppInstanceActivated fires during startup, fires before OnLaunching, and fires exactly once. This accurately mirrors the new ordering in MauiWinUIApplication.cs.

WebAuthenticator side (not automated): The core implementation in WebAuthenticator.windows.cs — calling OAuth2Manager.CompleteAuthRequest, detecting user cancellation via IsUserCancellation, and killing headless instances — is exercised only by [Trait(Human)] tests. If those tests were reverted, CI would not catch a regression in the OAuth flow.


2. Edge Cases & Gaps — ⚠️

Covered:

  • OnAppInstanceActivated fires in correct position in the startup sequence (before OnLaunching)
  • OnAppInstanceActivated fires exactly once
  • Basic end-to-end redirect with access token, refresh token, and expiry (human-only)
  • ResponseDecoder is invoked exactly once (human-only)
  • Cancellation token is threaded through (human-only)

Missing (automated gaps):

  • IsUserCancellation() is pure string-comparison logic — it should be a unit test. Cases to cover: "access_denied" (exact), errors containing "cancel" (case-insensitive), and error descriptions containing "cancel". A failing test here would be invisible in CI today.
  • OnAppInstanceActivated returning false when args.Kind != Protocol — the early-return path is untested.
  • OnAppInstanceActivated returning false when OAuth2Manager.CompleteAuthRequest returns false (non-auth protocol activations not hijacked).
  • The headless-instance kill path (WindowStateManager.Default.GetActiveWindow() is null → Process.Kill()) has no test.
  • Protocol declaration validation (IsUriProtocolDeclared for packaged apps, IsRegistryDeclared for unpackaged apps) — no test that InvalidOperationException is thrown when scheme is missing.
  • The integration wiring: that EssentialsMauiAppBuilderExtensions correctly routes OnAppInstanceActivated to WebAuthenticatorImplementation.OnAppInstanceActivated.

3. Test Type Appropriateness — ⚠️

Current: Device tests (lifecycle) + Device tests with human interaction (WebAuthenticator)

Lifecycle tests: ✅ Appropriate. Verifying event ordering on a running Windows app genuinely requires a device test; you can't mock the WinUI activation pipeline.

WebAuthenticator tests: ⚠️ The human-interaction tests are appropriate for the full end-to-end OAuth redirect scenario. However, several sub-components could be tested without a live server:

Currently untested Could be Why
IsUserCancellation("access_denied", null) Unit test Pure string logic, no platform context needed
IsUserCancellation with "cancel" substrings Unit test Same
AuthenticateAsync throws TaskCanceledException on user cancellation Unit test (with mocked OAuth2Manager) Business logic, not platform
IsUriProtocolDeclared / IsRegistryDeclared throwing Device test (controlled manifest) Needs packaged context but not live OAuth

4. Convention Compliance — ✅

  • LifecycleEventOrderTests.*.cs uses [Fact] (xUnit), [Category] attribute — correct.
  • MauiProgram.cs registration pattern is consistent with the existing device test infrastructure.
  • WebAuthenticator_Tests.cs uses [Theory]/[InlineData]/[Trait] — correct, consistent with the existing Essentials device test style.
  • TestCategory.Lifecycle is a new constant but is defined correctly alongside existing categories.
  • No convention violations detected by the automated script.

5. Flakiness Risk — ⚠️ Medium

  • Lifecycle tests: ✅ Low risk — they check a static log populated during deterministic app startup.
  • WebAuthenticator tests: ⚠️ Medium risk for the human tests — they depend on (xamarinessentialsauthsample.azurewebsites.net/redacted), an external server. If that server goes down or changes, the tests will fail. Since they're [Human] they won't run in automated CI, so this is an accepted risk, but worth noting for maintainability.

6. Duplicate Coverage — ✅ No duplicates

No overlapping tests found for the new OnAppInstanceActivated lifecycle event. The WebAuthenticator tests extend existing patterns.


7. Platform Scope — ✅

  • The core fix is Windows-specific; the Windows lifecycle test directly covers it.
  • Lifecycle ordering tests were also added for iOS and Android, which provides a useful baseline for those platforms even though the primary fix is Windows-only. This is a net positive.
  • No iOS/Android coverage gap for the WebAuthenticator changes (those platforms use a different implementation path untouched by this PR).

8. Assertion Quality — ✅

  • Lifecycle assertions include the full event log in the failure message: $"Expected X before Y. Log: [{string.Join(", ", log)}]" — excellent for debugging CI failures.
  • Assert.Equal(1, count) for "fires exactly once" is precise.
  • WebAuthenticator assertions check AccessToken, RefreshToken, ExpiresIn, and ResponseDecoder.CallCount — all directly relevant to the OAuth result.

9. Fix-Test Alignment — ⚠️

  • MauiWinUIApplication.csLifecycleEventOrderTests.Windows.cs: ✅ Good alignment. The tests directly validate the event ordering enforced by the new activation hook placement.
  • WebAuthenticator.windows.csWebAuthenticator_Tests.cs: ⚠️ Tests are human-only and cover the complete redirect flow, but don't isolate-test the new code paths added in this PR (cancellation detection, headless kill, OAuth2Manager integration, protocol registration checks). If the Windows implementation regressed silently, CI would not catch it.
  • EssentialsMauiAppBuilderExtensions.cs (Windows wiring): ❌ No test verifies that the new OnAppInstanceActivated hook is wired to WebAuthenticatorImplementation.OnAppInstanceActivated at the DI/lifecycle level.

Recommendations

  1. Add a unit test for IsUserCancellation — this pure method covers meaningful business logic ("access_denied", "cancel" in error vs description, case-insensitivity). A [Theory] xUnit test in Essentials.UnitTests would cover this instantly without any platform setup.
  2. Add a device test for OnAppInstanceActivated early-return paths — test that OnAppInstanceActivated returns false for non-Protocol ExtendedActivationKind values and for a Protocol URI that OAuth2Manager doesn't recognize. This ensures the method doesn't accidentally swallow non-OAuth activations.
  3. Consider mocking OAuth2Manager in a device test to verify that a failure response with "access_denied" maps to TaskCanceledException and a generic failure maps to InvalidOperationException — without requiring a live redirect server.

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dc.services.visualstudio.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dc.services.visualstudio.com"

See Network Configuration for more information.

Note

🔒 Integrity filtering filtered 2 items

Integrity filtering activated and filtered the following items during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

In .NET 10, UseAuthorization() requires AddAuthorization() to be called
explicitly — it is no longer auto-registered by the framework.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ✅ Tests are adequate

The lifecycle event ordering tests directly verify the core fix (that OnAppInstanceActivated fires at the correct point in the Windows startup sequence), and the WebAuthenticator tests are properly updated to reflect that Windows is now a supported platform.

👍 / 👎 — Was this evaluation helpful? React to let us know!

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34887 — Windows OAuth activation event support
Test files evaluated: 4 (LifecycleEventOrderTests.Windows.cs, LifecycleEventOrderTests.Android.cs, LifecycleEventOrderTests.iOS.cs, WebAuthenticator_Tests.cs)
Fix files: 11 (Windows lifecycle, MauiWinUIApplication, EssentialsMauiAppBuilderExtensions, WebAuthenticator.windows.cs, WebAuthenticator.shared.cs, etc.)


Overall Verdict

✅ Tests are adequate

The lifecycle event order tests directly validate the critical change: that OnAppInstanceActivated fires before OnLaunching, which is the core invariant being introduced. The WebAuthenticator tests are appropriately updated to drop the #if WINDOWS PlatformNotSupportedException expectations.


1. Fix Coverage — ✅

The new LifecycleEventOrderTests.Windows.cs tests directly trace the fix:

  • WindowsLifecycleEventsFireInCorrectOrder verifies that OnAppInstanceActivated → OnLaunching → OnWindowCreated → OnLaunched — the exact ordering that MauiWinUIApplication.OnLaunched now enforces.
  • OnAppInstanceActivatedFiresExactlyOnce catches potential double-fire regressions.

The removal of #if WINDOWS + PlatformNotSupportedException in WebAuthenticator_Tests.cs validates that WebAuthenticator now compiles and runs on Windows without throwing, aligned with the new implementation.

2. Edge Cases & Gaps — ⚠️

Covered:

  • Event fires and presence (Assert.Contains)
  • Correct ordering for all registered startup events
  • Exact-once firing of OnAppInstanceActivated
  • CancellationToken overload added to test suite (existing tests updated)

Missing:

  • Re-activation branch not tested: MauiWinUIApplication.OnLaunched has a distinct branch for when _application != null (app already running and re-activated). The test only covers first-launch. A re-activation scenario (calling OnLaunched a second time) is not validated.
  • Handler returning true (handled=true) not tested: When OnAppInstanceActivated returns true, the app exits early without calling OnLaunching. No test verifies that OnLaunching/OnLaunched are skipped when the activation is handled.
  • Headless kill path not tested: WebAuthenticator.windows.cs kills the process with Process.GetCurrentProcess().Kill() when no active window exists — this path is untestable in an automated device test, which is acceptable.

3. Test Type Appropriateness — ✅

Current: Device Tests (xUnit)
Recommendation: Correct choice. Lifecycle events genuinely require a running platform application — they cannot be tested in isolation. Device tests are the lightest viable type for this scenario.

The WebAuthenticator_Tests.cs tests are marked [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)], so they gate on human interaction in CI. They correctly document that the full OAuth redirect flow requires a real browser. Automated validation of the registration/callback plumbing (the Package.appxmanifest protocol declaration) is appropriately handled by the manifest change itself.

4. Convention Compliance — ✅

  • New TestCategory.Lifecycle constant added and referenced via [Category(TestCategory.Lifecycle)]
  • Tests use [Fact] (xUnit) ✅
  • MauiProgram.LifecycleEventLog cleared in CreateMauiApp() — avoids state leak across test runs ✅
  • Informative assertion failure messages that include the full event log ✅

5. Flakiness Risk — ✅ Low

  • Tests read from a List(string) populated at startup (no async timing issues)
  • No Task.Delay or Thread.Sleep
  • Log is populated by the time any test runs (startup event capture pattern)
  • LifecycleEventLog.Clear() in CreateMauiApp() prevents cross-test contamination

6. Duplicate Coverage — ✅ No duplicates

The Lifecycle test category is new. The WebAuthenticator_Tests.cs tests are updates of existing tests (not duplicates) to remove the Windows exception expectation.

7. Platform Scope — ⚠️

The fix is primarily Windows, and the lifecycle order tests run only on Windows ✅. However, Android and iOS lifecycle tests are also added to establish a baseline — this is a nice addition.

Minor gap: WebAuthenticator.windows.cs is the only platform-specific WebAuthenticator implementation changed, yet the WebAuthenticator_Tests.cs removes #if WINDOWS guards for all platforms. This is correct (the tests now work on Windows), but there are no Windows-specific automated assertions beyond "doesn't throw" — the full flow remains human-gated.

8. Assertion Quality — ✅

  • Assert.Contains(name, log) — appropriate for presence
  • Assert.True(a < b, $"... Log: [{string.Join(", ", log)}]") — ordering checks with full context on failure, excellent for debugging CI failures
  • Assert.Equal(1, count) — exact count assertion catches double-fire regressions
  • No magic numbers or brittle positional assertions

9. Fix-Test Alignment — ✅

The key code path in MauiWinUIApplication.OnLaunched (the ordering of OnAppInstanceActivated → OnLaunching → OnWindowCreated → OnLaunched) is exactly what LifecycleEventOrderTests.Windows.cs verifies. The test's MauiProgram.cs setup mirrors the real-world lifecycle wiring in EssentialsMauiAppBuilderExtensions.cs.


Recommendations

  1. Consider adding a re-activation test — Call into the activation path a second time (simulating a second protocol activation) and verify OnAppInstanceActivated fires again while OnLaunching/OnLaunched do not. This tests the if (_application != null) fast-path in MauiWinUIApplication. This would be a valuable safety net for regressions in the re-activation flow.
  2. Consider testing OnAppInstanceActivated returning true — Verify that when the handler returns true (auth was handled), the startup does not proceed to create windows. This tests the early-exit contract.

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dc.services.visualstudio.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dc.services.visualstudio.com"

See Network Configuration for more information.

Note

🔒 Integrity filtering filtered 1 item

Integrity filtering activated and filtered the following item during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

@MauiBot MauiBot added s/agent-review-incomplete s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels May 24, 2026
@MauiBot

MauiBot commented May 24, 2026

Copy link
Copy Markdown
Collaborator

🤖 AI Summary

👋 @mattleibow — new AI review results are available. Please review the latest session below.

📊 Review Sessiondc62109 · Windows WebAuthenticator: OAuth2Manager + TCS dual-strategy · 2026-05-24 16:02 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ❌ FAILED

Platform: ANDROID

⚠️ verify-tests-fail.ps1 exited before writing a verification report. Diagnostics below.

Exit code: 1

Artifacts written before exit:

  • verification-log.txt (2.6 KB)
Gate output log (last 60 lines)
📁 Output directory: CustomAgentLogsTmp/PRState/34887/PRAgent/gate/verify-tests-fail
🔍 Detecting base branch and merge point...
No PR detected, scanning remote branches for closest base...
✅ Base branch: main (via closest-merge-base)
✅ Merge base commit: b0ea772f
   (6 commits ahead of main)
╔═══════════════════════════════════════════════════════════╗
║         FULL VERIFICATION MODE                            ║
╠═══════════════════════════════════════════════════════════╣
║  Fix files detected - will verify:                        ║
║  1. Tests FAIL without fix                                ║
║  2. Tests PASS with fix                                   ║
╚═══════════════════════════════════════════════════════════╝
✅ Fix files (20):
   - eng/NuGetVersions.targets
   - eng/Versions.props
   - eng/pipelines/ci-copilot.yml
   - src/Controls/samples/Controls.Sample/MauiProgram.cs
   - src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs
   - src/Core/src/LifecycleEvents/Windows/WindowsLifecycle.cs
   - src/Core/src/LifecycleEvents/Windows/WindowsLifecycleBuilderExtensions.cs
   - src/Core/src/Platform/Windows/MauiWinUIApplication.cs
   - src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
   - src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs
   - src/Essentials/samples/Sample.Server.WebAuthenticator/Essentials.Sample.Server.WebAuthenticator.csproj
   - src/Essentials/samples/Sample.Server.WebAuthenticator/Program.cs
   - src/Essentials/samples/Sample.Server.WebAuthenticator/Properties/launchSettings.json
   - src/Essentials/samples/Sample.Server.WebAuthenticator/Startup.cs
   - src/Essentials/samples/Samples/View/WebAuthenticatorPage.xaml
   - src/Essentials/samples/Samples/ViewModel/WebAuthenticatorViewModel.cs
   - src/Essentials/src/Platform/Platform.shared.cs
   - src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
   - src/Essentials/src/WebAuthenticator/WebAuthenticator.shared.cs
   - src/Essentials/src/WebAuthenticator/WebAuthenticator.windows.cs
🔍 Auto-detecting test filter from changed test files...
✅ Auto-detected 1 test(s):
   📱 [DeviceTest] WebAuthenticator_Tests (filter: WebAuthenticator_Tests)
[2026-05-24 14:58:34] ==========================================
[2026-05-24 14:58:34] Verify Tests Fail Without Fix
[2026-05-24 14:58:34] ==========================================
[2026-05-24 14:58:34] Tests detected: 1
[2026-05-24 14:58:34]   - [DeviceTest] WebAuthenticator_Tests (filter: WebAuthenticator_Tests)
[2026-05-24 14:58:34] Platform: android
[2026-05-24 14:58:34] FixFiles: eng/NuGetVersions.targets, eng/Versions.props, eng/pipelines/ci-copilot.yml, src/Controls/samples/Controls.Sample/MauiProgram.cs, src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs, src/Core/src/LifecycleEvents/Windows/WindowsLifecycle.cs, src/Core/src/LifecycleEvents/Windows/WindowsLifecycleBuilderExtensions.cs, src/Core/src/Platform/Windows/MauiWinUIApplication.cs, src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt, src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs, src/Essentials/samples/Sample.Server.WebAuthenticator/Essentials.Sample.Server.WebAuthenticator.csproj, src/Essentials/samples/Sample.Server.WebAuthenticator/Program.cs, src/Essentials/samples/Sample.Server.WebAuthenticator/Properties/launchSettings.json, src/Essentials/samples/Sample.Server.WebAuthenticator/Startup.cs, src/Essentials/samples/Samples/View/WebAuthenticatorPage.xaml, src/Essentials/samples/Samples/ViewModel/WebAuthenticatorViewModel.cs, src/Essentials/src/Platform/Platform.shared.cs, src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt, src/Essentials/src/WebAuthenticator/WebAuthenticator.shared.cs, src/Essentials/src/WebAuthenticator/WebAuthenticator.windows.cs
[2026-05-24 14:58:34] BaseBranch: main
[2026-05-24 14:58:34] MergeBase: b0ea772fff1466e9d6b8d8660d4ffce03c202b96
[2026-05-24 14:58:34] 
[2026-05-24 14:58:34] Verifying fix files exist...
[2026-05-24 14:58:34]   ✓ eng/NuGetVersions.targets exists
[2026-05-24 14:58:34]   ✓ eng/Versions.props exists
[2026-05-24 14:58:34]   ✓ eng/pipelines/ci-copilot.yml exists
[2026-05-24 14:58:34]   ✓ src/Controls/samples/Controls.Sample/MauiProgram.cs exists
[2026-05-24 14:58:34]   ✓ src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs exists
[2026-05-24 14:58:34]   ✓ src/Core/src/LifecycleEvents/Windows/WindowsLifecycle.cs exists
[2026-05-24 14:58:34]   ✓ src/Core/src/LifecycleEvents/Windows/WindowsLifecycleBuilderExtensions.cs exists
[2026-05-24 14:58:34]   ✓ src/Core/src/Platform/Windows/MauiWinUIApplication.cs exists
[2026-05-24 14:58:34]   ✓ src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt exists
[2026-05-24 14:58:34] ERROR: Fix file not found: src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs

🧪 UI Tests — Essentials

Detected UI test categories: Essentials

⏭️ Deep UI tests — 0 passed, 0 failed across 1 category on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Essentials
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

🔍 Regression Cross-Reference

🔍 Regression Cross-Reference

🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.


🔍 Pre-Flight — Context & Validation

Issue: N/A - PR supersedes #30056; no direct issue metadata available in local gh auth context.
PR: #34887 - [Windows] WebAuthenticator: add OAuth support via app activation
Platforms Affected: Windows primary; Android used for requested regression testing because shared Essentials/WebAuthenticator tests and samples are touched.
Files Changed: 20 implementation/sample/build/public API files detected by gate; 7 test/device-test files detected in the squashed PR.

Key Findings

  • GitHub CLI was not authenticated, so PR metadata was gathered through GitHub MCP plus local squashed commit state.
  • Gate already failed before executing tests: verify-tests-fail.ps1 detected deleted sample files as fix files, then failed on missing src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs.
  • Current PR fix combines Windows WebAuthenticator implementation with a substantial sample-server rewrite/deletion, which is unrelated to the core Windows activation fix and creates Android gate churn.
  • Android Essentials device tests with -TestFilter "WebAuthenticator_Tests" completed successfully on emulator API 30: Tests run: 297 Passed: 267 Failed: 0 Ignored: 30.

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: high
Errors: 2 | Warnings: 0 | Suggestions: 0

Key code review findings:

  • src/Essentials/src/WebAuthenticator/WebAuthenticator.shared.cs:73 adds a required Windows member to shipped public interface IPlatformWebAuthenticatorCallback, which is a breaking change for existing implementers.
  • src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs:78 makes Essentials perform FindOrRegisterForKey("MauiEssentials"), redirect, and Process.Kill() for any unhandled activation, potentially forcing single-instance behavior for unrelated Windows app activations.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34887 Add Windows WebAuthenticator using OAuth2Manager, new app-activation lifecycle hook, and sample-server rewrite/deletions ❌ FAILED (Gate pre-test file existence); Android regression command ✅ PASSED 27 files Original PR; gate stopped because deleted sample files were classified as required fix files

🔬 Code Review — Deep Analysis

Code Review — PR #34887

Independent Assessment

What this changes: Adds Windows WebAuthenticator support using WinAppSDK OAuth2Manager and app-activation callbacks, plus sample/test updates.
Inferred motivation: Enable Windows OAuth/protocol callback handling instead of throwing PlatformNotSupportedException.

Reconciliation with PR Narrative

Author claims: Ports Windows OAuth/WebAuthenticator work onto the new Windows app-activation lifecycle hook.
Agreement/disagreement: Matches the code, but two implementation details introduce broader API/lifecycle risks.

Findings

❌ Error — Breaking public interface change

src/Essentials/src/WebAuthenticator/WebAuthenticator.shared.cs:73

Adds a required member to shipped public Windows interface IPlatformWebAuthenticatorCallback. Existing implementers may fail to compile/load. Prefer a non-breaking pattern such as a default interface method, versioned interface, or helper.

❌ Error — Essentials forces Windows single-instance behavior

src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs:78

The fallback FindOrRegisterForKey("MauiEssentials") / redirect / Process.Kill() path runs for any unhandled activation, not just auth protocol callbacks. This can make all Windows MAUI apps using Essentials single-instance and kill secondary launches.

Devil's Advocate

The general direction is reasonable, and existing platform implementations also keep one pending auth operation. These two issues are still concrete: one affects public API compatibility, the other changes app lifecycle semantics outside WebAuthenticator.

Verdict: NEEDS_CHANGES

Confidence: high
Summary: Code should address the public interface break and scope Windows activation redirection to auth/protocol scenarios before merge. No GitHub comments were posted.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix-1 Surgical Windows activation bridge; keep sample server unchanged; replace breaking callback member with optional Windows hook; remove global Essentials redirect/kill fallback ✅ Android regression passed; gate not re-run per instruction; statically addresses gate missing-file failure EssentialsMauiAppBuilderExtensions.cs, WebAuthenticator.shared.cs, WebAuthenticator.windows.cs, restored sample server files Best immediate repair: fixes both code-review errors and the gate failure cause
2 try-fix-2 Launcher + protocol callback only; avoid OAuth2Manager and direct OAuth sample changes ✅ Android regression passed; gate not re-run per instruction; statically addresses gate missing-file failure WebAuthenticator.windows.cs, sample view model/server restored Simpler and safer, but may not satisfy direct OAuth2/PKCE product goal
3 try-fix-3 Explicit Windows activation handler service/dispatcher; WebAuthenticator registers scoped handler only while pending ✅ Android regression passed; gate not re-run per instruction; statically addresses gate missing-file failure Windows lifecycle/platform hosting + WebAuthenticator registration Clean long-term design, but larger and likely needs design/API review
PR PR #34887 Windows WebAuthenticator via OAuth2Manager, new lifecycle hook, sample-server rewrite/deletions ❌ Gate failed before testing; ✅ Android regression command passed in this environment 27 files Gate failure: deleted sample controller/startup files classified as fix files and then required to exist

Cross-Pollination

Model Round New Ideas? Details
maui-expert-reviewer 1 Yes Prefer surgical repair: keep samples unchanged, use non-breaking optional Windows activation hook, scope activation handling to pending WebAuthenticator protocol callbacks
code-review sub-agent 1 Yes Avoid breaking IPlatformWebAuthenticatorCallback; remove Essentials global single-instance redirect/Process.Kill behavior
orchestrator 1 Yes Treat the Android gate failure as a deleted-file/gate-classification problem; avoid unrelated sample deletions in fix candidates

Test command executed: pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Essentials -Platform android -TestFilter "WebAuthenticator_Tests"

Android regression result: ✅ Passed on emulator emulator-5554 API 30. XHarness exit code 0; Tests run: 297 Passed: 267 Inconclusive: 0 Failed: 0 Ignored: 30.

Gate handling: The gate phase was not re-run, per instruction. The existing gate artifact shows failure before test execution due to missing deleted sample file src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs.

Exhausted: Yes — remaining distinct approaches are variations of (1) surgical non-breaking activation hook, (2) simpler Launcher-only WebAuthenticator, or (3) larger activation dispatcher/service architecture.
Selected Fix: Candidate #1 — it is the smallest candidate that directly addresses the gate failure and the two high-confidence code-review errors while preserving the PR's Windows WebAuthenticator goal.


📋 Report — Final Recommendation

Comparative Report — PR #34887

Candidate ranking

Rank Candidate Regression status Assessment
1 pr-plus-reviewer No failed regression observed; gate not re-run per instruction Best candidate. It preserves the PR's Windows OAuth/AppActivation goal while applying the expert feedback: non-breaking callback wiring, no global single-instance kill fallback, scoped pending-auth activation handling, non-empty OAuth client id handling, stable sample URL, and restored deleted sample files.
2 try-fix-1 Android regression passed Strong surgical repair. It fixes the two original high-confidence review errors and restores the sample files that blocked the gate, but the recorded candidate does not address the newly found empty client_id OAuth2Manager issue or the transient dev-tunnel sample URL as explicitly as pr-plus-reviewer.
3 try-fix-2 Android regression passed Safest runtime behavior because it avoids OAuth2Manager and uses Launcher plus protocol callback only, but it may not satisfy the PR's direct OAuth2/PKCE product goal.
4 try-fix-3 Android regression passed Architecturally clean activation-dispatch direction, but it is larger than needed for this fix and likely requires additional design/API review. It is less suitable as the immediate winning candidate.
5 pr Gate failed before test execution; Android regression command passed Raw PR should not win. It has major API/lifecycle/OAuth correctness issues and deleted sample files that triggered the known gate failure.

Candidate details

pr

The raw PR implements Windows WebAuthenticator app-activation support with OAuth2Manager, adds a Windows activation lifecycle hook through Essentials, and rewrites/deletes parts of the sample server. It failed the gate before test execution because deleted sample files were classified as required fix files. The expert review also found major API compatibility, Windows lifecycle, and OAuth correctness issues.

pr-plus-reviewer

This candidate applies the expert review feedback in a sandbox copy. It keeps the PR's intended Windows WebAuthenticator/OAuth support but removes the breaking interface member, avoids Essentials-owned global single-instance behavior, handles only pending matching WebAuthenticator protocol callbacks, avoids an empty OAuth client id, restores a stable sample endpoint, and restores the deleted sample files that caused the gate blocker.

try-fix-1

This is the best STEP 6a candidate. It is small and directly addresses the initial code-review errors plus the deleted-file gate failure. It ranks below pr-plus-reviewer because it does not explicitly incorporate the later expert findings around OAuth2Manager's empty client id and the transient dev-tunnel sample endpoint.

try-fix-2

This candidate is simpler and avoids the OAuth2Manager correctness issue by relying on Launcher plus protocol activation. It is safe but less complete for the PR's stated Windows direct OAuth2/PKCE objective.

try-fix-3

This candidate has the cleanest long-term lifecycle architecture, but it is too broad for a quick PR repair and needs design review beyond the scope of the immediate fix.

Winning candidate

Winner: pr-plus-reviewer

Rationale: it is the only candidate that preserves the PR's intended Windows OAuth support while addressing all expert findings and the known gate blocker. The raw PR is disqualified by major correctness issues and gate failure; the try-fix candidates that passed Android are credible, but each either leaves later expert feedback unaddressed or gives up part of the intended feature.


@kubaflo kubaflo 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.

Could you please check the ai's suggestions?

@solomonfried

Copy link
Copy Markdown
Contributor

Looks like there has been significant progress on this. Is it safe to assume that WebAuthenticator will work in MAUI apps when .NET 11 is released?

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 10, 2026
@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 10, 2026
kubaflo pushed a commit that referenced this pull request Jul 14, 2026
…ivation (#36415)

> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could <a
href=https://github.com/dotnet/maui/wiki/Testing-PR-Builds>test the
resulting artifacts</a> from this PR and let us know in a comment if
this change resolves your issue. Thank you!

### Root Cause

`WebAuthenticator` is unsupported on Windows. A custom-protocol callback
may launch a second app process, so Windows needs both protocol
activation handling and cross-instance routing back to the process that
owns the pending authentication.

Windows App SDK also cannot reliably re-register an `AppInstance` after
`UnregisterKey()`
([microsoft/WindowsAppSDK#4420](microsoft/WindowsAppSDK#4420)).
Releasing the route after the first authentication caused a subsequent
callback process to find no route owner.

After a redirected callback completes, the existing MAUI window is not
automatically restored or brought in front of the browser. The request
must retain its originating window and perform a best-effort foreground
activation only after a valid callback completes it.

### Description of Change

Adds a Windows implementation that preserves the existing cross-platform
contract: caller-provided authentication URL and callback URL in,
`WebAuthenticatorResult` out.

- opens the system browser with `Launcher.LaunchUriAsync`
- handles protocol callbacks through `OnAppInstanceActivated` from
#34883
- registers a WebAuthenticator-specific `AppInstance` route for the
callback scheme
- redirects transient callback processes to the instance that owns that
route
- keeps the route registered for the lifetime of the process while
keeping pending authentication state request-scoped
- validates packaged manifest and unpackaged protocol registration
- forwards callbacks through the existing
`IPlatformWebAuthenticatorCallback`
- captures the `AppWindow` that started each authentication so
multi-window callbacks return to the correct window
- restores minimized or hidden windows without intermediate activation,
then makes one best-effort foreground request

This intentionally does **not** use `OAuth2Manager`. The caller still
owns `state`, PKCE, provider parameters, and token exchange.

### Key Technical Details

- route keys use `Microsoft.Maui.WebAuthenticator:{scheme}`
- application-owned `AppInstance` keys are preserved
- transient processes use `AppInstance.GetInstances()` to find the route
owner without registering another key
- final callback matching still uses `WebUtils.CanHandleCallback(...)`
- only one WebAuthenticator request can be pending per app instance,
matching the existing platform model
- redirect completion is awaited before the transient callback process
exits
- the route remains registered for the process lifetime to avoid the
`UnregisterKey()` re-registration issue
- the persistent route is routing infrastructure; the TCS and expected
callback URI determine whether a callback is currently handled
- only the callback that wins `TrySetResult` may restore and foreground
its captured window
- cancellation, invalid callbacks, duplicate callbacks, and late
callbacks do not request foreground activation
- minimized windows use `OverlappedPresenter.Restore(false)` and hidden
windows use `AppWindow.Show(false)` so `SetForegroundWindow` is the only
explicit activation request
- foreground work is isolated behind best-effort exception handling and
never changes the OAuth result

`RedirectActivationToAsync` already requests foreground permission for
the route-owner process through `AllowSetForegroundWindow` in Windows
App SDK
[`AppInstance::QueueRequest`](https://github.com/microsoft/WindowsAppSDK/blob/91160b078668051e58ba1e19eb699f4efc8c5b20/dev/AppLifecycle/AppInstance.cpp).
MAUI therefore does not duplicate that call.

The foreground path intentionally does not use
`AppWindow.DispatcherQueue`.
[`AppWindow`](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.appwindow?view=windows-app-sdk-1.8)
and
[`OverlappedPresenter`](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.overlappedpresenter?view=windows-app-sdk-1.8)
are agile, and a dispatcher queue is not guaranteed to be available in
the redirected activation context.

Packaged apps declare the callback scheme in `AppxManifest.xml`;
unpackaged apps register it with
`ActivationRegistrationManager.RegisterForProtocolActivation(...)`.

### Foreground Behavior

Windows ultimately decides whether
[`SetForegroundWindow`](https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-setforegroundwindow)
succeeds. Foreground activation is therefore best effort by design:
failure is diagnostic only and never changes a successful authentication
result.

### Testing

The routing revision passed Unit, Public API, Windows build, sample
build, and all 14 WebAuthenticator Windows device tests.

The foreground revision was validated with:

- `Microsoft.Maui.Essentials` Windows build: 0 warnings, 0 errors
- manual OAuth callback verification confirming that the existing MAUI
window returns in front of the browser
- runtime verification that accessing `AppWindow.DispatcherQueue` from
the redirected activation context can fail with `COMException`; the
final implementation does not depend on it

Additional human-interaction validation remains useful for minimized,
maximized, multi-window, cancellation followed by retry, invalid
callback, and consecutive-authentication scenarios.

### Breaking Changes

None.

### Issues Fixed

- Alternative implementation to #34887
- Related to #30056
- Stacked on #34883
- Related Windows App SDK behavior:
[microsoft/WindowsAppSDK#4420](microsoft/WindowsAppSDK#4420)

Looking forward to your feedback, thanks!

@mattleibow
@dotMorten

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@kubaflo

kubaflo commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

closed in favour of #36640

@kubaflo kubaflo closed this Jul 18, 2026
kubaflo pushed a commit that referenced this pull request Jul 27, 2026
…tor support (#36640)

### Description of Change

.NET MAUI on Windows currently has two related gaps: it doesn't expose
the Windows App SDK `AppActivationArguments` through its lifecycle
events, and `WebAuthenticator` isn't supported on Windows. As a result,
single-instancing, protocol and file activation, and browser-based
authentication callbacks require additional app-specific plumbing.

This PR addresses both areas by adding:

- a Windows `OnAppInstanceActivated` lifecycle hook with handled
semantics;
- initial and subsequent `AppInstance` activation delivery through the
MAUI lifecycle;
- lifetime-safe UI dispatching for later activation callbacks;
- Windows `WebAuthenticator` support using the system browser, protocol
activation, callback routing, result completion, asynchronous activation
redirection, and best-effort window foreground activation;
- packaged manifest and unpackaged protocol-registration validation;
- a Controls Sample implementation demonstrating single-instancing and
activation redirection;
- Windows lifecycle, WebAuthenticator routing, and callback validation
tests.

The implementation keeps the existing cross-platform `WebAuthenticator`
contract and uses the established `Launcher` + callback route + pending
task model.

### Related Work

This PR fixes #2702 and is also related to #9973.

It is a fresh, self-contained alternative to:

#34883, which introduces the Windows AppInstance lifecycle hook;
#34887, which adds Windows WebAuthenticator support as a stacked
OAuth2Manager-based follow-up to #34883.

This version combines both areas in one PR based on the current
`net11.0` branch while preserving the existing MAUI WebAuthenticator
model.

Related to #9973.

### Testing

- Debug and Release x64 builds passed for Core, Essentials, Controls
Sample, Core DeviceTests, and Essentials DeviceTests.
- `WebUtils_Tests`: 20/20 passed.
- Windows WebAuthenticator routing helpers: 14/14 passed.
- Windows lifecycle device tests: 3/3 passed.
- Public API validation passed.
- Controls Sample single-instance redirection was manually validated,
including suspending the route owner and resuming it after a second
instance attempted redirection.
- Packaged and framework-dependent unpackaged browser/protocol callback
transports completed successfully and returned to the existing runner
without leaving a second window.
- Application-owned routing completed `owned-ok` in the main process
while preserving the `Pr36640.AppOwned` key; the transient process
exited and the main process remained alive.
- Sequential authentications using `pr36640-a` and `pr36640-b` both
completed in one process, with the route key updating to
`Microsoft.Maui.WebAuthenticator:pr36640-b`.
- A focused compatibility harness confirmed the original abstract-member
break (`CS0535`), source and binary compatibility with the default
member, a default result of `false`, and an overriding result of `true`.
- The public test endpoint returned its literal `ACCESS_TOKEN_VALUE`
placeholder instead of `testtokenvalue`, so transport completed but that
external-data assertion remained unsuitable as a deterministic pass
criterion.

### Review Notes

- **Asynchronous activation redirect:** the lifecycle callback now marks
the transient activation handled immediately and starts an asynchronous
helper. The helper awaits `RedirectActivationToAsync` and terminates the
transient process only after the native operation completes or fails,
removing the previous sync-over-async wait from the activation path.
- **Why there is no destructive timeout:** a five-second `WaitAsync`
followed by process termination was tested with the route owner
suspended. The transient exited at about 5.4 seconds, but resuming the
owner produced an unhandled Windows App Runtime failure in
`RedirectionRequest.cpp` (`0x8003001E`, `wil::ResultException`). The
managed timeout does not safely cancel the pending native redirection.
This matches the Windows App SDK guidance that the caller must await
redirection before exiting:
https://learn.microsoft.com/windows/apps/windows-app-sdk/applifecycle/applifecycle-instancing
- **Redirected activation lifetime:** Windows App SDK keeps the
redirected activation request alive through the synchronous
`AppInstance.Activated` event. MAUI now waits for the dispatched UI
lifecycle handlers to finish before that event callback returns. This
prevents the redirecting process from releasing marshaled resources
before the UI consumes the activation, while preserving lifecycle
exception behavior and avoiding a destructive timeout.
- **Public interface compatibility:** the new Windows member on the
already-shipped `IPlatformWebAuthenticatorCallback` has a default
implementation returning `false`. Existing custom implementers therefore
remain source- and binary-compatible, while implementations that opt in
can override it.
- **Warm activation delivery:** the same activation might theoretically
reach both `OnLaunched` and `AppInstance.Activated`. This has not been
reproduced and should be verified before adding deduplication.
- **Application-owned `AppInstance` key:** when an app owns the key, it
also owns activation routing and must redirect protocol callbacks to the
instance that started authentication. The Controls Sample now calls this
out explicitly. A general fail-fast cannot distinguish a complete
cooperative router from an incomplete one without rejecting the
supported app-owned scenario.
- **Sequential callback schemes:** Windows App SDK 1.8 can rebind the
current instance to a different key without calling `UnregisterKey`; the
tested A-to-B flow completed successfully. MAUI intentionally avoids
`UnregisterKey` because registering another key after it is unsafe
(`microsoft/WindowsAppSDK#4420`).

Thank you @kubaflo for suggesting a fresh PR and for the previous
reviews.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p/0 Current heighest priority issues that we are targeting for a release. s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants