关于 auth0-net-android
为 Agent 提供在 .NET Android 应用中集成 Auth0 身份验证的指导,使用 `Auth0.OidcClient.AndroidX`。内容涵盖 Chrome Custom Tabs 中的 PKCE 流程、回调处理、安全令牌存储和构建验证。
主要功能
- 01.NET Android 的 Auth0 集成指导
- 02PKCE 登录与注销流程
- 03IntentFilter 和回调配置
- 04安全令牌存储指导
- 05构建与设备测试清单
适用场景
- 01在 .NET Android 应用中集成 Auth0 登录
- 02配置 Activity 回调处理
- 03使用安全存储保存令牌
- 04在设备上验证身份验证流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ auth0/ agent-skills --skill auth0-net-android安装后在 Claude Code 中输入/auth0-net-android确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api.mdintegration.mdsetup.md详细介绍
auth0-net-android 集成
为 .NET Android 应用添加 Auth0 身份验证。此技能集成 Auth0.OidcClient.AndroidX NuGet 包,该包使用 Chrome Custom Tabs,通过 PKCE 实现安全的基于 OIDC 的登录和注销流程。
Agent 指令: 在提供 SDK 设置说明之前,运行以下命令获取最新发布版本:
gh api repos/auth0/auth0-oidc-client-net/releases --jq '[.[] | select(.tag_name | startswith("androidx-")) | .tag_name | ltrimstr("androidx-")] | .[0]'在所有依赖项行中使用返回的版本,而不要使用下文中任何硬编码版本。
先决条件
- .NET 8.0 SDK 或更高版本
- Visual Studio 2022(17.8+)并安装 .NET Android 工作负载,或 JetBrains Rider
- Android SDK API 21+(最低要求);建议使用适用于 net9.0-android 的 API 34+
- 已配置 Native 应用的 Auth0 账户
- Node.js 20+(用于通过 Auth0 CLI 自动设置)
不适用场景
| 使用场景 | 推荐技能 |
|---|---|
| .NET MAUI 跨平台应用(iOS + Android + Windows) | auth0-maui |
| 仅适用于 .NET iOS 的应用 | auth0-net-ios |
| 仅适用于 Android 的 Kotlin 应用 | auth0-android |
| ASP.NET Core 服务端 Web 应用 | auth0-aspnetcore-authentication |
| ASP.NET Core Web API(JWT 验证) | auth0-aspnetcore-api |
| React Native 移动应用 | auth0-react-native |
快速入门工作流
Agent 指令: 开始之前,检查用户的项目:
- 从
.csproj文件中的TargetFramework确认 .NET 版本- 检查现有的身份验证实现——搜索现有的登录/注销处理程序;如果找到,则接入这些处理程序(复用现有 UI 元素,例如登录按钮,而不要重复创建)
- 从
.csproj或AndroidManifest.xml中记录项目的命名空间和包名- 查找现有的
Auth0Client或Auth0ClientOptions用法,以避免重复配置
- 安装 SDK:
dotnet add package Auth0.OidcClient.AndroidX - 配置 Auth0:有关自动或手动配置,请参阅设置指南。
- 集成身份验证:添加
Auth0Client实例化代码,在 Activity 上配置IntentFilter,并将登录/注销操作绑定到 UI 操作。 - 处理回调:重写
OnNewIntent,并调用ActivityMediator.Instance.Send(intent.DataString)以完成身份验证流程。 - 构建并验证:
dotnet build
Agent 指令: 编写 Auth0Client 配置时:
- 将
this(Activity)作为第二个参数传递给Auth0Client构造函数。- 务必设置
Scope = "openid profile email offline_access"——接收刷新令牌需要offline_access范围,这样才能在不再次提示用户的情况下静默续期令牌。- 回调 URL 格式为
YOUR_ANDROID_PACKAGE_NAME://YOUR_AUTH0_DOMAIN/android/YOUR_ANDROID_PACKAGE_NAME/callback——全部使用小写字母。IntentFilter中的DataScheme必须为小写,否则 Android 将无法接收回调。- 在 Activity 上设置
LaunchMode = LaunchMode.SingleTask,以防止创建重复实例。不要使用SingleTop——它无法正确处理回调重定向,并会创建重复的 Activity 实例。- Activity 应继承
Auth0ClientActivity,或者手动重写OnNewIntent并调用ActivityMediator.Instance.Send(intent.DataString)。- 安全存储令牌:成功登录后,使用
SecureStorage(MAUI/Essentials)或EncryptedSharedPreferences(AndroidX Security——需要运行dotnet add package Xamarin.AndroidX.Security.SecurityCrypto)持久化AccessToken和RefreshToken。切勿将令牌以明文形式存储在SharedPreferences中,也不要只存储在内存变量中。编写配置和代码后,验证构建是否成功:
dotnet build如果构建失败,请尝试修复问题。失败 5-6 次后,请向用户寻求帮助。
WebAuth — 身份验证的工作原理
SDK 通过 Chrome Custom Tabs(系统浏览器)使用 WebAuth 模式。调用 LoginAsync() 时,SDK 会:
- 使用 PKCE 参数构建
/authorizeURL - 使用授权 URL 打开 Chrome Custom Tabs
- 身份验证完成后,Auth0 会重定向到原生回调 URL
- Android 系统匹配 URL 方案,并通过
OnNewIntent将其传递给 Activity ActivityMediator完成令牌交换
这是 OAuth 2.0 授权码流与 PKCE 的标准流程,推荐用于原生移动应用。
回调 URL 配置
.NET Android 的原生回调 URL 使用包名作为方案。格式如下:
YOUR_ANDROID_PACKAGE_NAME://YOUR_AUTH0_DOMAIN/android/YOUR_ANDROID_PACKAGE_NAME/callback
其中,YOUR_ANDROID_PACKAGE_NAME 是应用的包名,例如 com.mycompany.myapplication。例如:com.mycompany.myapp://tenant.us.auth0.com/android/com.mycompany.myapp/callback。
注意: 某些 Auth0 原生 SDK 使用
https://{domain}/android/{package}/callback作为回调 URL 格式。 .NET Android SDK 则使用包名作为 URL 方案。
确保此回调 URL 使用小写字母。
此 URL 必须:
- 在 Auth0 Dashboard 的 Allowed Callback URLs 和 Allowed Logout URLs 中注册
- 与 Activity 上
IntentFilter的属性(DataScheme、DataHost、DataPathPrefix)匹配
完成标准
- 已安装
Auth0.OidcClient.AndroidX包(最新稳定版本) - 已使用 Domain、ClientId 和
Scope = "openid profile email offline_access"配置Auth0Client - 已在 Activity 上配置
IntentFilter,并设置正确的 DataScheme、DataHost、DataPathPrefix - 已在 Activity 上设置
LaunchMode = LaunchMode.SingleTask - 已通过
ActivityMediator.Instance.Send(intent.DataString)处理OnNewIntent - 已将回调 URL 添加到 Auth0 Dashboard 的 Allowed Callback URLs 和 Allowed Logout URLs
- 令牌已安全存储(SecureStorage 或 EncryptedSharedPreferences)
- 登录/注销流程正常运行
- 构建成功且无错误
详细文档
- 设置指南 — Auth0 租户配置、SDK 安装、IntentFilter 设置
- 集成模式 — 登录/注销流程、令牌访问、用户资料、错误处理
- API 参考与测试 — 完整的
Auth0ClientOptions参考、声明、测试清单、故障排查
常见错误
| 错误 | 修复方法 |
|---|---|
| Auth0 Dashboard 中的应用类型未设置为 Native | 在 Dashboard 设置中将应用类型更改为 "Native" |
| Auth0 Dashboard 中缺少回调 URL | 将 yourpackagename://{domain}/android/yourpackagename/callback 添加到 Allowed Callback URLs 和 Allowed Logout URLs |
DataScheme 未使用小写字母 | Android 要求方案使用小写字母 — 请使用小写包名 |
缺少 LaunchMode.SingleTask | 在 Activity 上设置 LaunchMode = LaunchMode.SingleTask,以防止创建重复实例 |
未处理 OnNewIntent | 重写 OnNewIntent 并调用 ActivityMediator.Instance.Send(intent.DataString) |
Domain 使用了 https:// 前缀 | Domain 应仅为主机名(例如 tenant.auth0.com,而不是 https://tenant.auth0.com) |
| 未向 Auth0Client 传递 Activity 上下文 | 将 this 作为第二个参数传递:new Auth0Client(options, this) |
| IntentFilter 的 DataHost/DataPathPrefix 不匹配 | 确保 DataHost 与 Auth0 域名匹配,且 DataPathPrefix 为 /android/yourpackagename/callback |
缺少 offline_access 作用域 | 始终在 Scope 中包含 offline_access,以接收用于静默续期的刷新令牌 |
使用 LaunchMode.SingleTop 而不是 SingleTask | 必须使用 LaunchMode.SingleTask — SingleTop 无法正确处理 Auth0 回调重定向 |
| 将令牌存储在普通 SharedPreferences 中 | 使用 AndroidX Security 库中的 SecureStorage 或 EncryptedSharedPreferences |
测试说明
Agent 指令: 提醒用户除了模拟器,还要在实体设备上进行测试。某些 WebAuth 行为(Chrome Custom Tabs、URL 方案拦截)在实体设备和模拟器上可能有所不同。发布前,请在真实硬件上测试完整的登录 → 回调 → 令牌流程。
实体设备测试:
- 登录流程:打开 Chrome Custom Tab → 完成身份验证 → 返回应用
- 回调:
OnNewIntent使用正确的 intent 数据触发 - 注销流程:打开浏览器 → 清除会话 → 返回应用
- 取消:用户按下返回键 → 应用妥善处理
UserCancel
相关技能
- auth0-maui — .NET MAUI 跨平台应用(iOS + Android + Windows)
- auth0-net-ios — 仅适用于 .NET iOS 的应用
- auth0-android — Android 原生 Kotlin 应用
- auth0-aspnetcore-authentication — ASP.NET Core 服务端 Web 应用
- auth0-aspnetcore-api — 带 JWT 验证的 ASP.NET Core Web API
快速参考
using Auth0.OidcClient;
var client = new Auth0Client(new Auth0ClientOptions
{
Domain = "YOUR_AUTH0_DOMAIN",
ClientId = "YOUR_AUTH0_CLIENT_ID",
Scope = "openid profile email offline_access"
}, this);
登录
var loginResult = await client.LoginAsync();
处理错误
var loginResult = await client.LoginAsync();
if (loginResult.IsError)
{
Debug.WriteLine($"An error occurred during login: {loginResult.Error}");
}
访问令牌
var loginResult = await client.LoginAsync();
if (!loginResult.IsError)
{
Debug.WriteLine($"Authentication successful.");
}
用户信息
if (!loginResult.IsError)
{
Debug.WriteLine($"name: {loginResult.User.FindFirst(c => c.Type == "name")?.Value}");
Debug.WriteLine($"email: {loginResult.User.FindFirst(c => c.Type == "email")?.Value}");
}
列出所有声明
if (!loginResult.IsError)
{
foreach (var claim in loginResult.User.Claims)
{
Debug.WriteLine($"{claim.Type} = {claim.Value}");
}
}
注销
BrowserResultType browserResult = await client.LogoutAsync();
带 IntentFilter 的 Activity(必需)
[Activity(Label = "AndroidSample", MainLauncher = true, Icon = "@drawable/icon",
LaunchMode = LaunchMode.SingleTask)]
[IntentFilter(
new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataScheme = "YOUR_ANDROID_PACKAGE_NAME",
DataHost = "YOUR_AUTH0_DOMAIN",
DataPathPrefix = "/android/YOUR_ANDROID_PACKAGE_NAME/callback")]
public class MainActivity : Activity
{
// Code omitted
}
在 OnNewIntent 中处理回调(必需)
protected override async void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
Auth0.OidcClient.ActivityMediator.Instance.Send(intent.DataString);
}