关于 maui-authentication
为实现 OAuth 和 Microsoft Entra ID 登录的 .NET MAUI 开发者提供身份验证指导,说明安全令牌流程、平台配置陷阱,以及 WebAuthenticator 与 MSAL.NET 的适用场景。
主要功能
- 01安全的 OAuth 流程设计
- 02WebAuthenticator 配置与注意事项
- 03WebAuthenticator 和 MSAL.NET 选择指导
- 04MSAL.NET 平台配置检查
- 05Blazor Hybrid 身份验证模式
适用场景
- 01实现通用 OAuth 或 OIDC 登录
- 02添加 Microsoft Entra ID 身份验证
- 03排查回调 URI 不匹配
- 04处理 Blazor Hybrid 身份验证
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-authentication安装后在 Claude Code 中输入/maui-authentication确认已加载。
附带文件references/authentication-api.md
详细介绍
.NET MAUI 身份验证
安全:切勿嵌入机密信息
❌ 切勿在移动应用二进制文件中嵌入客户端机密、API 密钥或签名密钥。 通过反编译即可轻松提取这些信息。
正确的做法:
- 应用调用
WebAuthenticator,并将其指向你的服务端点 - 服务向身份提供方发起 OAuth 流程(由服务持有客户端机密)
- 提供方通过重定向将授权码返回给你的服务
- 服务用授权码交换令牌,并通过回调 URI 将令牌返回给应用
WebAuthenticator 的注意事项
⚠️ Windows WebAuthenticator 无法正常工作
Windows WebAuthenticator 目前无法正常工作。请参阅 dotnet/maui#2702。对于 Windows 身份验证流程,请使用 MSAL 或 WinUI 专用变通方案。
⚠️ Apple Sign In 仅返回一次姓名和电子邮件
Apple 仅在用户首次登录时返回其姓名和电子邮件。请立即缓存这些信息——后续登录不会包含这些信息。
⚠️ PrefersEphemeralWebBrowserSession
在 iOS 13+ 上将其设为 true,以强制显示新的登录提示。设为 false(默认值)时,身份验证会话会与 Safari 共享 Cookie——用户可能会自动登录,这会导致退出登录或切换帐户的流程令人困惑。
⚠️ 回调 URI 不匹配
最常见的身份验证失败原因是 URI 方案不匹配。代码中的 CallbackUrl 必须与以下配置完全匹配:
- Android:
IntentFilter中的DataScheme+DataHost - iOS:
Info.plist中的CFBundleURLSchemes - Windows:
Package.appxmanifest中的Protocol Name
WebAuthenticator 检查清单
- 所有平台配置中的回调 URI 方案均与
CallbackUrl匹配 - Android 配置了包含正确
IntentFilter的WebAuthenticatorCallbackActivity - Android 11+ 的清单中包含用于 Custom Tabs 的
<queries> - iOS/Mac Catalyst 的
Info.plist中包含CFBundleURLTypes - 客户端机密保存在服务端,而不是应用中
- 使用
SecureStorage存储令牌,并在退出登录时清除 - 在 UI 中妥善处理
TaskCanceledException
如何在 WebAuthenticator 和 MSAL.NET 之间进行选择
| 标准 | WebAuthenticator | MSAL.NET |
|---|---|---|
| 身份提供方 | 任意 OAuth 2.0 / OIDC | Microsoft Entra ID |
| Broker 支持(SSO) | ❌ 不支持 | ✅ Microsoft Authenticator、Company Portal |
| 条件访问 / MFA | ❌ 手动处理 | ✅ 内置 |
| 令牌缓存与刷新 | ❌ 手动处理(SecureStorage) | ✅ 自动处理 |
| 复杂度 | 简单 | 需要更多设置 |
| 适用场景 | Google、Apple、通用 OIDC | Entra ID / Azure AD、Microsoft Graph |
MSAL.NET 的注意事项
⚠️ Android:必须实现 OnActivityResult
如果忘记在 MainActivity.OnActivityResult 中调用 AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs,浏览器返回后身份验证会悄无声息地挂起。
⚠️ iOS:必须启用钥匙串共享
如果 Entitlements.plist 中没有包含钥匙串组 com.microsoft.adalcache,令牌缓存会悄无声息地失败,用户每次都会被要求登录。
⚠️ 处理 MsalUiRequiredException
当 AcquireTokenSilent 抛出 MsalUiRequiredException 时,表示缓存的令牌已过期,需要进行交互。请始终回退到 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);
}
⚠️ 妥善处理用户取消操作
// ✅ Don't treat cancellation as an error
catch (MsalClientException ex) when (ex.ErrorCode == "authentication_canceled")
{
return null; // User cancelled — not an error
}
⚠️ Blazor Hybrid:身份验证在 MAUI 层进行
在 MAUI Blazor Hybrid 应用中,身份验证必须在 MAUI 层(MSAL.NET)进行,而不是在 Blazor WebView 中进行。不要使用 AddMicrosoftIdentityWebApp 或服务端 OIDC 模式。请改为:
- MAUI 通过
IAuthService(MSAL.NET)处理登录 - 自定义
MsalAuthenticationStateProvider向 Blazor 提供身份验证状态 HttpClient使用DelegatingHandler自动附加持有者令牌
MSAL.NET 检查清单
- 已添加
Microsoft.Identity.ClientNuGet 程序包 - 已在 Entra ID 中创建应用注册,并设置正确的重定向 URI
-
AuthConfig/appsettings.json中包含 ClientId、TenantId、Scopes - Android:
AndroidManifest.xml中包含用于代理和浏览器的<queries> - Android:
MainActivity.OnActivityResult调用AuthenticationContinuationHelper - iOS:
Info.plist中的CFBundleURLSchemes包含msauth.{BundleId} - iOS:
Entitlements.plist中包含密钥链组com.microsoft.adalcache - iOS:
AppDelegate.OpenUrl调用AuthenticationContinuationHelper - 已在 DI 中将
IAuthService注册为单例 - 已为 API 调用将
DelegatingHandler附加到HttpClient - 已接入登录/注销 UI
- 已处理
MsalUiRequiredException(触发交互式登录) - 已妥善处理
authentication_canceled对应的MsalClientException