要在 Capacitor 应用中通过 Auth0 登录,请创建一个 原生 在 Auth0 应用中,注册一个自定义 scheme 的回调 URL,例如 com.example.app://oauth/auth0,并通过系统浏览器运行带 PKCE 的 OpenID Connect 授权 code 流程。配合 @capgo/capacitor-social-login 即一个 initialize 使用你的 Auth0 端点发起一次调用,以及一次 SocialLogin.login({ provider: 'oauth2', options: { providerId: 'auth0' } }).
本指南涵盖 Auth0 dashboard 设置、iOS、Android 和 Web 的客户端 code、API access token、refresh token、登出,以及你会遇到的错误。内容基于 Capacitor 8,截至 2026 年 10 月。
Capacitor 中的 Auth0 流程原理
Auth0 托管登录页面 (Universal Login)。你的应用始终接触不到密码。流程如下:
- 应用生成 PKCE verifier 和 challenge,然后打开
https://YOUR_TENANT/authorize在安全浏览器中打开。 - 用户登录(密码、社交账号、passkey、MFA,或你在 Auth0 中配置的任何方式)。
- Auth0 会重定向到你的回调 URL,并携带一次性的
code. - 应用在以下地址交换 code 和 PKCE verifier:
/oauth/token用于 ID token、access token,以及可选的 refresh token。
使用的浏览器取决于平台:
| 平台 | 浏览器 | 回调处理 |
|---|---|---|
| iOS | ASWebAuthenticationSession |
由系统处理,无需 Info.plist 更改 |
| Android | 默认使用嵌入式 WebView,或使用 Chrome Custom Tabs 搭配 androidUseCustomTabs: true |
Custom Tabs 需要 intent filter |
| Web 端 | Popup(默认)或整页重定向 | handleRedirectCallback() 在返回页面上 |
在 Android 上,请使用 Custom Tabs。它们与 Chrome 共享 cookies,支持 passkeys 和密码管理器,并遵循 RFC 8252(原生应用的 OAuth),这正是 Auth0 所期望的。
步骤 1:设置 Auth0 应用
- 在 Auth0 控制台,前往 应用 > 应用 > 创建应用.
- 选择 原生 并创建。
- 在 设置 选项卡,记下 域名 (例如
example.eu.auth0.com) 和 客户端 ID. - 在 凭据,请确认认证方式为 无。这是与 PKCE 配合使用的公共客户端设置。
回调和登出 URL
请填写以下字段 设置 > 应用程序 URI:
| 字段 | 移动端值 | Web 端值 |
|---|---|---|
| 允许的回调 URL | com.example.app://oauth/auth0 |
https://app.example.com/auth/callback |
| 允许的登出 URL | com.example.app://oauth/logout |
https://app.example.com/ |
| 允许的 Web 源 | 无需配置 | https://app.example.com |
多个值请用逗号分隔。请使用您的 Capacitor appId 作为 scheme。反向域名 scheme 可避免与其他应用冲突。如果您还发布了 Web 版本,许多团队会为其单独创建一个 单页应用 ,以便 Web 和移动端的设置互不干扰。
Refresh Token
如果希望用户保持登录状态:
- 在 设置 > Refresh Token Rotation,开启轮换。每次刷新都会返回新的 refresh token 并使旧 token 失效。
- 设置符合您产品需求的绝对有效期和非活动有效期。
- 如果你调用自己的 API,请打开 应用程序 > API > 你的 API > 设置 并启用 允许离线访问.
- 请求
offline_access应用中的作用域。
API 受众
如果您的后端要验证 Auth0 access token,请在下面创建一个 API 应用 > API 并使用如下标识符: https://api.example.com。将该标识符作为 audience。没有它,access token 只对 Auth0 的 /userinfo 端点有效。
第 2 步:安装并配置插件
bun add @capgo/capacitor-social-login
bunx cap sync
如果 Auth0 是您唯一的提供商,请禁用内置的社交 SDK 以减小二进制体积:
// capacitor.config.ts
plugins: {
SocialLogin: {
providers: { google: false, facebook: false, apple: false, twitter: false },
},
},
通用的 OAuth2 引擎并不依赖这些 SDK。
用于 Custom Tabs 的 Android intent filter
将回调 scheme 添加到 MainActivity 中的 android/app/src/main/AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.example.app" android:host="oauth" />
</intent-filter>
Capacitor 模板已设置 android:launchMode="singleTask" 开启 MainActivity,重定向需要它。请保留。
步骤 3:初始化 Auth0 提供商
import { Capacitor } from '@capacitor/core';
import { SocialLogin } from '@capgo/capacitor-social-login';
const AUTH0_DOMAIN = 'https://example.eu.auth0.com';
const isWeb = Capacitor.getPlatform() === 'web';
export async function initAuth() {
await SocialLogin.initialize({
oauth2: {
auth0: {
appId: 'YOUR_AUTH0_CLIENT_ID',
authorizationBaseUrl: `${AUTH0_DOMAIN}/authorize`,
accessTokenEndpoint: `${AUTH0_DOMAIN}/oauth/token`,
resourceUrl: `${AUTH0_DOMAIN}/userinfo`,
redirectUrl: isWeb
? 'https://app.example.com/auth/callback'
: 'com.example.app://oauth/auth0',
scope: 'openid profile email offline_access',
pkceEnabled: true,
additionalParameters: {
audience: 'https://api.example.com',
},
logoutUrl: `${AUTH0_DOMAIN}/oidc/logout`,
postLogoutRedirectUrl: isWeb
? 'https://app.example.com/'
: 'com.example.app://oauth/logout',
androidUseCustomTabs: true,
},
},
});
}
你可以用以下代码替换显式端点: issuerUrl: AUTH0_DOMAIN。随后插件会读取 /.well-known/openid-configuration。显式值优先于自动发现的值,这在您使用 Auth0 自定义域名时非常有用。
如果您正从 Ionic Auth Connect 迁移,该插件还导出了 SocialLoginAuthConnect ,并带有 auth0 预设,该预设接受 domain, clientId, redirectUrl 以及 audience。查看 Auth Connect 迁移指南.
步骤 4:登录
import { SocialLogin } from '@capgo/capacitor-social-login';
export async function signInWithAuth0() {
try {
const { result } = await SocialLogin.login({
provider: 'oauth2',
options: { providerId: 'auth0' },
});
return {
idToken: result.idToken,
accessToken: result.accessToken?.token,
expiresAt: result.accessToken?.expires,
refreshToken: result.refreshToken,
user: result.resourceData, // JSON from /userinfo
};
} catch (error: any) {
if (error?.code === 'USER_CANCELLED') return null;
throw error;
}
}
常用的登录选项:
prompt: 'login'即使 Auth0 仍存有 session cookie,也会强制跳转登录页。loginHint: 'user@example.com'预填入邮箱地址。additionalParameters: { connection: 'google-oauth2' }跳过 Universal Login,直接进入特定连接。additionalParameters: { screen_hint: 'signup' }打开注册界面。
Web 重定向回调
在 Web 端,默认的 popup 流程无需额外配置。如果你使用 flow: 'redirect',请在回调页面读取结果:
const result = await SocialLogin.handleRedirectCallback();
if (result?.provider === 'oauth2') {
console.log(result.result.idToken);
}
handleRedirectCallback 仅支持 Web,在 iOS 和 Android 上会被拒绝。
步骤 5:读取 ID token 并调用您的 API
仅在客户端解码 ID token 用于展示:
const { claims } = await SocialLogin.decodeIdToken({ idToken: result.idToken! });
console.log(claims.email, claims.name, claims.sub);
decodeIdToken 不会验证签名。对于任何安全相关操作,请将 access token 发送到您的 API 并在那里进行验证:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(new URL('https://example.eu.auth0.com/.well-known/jwks.json'));
export async function verifyAuth0AccessToken(token: string) {
const { payload } = await jwtVerify(token, jwks, {
issuer: 'https://example.eu.auth0.com/', // Auth0 issuers end with a slash
audience: 'https://api.example.com',
});
return payload; // payload.sub is the Auth0 user ID
}
步骤 6:刷新 access token
import { SocialLogin } from '@capgo/capacitor-social-login';
export async function getValidAccessToken() {
const { isLoggedIn } = await SocialLogin.isLoggedIn({ provider: 'oauth2', providerId: 'auth0' });
if (!isLoggedIn) {
// access token expired: try the stored refresh token
try {
await SocialLogin.refresh({ provider: 'oauth2', options: { providerId: 'auth0' } });
} catch {
// refresh token expired, revoked or already rotated: sign in again
const loginResult = await signInWithAuth0();
if (!loginResult) throw new Error('Sign-in cancelled');
}
}
const { accessToken } = await SocialLogin.getAuthorizationCode({
provider: 'oauth2',
providerId: 'auth0',
});
return accessToken;
}
refresh 当 refresh token 过期或被吊销时会拒绝。如果启用了轮换,已使用过的 refresh token 也会失败,因此请务必处理拒绝情况,将用户重定向回登录页面。
refresh 使用插件在登录时保存的 refresh token。如果您自行管理 refresh token,请调用 SocialLogin.refreshToken({ provider: 'oauth2', providerId: 'auth0', refreshToken }),返回完整的最新 token 响应。启用轮换后,每次都要存储新的 refresh token。
该插件将 token 副本存储在应用私有存储中。如果安全审查要求使用 Keychain 或 Keystore,请使用 NativeBiometric.setData({ key, value }) 来自 @capgo/capacitor-native-biometric 并将其传递给 refreshToken 自己动手。我们的 安全 Token 存储指南 对比了各种方案。
第 7 步:退出登录
await SocialLogin.logout({ provider: 'oauth2', providerId: 'auth0' });
该插件会删除已存储的 Token,然后打开 logoutUrl 配合 id_token_hint 以及 post_logout_redirect_uri 以便 Auth0 清除其会话 Cookie。Auth0 的 /oidc/logout endpoint 能识别这些参数,且重定向目标必须列在 允许的登出 URL.
在 iOS 和 Android 上,end-session 页面会在系统浏览器中打开,因此用户会短暂离开 App。如果你只需要本地登出,请省略 logoutUrl 并传递 prompt: 'login' 在下次登录时生效,防止已记住的 Auth0 会话直接让用户重新登录。设置 iosPrefersEphemeralSession: true 同时避免了 iOS 上的共享 Cookie 问题,但代价是应用之间无法实现 SSO。
故障排除
| 错误 | 原因 | 修复 |
|---|---|---|
Callback URL mismatch |
重定向 URL 不在允许的回调 URL 列表中 | 将 code 中的确切值复制到 dashboard,并检查 scheme 和 host |
Unauthorized 或 invalid_client 占 /oauth/token |
App 类型不是 Native,或认证方式不是 None | 重新创建为 Native,并将 token endpoint 认证设置为 None |
否 refreshToken 结果中 |
offline_access 缺失或 API 不允许离线访问 |
添加 scope,启用 Allow Offline Access,并启用 rotation |
| API 无法验证 Access token | 否 audience 已发送 |
添加 audience 中的 additionalParameters |
| Android 端登录后 App 未重新打开 | Intent filter 的 scheme 或 host 不匹配 redirectUrl |
匹配 com.example.app 以及 oauth 完全 |
OAuth2 provider "auth0" not configured |
login 在...之前调用 initialize 或密钥不匹配 |
等待 initialize 在启动时,使用相同的 providerId |
| 登出页面保持打开 | post_logout_redirect_uri 不在 Allowed Logout URLs 中 |
请添加该 URL,或仅使用本地登出 |
| Token 交换时出现 Web CORS 错误 | Origin 不在 Allowed Web Origins 中 | 请添加该 Web Origin,或为 Web 使用 SPA 应用 |
设置 logsEnabled: true 在 provider 配置中,以便在调试时打印生成的 URL 和 token 交换详情。
无需商店发布即可推送认证修复
修改 scopes、audience 或错误处理仅涉及 TypeScript。使用 Capgo 实时更新 即可在同一天将这些修复推送给用户。像新增 intent filter 这样的原生更改仍需新的二进制文件,而 Capgo 构建 可以在云端生成。