Skip to content

Add MauiBlazorWebEntra sample — Entra External ID (CIAM) for .NET 10 MAUI + Blazor Web#649

Open
mattleibow wants to merge 11 commits intodotnet:mainfrom
mattleibow:dev/maui-blazor-web-entra-10
Open

Add MauiBlazorWebEntra sample — Entra External ID (CIAM) for .NET 10 MAUI + Blazor Web#649
mattleibow wants to merge 11 commits intodotnet:mainfrom
mattleibow:dev/maui-blazor-web-entra-10

Conversation

@mattleibow
Copy link
Member

New sample: 10.0/MauiBlazorWebEntra

Demonstrates Microsoft Entra External ID (CIAM) authentication for a .NET 10 MAUI Blazor Hybrid app with a shared Blazor Web companion.

Architecture

  • Dual auth on web: OIDC+Cookie for browsers, JWT Bearer for MAUI API calls
  • MSAL on native: platform-specific interactive sign-in with silent token refresh
  • Shared UI: Razor class library with Home, Counter, Weather, and Account pages

Platform support

Platform Auth method Status
Web (Blazor Server) OIDC via Microsoft.Identity.Web
iOS MSAL + system browser
Android MSAL + Chrome Custom Tabs
Windows MSAL + loopback redirect 🔲 (untested, wired up)
Mac Catalyst MSAL + custom ASWebAuthenticationSession WebUI 🔄 (workaround for MSAL #3527)

Key features

  • Setup-Azure.ps1 — Guided 5-step interactive script to create CIAM tenant, register apps, configure user flows
  • Teardown-Azure.ps1 — Clean removal of app registrations and user flows
  • CIAM sign-up via prompt=create deep-link (screen_hint=signup is not supported by CIAM)
  • Account page showing user profile claims
  • Chrome Custom Tabs on Android (requires <queries> manifest entry for API 30+)
  • Mac Catalyst auth via custom ICustomWebUi using ASWebAuthenticationSession since MSAL has no native Mac Catalyst support

Notable workarounds

  • Mac Catalyst: MSAL.NET doesn't ship a maccatalyst TFM — falls back to generic net8.0 assembly which throws PlatformNotSupportedException. Solved with custom MacCatalystWebUi class implementing ICustomWebUi
  • Android Chrome Custom Tabs: Requires <queries> in AndroidManifest.xml for package visibility on API 30+
  • CIAM sign-up: screen_hint=signup is silently ignored — must use prompt=create

Setup

Run Setup-Azure.ps1 for guided configuration, or see README for manual steps.

mattleibow and others added 9 commits March 12, 2026 23:00
New .NET MAUI Blazor Hybrid + ASP.NET Core Web App sample that replaces
ASP.NET Core Identity with Microsoft Entra External ID (CIAM) for
authentication.

Web server:
- Dual auth via BearerOrCookie policy scheme: OIDC + Cookie for browser
  users, JWT Bearer for MAUI API calls
- Uses Microsoft.Identity.Web instead of EF Core/Identity
- No local user database — Entra manages all accounts
- Login/logout/weather API endpoints

MAUI app:
- MSAL.NET (Microsoft.Identity.Client) for native authentication
- Interactive sign-in via system browser, silent token refresh
- Android: MsalActivity for MSAL redirect URI callback
- iOS: CFBundleURLTypes for MSAL redirect URI scheme

Infrastructure:
- Setup-Azure.ps1: Interactive PowerShell script that creates Entra app
  registrations, exposes API scope, generates client secret, and patches
  all config files with real values
- Teardown-Azure.ps1: Cleanup script to remove app registrations
- README.md with architecture overview and quick start guide

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

- Add Account.razor shared page with two-column claims display
- Add /authentication/register endpoint with prompt=create for CIAM sign-up
- Add Register and Account nav links (Web + MAUI)
- iOS: Add MSAL callback in AppDelegate.cs, keychain security group
- Android: Add OnActivityResult callback, Chrome Custom Tabs <queries> manifest
- Mac Catalyst: Custom ICustomWebUi using ASWebAuthenticationSession (MSAL has no native Mac Catalyst support - issue #3527)
- Upgrade Microsoft.Identity.Client 4.70.0 → 4.83.1
- Rewrite Setup-Azure.ps1 as guided 5-step interactive walkthrough
- Update Teardown-Azure.ps1 to match new setup flow
- Update README quick start section

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add missing Platforms/iOS/Entitlements.plist with keychain-access-groups
- Use $(AppIdentifierPrefix)$(CFBundleIdentifier) instead of hardcoded adalcache group
- Update WithIosKeychainSecurityGroup to match bundle identifier
- Align MacCatalyst entitlements to same pattern

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Convert all .cs files to file-scoped namespaces (C# 10)
- Use primary constructors for MsalAuthenticationStateProvider and WeatherService (C# 12)
- Replace collection initializers with collection expressions (C# 12)
- Remove custom MacCatalystWebUi (ASWebAuthenticationSession workaround)
- Mac Catalyst now uses WithSystemWebViewOptions like iOS
- Remove network.server entitlement (no longer needed without loopback)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On first call to GetAuthenticationStateAsync, attempt a silent token
acquisition using MSAL's cached credentials. This restores the user's
authenticated session automatically when the app restarts, without
requiring an interactive sign-in.

Also converts MsalConfig.cs to file-scoped namespace.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Step 3: Check if app registrations exist by display name before
  creating. Reuse existing apps and preserve client secrets when
  appsettings.json already has a real value. Only generate a new
  secret when the config file still has a placeholder.
- Step 4: Check if signup_signin user flow exists before creating.
  If it exists, verify linked apps and add any missing ones.

This allows the script to be safely re-run without duplicating
resources or invalidating secrets on other machines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace credential reset with Graph API addPassword so new secrets
never invalidate existing ones on other machines. When app already
exists, interactively ask whether to keep the current secret, paste
one from another machine, or generate a new one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update PSScriptRoot paths to resolve parent directory and update
README with new script locations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Microsoft.Identity.Client.Desktop.WinUI3 package for embedded WebView2
  auth on Windows (WAM broker doesn't support CIAM tenants)
- Add Microsoft.Identity.Client.Broker package (Windows-only)
- Update MauiVersion to 10.0.50 to fix HybridWebView.js build issue
- Configure WithWindowsDesktopFeatures for embedded WebView2 + WAM broker
- Set http://localhost redirect URI for Windows (CIAM requires explicit URI,
  WithDefaultRedirectUri resolves to nativeclient on MAUI which doesn't work)
- Pass WinUI3 Window object (not IntPtr handle) to WithParentActivityOrWindow
  for embedded WebView2 compatibility
- Move MsalConfig.cs to project root
- Add platform-specific redirect URI via #if WINDOWS in MsalConfig
- Register http://localhost in Setup-Azure.ps1 alongside msal{ClientId}://auth
- Add Windows token cache persistence via SecureStorage (DPAPI-backed) since
  MSAL only persists automatically on iOS (Keychain) and Android (SharedPrefs)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mattleibow mattleibow force-pushed the dev/maui-blazor-web-entra-10 branch from acd800e to dc6b2ed Compare March 16, 2026 20:17
mattleibow and others added 2 commits March 16, 2026 22:48
- Extract MSAL client setup into MsalServiceExtensions.AddMsalClient()
  extension on IServiceCollection, cleaning up MauiProgram.cs
- Extract platform-specific interactive auth config into
  WithPlatformOptions() extension on AcquireTokenInteractiveParameterBuilder,
  removing #if directives from MsalAuthenticationStateProvider
- Extract Windows token cache persistence into private
  EnableSecureStorageTokenCachePersistence() method using MAUI SecureStorage
  (DPAPI-backed); iOS/Android persist natively via Keychain/SharedPreferences
- Add null checks for platform window/activity in WithPlatformOptions()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align iOS keychain-access-groups entitlement with Mac Catalyst to use
com.microsoft.adalcache (MSAL's default shared keychain group). Add
WithIosKeychainSecurityGroup to the MSAL builder for iOS and Mac
Catalyst so tokens persist correctly across app restarts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mattleibow mattleibow force-pushed the dev/maui-blazor-web-entra-10 branch from 800764c to 597b596 Compare March 16, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant