Install
$ agentstack add skill-davidortinau-maui-skills-maui-authentication ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
.NET MAUI Authentication
Security: Never Embed Secrets
> ❌ Never embed client secrets, API keys, or signing keys in a mobile app binary. They can be extracted trivially via decompilation.
The correct pattern:
- App calls
WebAuthenticatorpointing to your server endpoint - Server initiates the OAuth flow with the identity provider (holds the client secret)
- Provider redirects back to your server with an auth code
- Server exchanges the code for tokens and returns them to the app via the callback URI
WebAuthenticator Gotchas
⚠️ Windows WebAuthenticator is broken
Windows WebAuthenticator is currently broken. See dotnet/maui#2702. Use MSAL or a WinUI-specific workaround for Windows auth flows.
⚠️ Apple Sign In returns name/email only once
Apple only returns the user's name and email on the first sign-in. Cache them immediately — subsequent sign-ins won't include them.
⚠️ PrefersEphemeralWebBrowserSession
Set to true on iOS 13+ to force a fresh login prompt. When false (default), the auth session shares cookies with Safari — the user may be auto-logged in, which can confuse logout/switch-account flows.
⚠️ Callback URI mismatches
The most common auth failure is a URI scheme mismatch. The CallbackUrl in code must exactly match:
- Android:
DataScheme+DataHostin theIntentFilter - iOS:
CFBundleURLSchemesinInfo.plist - Windows:
Protocol NameinPackage.appxmanifest
WebAuthenticator Checklist
- [ ] Callback URI scheme matches across all platform configs and
CallbackUrl - [ ] Android has
WebAuthenticatorCallbackActivitywith correctIntentFilter - [ ] Android 11+ has `` for Custom Tabs in the manifest
- [ ] iOS/Mac Catalyst has
CFBundleURLTypesinInfo.plist - [ ] Client secrets are on the server, not in the app
- [ ] Tokens stored with
SecureStorage, cleared on logout - [ ]
TaskCanceledExceptionhandled gracefully in UI
Choosing Between WebAuthenticator and MSAL.NET
| Criteria | WebAuthenticator | MSAL.NET | |----------|-----------------|----------| | Identity provider | Any OAuth 2.0 / OIDC | Microsoft Entra ID | | Broker support (SSO) | ❌ No | ✅ Microsoft Authenticator, Company Portal | | Conditional Access / MFA | ❌ Manual | ✅ Built-in | | Token cache & refresh | ❌ Manual (SecureStorage) | ✅ Automatic | | Complexity | Simple | More setup | | Use when | Google, Apple, generic OIDC | Entra ID / Azure AD, Microsoft Graph |
MSAL.NET Gotchas
⚠️ Android: OnActivityResult is required
Forgetting AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs in MainActivity.OnActivityResult causes auth to hang silently after the browser returns.
⚠️ iOS: Keychain sharing is required
Without Entitlements.plist containing keychain group com.microsoft.adalcache, token caching fails silently and users are prompted to sign in every time.
⚠️ Handle MsalUiRequiredException
When AcquireTokenSilent throws MsalUiRequiredException, the cached token is expired and interaction is needed. Always fall back to AcquireTokenInteractive.
// ❌ Ignoring MsalUiRequiredException — user gets a crash
var result = await _pca.AcquireTokenSilent(scopes, account).ExecuteAsync(ct);
// ✅ Fall back to interactive when silent fails
try
{
result = await _pca.AcquireTokenSilent(scopes, account).ExecuteAsync(ct);
}
catch (MsalUiRequiredException)
{
result = await _pca.AcquireTokenInteractive(scopes).ExecuteAsync(ct);
}
⚠️ Handle user cancellation gracefully
// ✅ Don't treat cancellation as an error
catch (MsalClientException ex) when (ex.ErrorCode == "authentication_canceled")
{
return null; // User cancelled — not an error
}
⚠️ Blazor Hybrid: Auth happens at the MAUI layer
In MAUI Blazor Hybrid apps, authentication must happen at the MAUI layer (MSAL.NET), not in the Blazor WebView. Don't use AddMicrosoftIdentityWebApp or server-side OIDC patterns. Instead:
- MAUI handles sign-in via
IAuthService(MSAL.NET) - A custom
MsalAuthenticationStateProviderexposes auth state to Blazor HttpClientwithDelegatingHandlerattaches bearer tokens automatically
MSAL.NET Checklist
- [ ]
Microsoft.Identity.ClientNuGet package added - [ ] App registration created in Entra ID with correct redirect URIs
- [ ]
AuthConfig/appsettings.jsonhas ClientId, TenantId, Scopes - [ ] Android:
AndroidManifest.xmlhas `` for broker and browsers - [ ] Android:
MainActivity.OnActivityResultcallsAuthenticationContinuationHelper - [ ] iOS:
Info.plisthasCFBundleURLSchemeswithmsauth.{BundleId} - [ ] iOS:
Entitlements.plisthas keychain groupcom.microsoft.adalcache - [ ] iOS:
AppDelegate.OpenUrlcallsAuthenticationContinuationHelper - [ ]
IAuthServiceregistered as singleton in DI - [ ]
DelegatingHandlerattached toHttpClientfor API calls - [ ] Login/logout UI wired up
- [ ]
MsalUiRequiredExceptionhandled (triggers interactive sign-in) - [ ]
MsalClientExceptionwithauthentication_canceledhandled gracefully
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: davidortinau
- Source: davidortinau/maui-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.