概述
app.auth() 默认返回 GoTrue 身份认证客户端,负责注册、登录、会话、第三方身份源和多因素认证。调用结果统一为 { data, error }:成功时 error 为 null,失败时 error 为 AuthError。init({ throw: true }) 时,认证失败改为抛出错误。
authMode 是 cloudbase.init 的参数,用来决定 app.auth() 的实现:
authMode?: 'supabase' | 'cloudbase' // 默认 'supabase'
authMode | 客户端 | 说明 |
|---|---|---|
supabase | GoTrue | 默认。本页接口按此模式编写 |
cloudbase | CloudBase Auth | 兼容模式,与 v3 身份认证 的 app.auth() 相同 |
基础使用示例
- 初始化
- 检查登录状态
- 邮箱注册
- 密码登录
- OAuth 登录
- 微信 OpenID 登录
- 退出登录
- 监听认证状态
Publishable Key 可在 云开发平台 / API Key 配置 生成。app.auth() 与 app.auth 指向同一个客户端,下文使用 app.auth()。
import cloudbase from "@cloudbase/js-sdk";
const app = cloudbase.init({
env: "your-env-id",
region: "ap-shanghai",
accessKey: "your-publishable-key",
auth: {
detectSessionInUrl: true, // 默认即为 true,关闭时设为 false
},
});
const auth = app.auth();
const { data, error } = await auth.getSession();
if (error) {
console.error(error.message);
} else if (data.session) {
console.log(data.session.user);
} else {
console.log("未登录");
}
开启邮箱确认时,signUp 返回用户但 session 为 null。用户提交验证码后,再调用 verifyOtp。
const { data, error } = await auth.signUp({
email: "user@example.com",
password: "securePassword123",
});
if (error) {
console.error(error.message);
} else if (!data.session) {
const verified = await auth.verifyOtp({
email: "user@example.com",
token: "123456",
type: "signup",
});
console.log(verified.data.session);
}
const { data, error } = await auth.signInWithPassword({
email: "user@example.com",
password: "securePassword123",
});
if (error) {
console.error(error.message);
} else {
console.log(data.session.access_token);
}
浏览器会立刻跳转到 GitHub。授权完成后回到 redirectTo,会话写在 URL hash 中。默认开启的 detectSessionInUrl 会写入会话。
const { error } = await auth.signInWithOAuth({
provider: "github",
options: {
redirectTo: window.location.origin,
scopes: "read:user user:email",
},
});
if (error) {
console.error(error.message);
}
先用 wx.login 取得 code,再和 appId 一起传入。成功后写入会话。
const { data, error } = await auth.signInWithOpenId({
code: "wx-login-code",
appId: "wx-app-id",
});
if (error) {
console.error(error.message);
} else {
console.log(data.session.access_token);
}
const { error } = await auth.signOut();
if (error) {
console.error(error.message);
}
const { data } = auth.onAuthStateChange((event, session) => {
console.log(event, session?.user?.id);
});
// 不再监听时调用
data.subscription.unsubscribe();
认证登录
signUp
signUp(credentials: SignUpWithPasswordCredentials): Promise<AuthResponse>
使用邮箱或手机号和密码注册。邮箱与手机号二选一。手机号使用“国家号+手机号”格式,例如 +8613800138000。
服务端开启确认时,返回的 user 有值、session 为 null,需要再用 verifyOtp 或邮件链接完成确认。确认关闭时,注册成功会直接写入会话并触发 SIGNED_IN。
options.data 写入 user_metadata。options.emailRedirectTo 只对邮箱生效。options.channel 只对手机号生效,取值为 sms 或 whatsapp,默认 sms。
参数
返回
示例
const { data, error } = await auth.signUp({
email: "user@example.com",
password: "securePassword123",
options: {
data: { name: "Ada" },
emailRedirectTo: "https://example.com/welcome",
},
});
signInAnonymously
signInAnonymously(credentials?: SignInAnonymouslyCredentials): Promise<AuthResponse>
创建匿名用户并建立会话。options.data 写入 user_metadata。匿名用户的 user.is_anonymous 为 true。
参数
返回
示例
const { data, error } = await auth.signInAnonymously();
signInWithPassword
signInWithPassword(credentials: SignInWithPasswordCredentials): Promise<AuthTokenResponsePassword>
使用邮箱或手机号和密码登录。不支持用户名。密码过弱时,成功结果里可能带 weakPassword。
参数
返回
示例
- 邮箱
- 手机号
const { data, error } = await auth.signInWithPassword({
email: "user@example.com",
password: "securePassword123",
});
const { data, error } = await auth.signInWithPassword({
phone: "+8613800138000",
password: "securePassword123",
});
signInWithOtp
signInWithOtp(credentials: SignInWithPasswordlessCredentials): Promise<AuthOtpResponse>
向邮箱或手机号发送一次性验证码或魔法链接。这个方法只负责发码,不会建立会话。用户提交验证码后调用 verifyOtp。
options.shouldCreateUser 默认为 true。设为 false 时,账号不存在则不会创建用户。options.emailRedirectTo 是邮件链接的跳转地址。
参数
返回
示例
const { error } = await auth.signInWithOtp({
email: "user@example.com",
options: { shouldCreateUser: true },
});
const verified = await auth.verifyOtp({
email: "user@example.com",
token: "123456",
type: "email",
});
verifyOtp
verifyOtp(params: VerifyOtpParams): Promise<AuthResponse>
校验邮箱或手机号验证码,或校验邮件链接中的 token_hash。成功后写入会话。
邮箱 type 常用值:signup、invite、magiclink、recovery、email_change、email。手机号 type 常用值:sms、phone_change。
参数
返回
示例
const { data, error } = await auth.verifyOtp({
email: "user@example.com",
token: "123456",
type: "email",
});
signInWithOAuth
signInWithOAuth(credentials: SignInWithOAuthCredentials): Promise<OAuthResponse>
跳转到第三方 OAuth 提供方。浏览器中默认立即跳转。options.skipBrowserRedirect: true 时不跳转,调用方使用返回的 data.url。
通过 cloudbase.init({ accessKey }) 传入的 Publishable Key 会作为请求的 apikey。auth.detectSessionInUrl 默认开启,回跳后从 URL 恢复会话。设为 false 则关闭。默认流程是 implicit,回调参数在 URL hash 中。改用 PKCE 时设置 auth.flowType: 'pkce',说明见 exchangeCodeForSession。
会话恢复后会触发 SIGNED_IN。provider 可以是内置提供方,也可以是 custom:<id> 形式的自定义 OIDC 提供方。
参数
返回
示例
const { data, error } = await auth.signInWithOAuth({
provider: "github",
options: {
redirectTo: window.location.origin,
scopes: "read:user user:email",
},
});
exchangeCodeForSession
exchangeCodeForSession(authCode: string, options?: { flowId?: string }): Promise<AuthTokenResponse>
用于 PKCE(Proof Key for Code Exchange,授权码交换证明密钥)流程,把查询参数中的 code 换成会话。
detectSessionInUrl 开启(默认即开启),且 auth.flowType 为 'pkce' 时,SDK 在初始化时自动换会话,不必调用本方法。
以下情况需要手动调用:detectSessionInUrl 为 false,或授权码不在当前页面的 URL 里。多个 PKCE 流程并发时,传入对应的 options.flowId。
默认 flowType 为 'implicit',不走本方法。两种流程的区别:
| 流程 | 回调里有什么 | 如何完成登录 |
|---|---|---|
| implicit | 令牌,在 URL hash 中 | 回跳后由 detectSessionInUrl 写入会话 |
| PKCE | code,在查询参数中 | flowType 为 'pkce' 且 detectSessionInUrl 开启时,SDK 自动换会话。关闭自动检测后,调用本方法 |
参数
PKCE 回调 URL 查询参数中的 code
返回数据
返回
示例
// detectSessionInUrl 为 false 时,自行取出 code 再换会话
const code = new URL(window.location.href).searchParams.get("code");
const { data, error } = await auth.exchangeCodeForSession(code);
signInWithIdToken
signInWithIdToken(credentials: SignInWithIdTokenCredentials): Promise<AuthTokenResponse>
使用第三方签发的 OIDC ID Token 登录。provider 支持 google、apple 等,以及自定义 OAuth 提供商的 custom:<id>。ID Token 的 iss 必须与该提供方匹配。Token 含 nonce 或 at_hash 时,需要同时传入对应的 nonce 或 access_token。
参数
返回
示例
const { data, error } = await auth.signInWithIdToken({
provider: "google",
token: idToken,
nonce,
});
signInWithSAML
signInWithSAML(credentials: SignInWithProviderUriCredentials): Promise<ProviderUriResponse>
向控制台已配置的 SAML 身份源发起登录。SDK 请求 GET /auth/v1/provider/uri,浏览器默认跳到返回的地址。
options.skipBrowserRedirect 为 true 时不跳转。调用方使用返回的 data.url。
options.redirectTo 是登录完成后回到的页面。不传则使用当前页。发给服务端的地址只保留 origin 与 pathname,不含查询参数和 hash。非浏览器环境要传入该字段,否则请求不带 redirect_uri。
options.state 原样带给身份源。不传则由 SDK 生成。返回值不含 data.flowId。
options.queryParams 里每个非空值会作为 other_params[key] 附在请求上。
参数
返回
示例
const { data, error } = await auth.signInWithSAML({
providerId: "saml-provider-id",
options: { redirectTo: "https://example.com/welcome" },
});
signInWithSSO
signInWithSSO(params: SignInWithSSO): Promise<SSOResponse>
通过 SSO 提供方的 UUID 或企业邮箱域名发起单点登录。浏览器中默认跳转。options.skipBrowserRedirect: true 时返回 data.url,由调用方自行跳转。
参数
返回
示例
const { data, error } = await auth.signInWithSSO({
domain: "company.com",
options: { redirectTo: window.location.origin },
});
signInWithCAS
signInWithCAS(credentials: SignInWithProviderUriCredentials): Promise<ProviderUriResponse>
向控制台已配置的 CAS 身份源发起登录。请求与跳转和 signInWithSAML 相同。
浏览器回到 redirect_uri?state=...&ticket=...。state 携带流程 ID,方法把它作为 data.flowId 返回。有 redirect_uri 时,还会把这次登录记到客户端存储。
detectSessionInUrl 默认开启。页面再次加载且能读到这条记录时,SDK 用 URL 里的 ticket 换会话,并清掉记录。成功后触发 SIGNED_IN。
要自己换票据时,创建客户端时把 detectSessionInUrl 设为 false,或设置 skipAutoInitialize: true。然后调用 exchangeTicketForSession。自动换过之后再调,票据已经用掉,存储里的字段也已清除。
options.state 同时充当流程 ID:
- 不传时,SDK 生成流程 ID,并把它当作
state - 传入值匹配
[A-Za-z0-9_-]{8,64}时,该值就是流程 ID - 传入其他值时,SDK 另生成流程 ID,并用
.接在原值前面。回跳后仍能从state里取出流程 ID
redirectTo、skipBrowserRedirect、queryParams 的含义与 signInWithSAML 相同。
参数
返回
示例
const { data, error } = await auth.signInWithCAS({
providerId: "cas-provider-id",
});
// data.flowId 用来在换票据时选中这次登录
exchangeTicketForSession
exchangeTicketForSession(ticket: string, options?: ExchangeTicketForSessionOptions): Promise<AuthTokenResponse>
用 CAS 服务票据换会话。请求 POST /auth/v1/signin/with/provider。成功后写入会话,并触发 SIGNED_IN。
detectSessionInUrl 开启时,SDK 在初始化时已经换过票据,不必调用本方法。
以下情况需要手动调用:detectSessionInUrl 为 false,或票据不在当前页面的 URL 里。多个 CAS 流程并发时,传入对应的 options.flowId。
未传的 providerId、redirectUri、state 从 signInWithCAS 记下的登录里读取。传入 flowId 后,不再用 URL 里的 state 去匹配。不传 flowId 时,先从当前 URL 的 state 解析,再回退到最近一次记录。
换了标签页、来源或设备,关闭了会话持久化,或这次回调已经处理过时,记录可能读不到。这时把上述字段显式传入。
redirectUri 必须是发给身份源的地址,即 origin 加 pathname,不能带 ticket。服务端用 redirectUri?state=... 还原签发票据时的 CAS service。
参数
CAS 回跳 URL 查询参数中的 ticket
返回数据
返回
示例
// 创建客户端时设置 detectSessionInUrl: false,再自行取出 ticket
const ticket = new URL(window.location.href).searchParams.get("ticket");
if (ticket) {
const { data, error } = await auth.exchangeTicketForSession(ticket);
}
signInWithCustomTicket
signInWithCustomTicket(credentials: SignInWithCustomTicketCredentials): Promise<AuthTokenResponse>
使用自定义登录票据登录。成功后写入会话,并触发 SIGNED_IN。
ticket 形如 <keyId>/<anything>/<JWT>,由业务私钥签发。该私钥对应控制台已配置的自定义登录公钥。服务端用 createTicket 签发。
参数
返回
示例
const { data, error } = await auth.signInWithCustomTicket({
ticket,
});
createTicket
async auth.createTicket(uid: string, options?: ICreateTicketOpts): Promise<string>
创建自定义登录凭据 Ticket,使用 RSA 私钥签发 JWT,客户端凭此 Ticket 可换取登录态。
- 仅适用于 Node.js 服务端(云函数、云托管、自建服务器等),私钥严禁写入小程序、H5 等前端环境
- 使用前需在
init时传入自定义登录私钥credentials,该字段是cloudbase.init()的顶层配置,属于 Node.js 端专属参数,不要将其放在auth下 - 前往 云开发平台/身份认证/登录方式,通过“自定义登录”下载私钥文件
- 需安装依赖:
npm install jsonwebtoken
credentials 对象包含以下字段(均来自下载的私钥文件 tcb_custom_login.json,无需手动填写):
| 字段 | 说明 |
|---|---|
env_id | 私钥所属的环境 ID |
private_key_id | 私钥标识,签发的 Ticket 中会携带此 ID 用于校验 |
private_key | RSA 私钥内容,用于签发 JWT,请务必妥善保管勿泄漏 |
参数
开发者自定义的用户唯一 ID(4~32 位,支持字母数字和部分特殊字符)
可选配置
返回
返回格式为 "{private_key_id}/@@/{jwt_token}" 的票据字符串
示例
- 基本用法
- TypeScript(读取私钥文件)
const cloudbase = require("@cloudbase/js-sdk");
const app = cloudbase.init({
env: "your-env-id",
// credentials 是 init() 的顶层配置,不要放在 auth 下
credentials: require("/path/to/tcb_custom_login.json"),
});
exports.main = async (event, context) => {
const ticket = await app.auth.createTicket("custom-user-id-123", {
refresh: 3600 * 1000, // 1 小时刷新
});
console.log(ticket);
// 将 ticket 返回给客户端,客户端调用 signInWithCustomTicket 完成登录
};
import cloudbase from "@cloudbase/js-sdk";
import { readFileSync } from "node:fs";
// 从文件或环境变量读取私钥(私钥文件内容形如 { env_id, private_key_id, private_key })
const credentials = JSON.parse(
readFileSync(process.env.CLOUDBASE_CREDENTIALS_PATH!, "utf-8")
);
const app = cloudbase.init({
env: process.env.CLOUDBASE_ENV_ID, // 环境 ID 在顶层
credentials, // credentials 同样在顶层,无需自定义类型
});
export const main = async (event: unknown, context: unknown) => {
const ticket = await app.auth.createTicket("custom-user-id-123", {
refresh: 3600 * 1000, // 1 小时刷新
});
// 将 ticket 返回给客户端,客户端调用 signInWithCustomTicket 完成登录
return { ticket };
};
credentials 中的 private_key 是证明管理员身份的敏感凭证,只能在服务端使用,严禁写入小程序、H5 或任何前端环境变量中,避免泄漏。
signInWithOpenId
signInWithOpenId(credentials?: SignInWithOpenIdCredentials): Promise<AuthTokenResponse>
使用微信 OpenID 登录。成功后写入会话,并触发 SIGNED_IN。
小程序、第三方平台、公众号和开放平台,传入 wx.login 返回的 code 和对应的 appId。云开发免登录可以不传 code,网关会补齐身份请求头。这种情况下 access token 可能没有 refresh token,会话不能刷新。
参数
返回
示例
const { data, error } = await auth.signInWithOpenId({
code: "wx-login-code",
appId: "wx-app-id",
});
signInWithPhoneAuth
signInWithPhoneAuth(credentials: SignInWithPhoneAuthCredentials): Promise<AuthTokenResponse>
使用微信小程序手机号授权登录。phoneCode 来自 wx.getPhoneNumber。成功后写入会话,并触发 SIGNED_IN。
providerId 写入授权票据,默认与 appId 相同。
参数
返回
示例
const { data, error } = await auth.signInWithPhoneAuth({
phoneCode: "wx-phone-code",
appId: "wx-app-id",
});
会话管理
会话默认保存在本地存储,并自动刷新 access token。同一环境下存储键为 cloudbase-auth-js-<env>。
getSession
getSession(): Promise<{ data: { session: Session | null }; error: AuthError | null }>
读取当前本地会话。access token 临近过期时会先用 refresh token 刷新,再返回新会话。没有登录态时 data.session 为 null。
const { data, error } = await auth.getSession();
const token = data.session?.access_token;
refreshSession
refreshSession(currentSession?: { refresh_token: string }): Promise<AuthResponse>
使用 refresh token 换取新的会话。不传参数时使用本地会话中的 refresh token。
const { data, error } = await auth.refreshSession();
setSession
setSession(currentSession: { access_token: string; refresh_token: string }): Promise<AuthResponse>
用已有的 access token 和 refresh token 恢复会话,并写入本地存储。
参数
返回
示例
const { data, error } = await auth.setSession({
access_token,
refresh_token,
});
signOut
signOut(options?: { scope?: "global" | "local" | "others" }): Promise<{ error: AuthError | null }>
退出登录。scope 默认 global。
| scope | 行为 |
|---|---|
global | 注销该账号的全部会话,并清除本地会话 |
local | 只清除本地会话 |
others | 注销除当前会话以外的会话,当前页面不会收到 SIGNED_OUT |
const { error } = await auth.signOut({ scope: "local" });
用户管理
getUser
getUser(jwt?: string): Promise<UserResponse>
向认证服务请求当前用户。传入 jwt 时校验该 token,否则使用本地会话的 access token。这个方法每次都请求服务端,适合读取最新的邮箱确认状态和身份源。
const { data, error } = await auth.getUser();
console.log(data.user?.email, data.user?.user_metadata);
updateUser
updateUser(attributes: UserAttributes, options?: { emailRedirectTo?: string }): Promise<UserResponse>
更新当前用户的邮箱、手机号、密码或 user_metadata。调用前必须已有会话。
修改邮箱或手机号后,新值要等对应用户完成验证才会生效,期间可以在 user.new_email / user.new_phone 看到待确认值。修改密码时如果服务端要求重新认证,先调用 reauthenticate,再把验证码作为 nonce 传入。
参数
返回数据
返回
示例
- 更新资料
- 修改密码
const { data, error } = await auth.updateUser({
data: { name: "Ada" },
});
const { error: reauthError } = await auth.reauthenticate();
if (reauthError) throw reauthError;
const { data, error } = await auth.updateUser({
password: "new-password",
nonce: "123456",
});
身份源管理
getUserIdentities
getUserIdentities(): Promise<{ data: { identities: UserIdentity[] } | null; error: AuthError | null }>
返回当前用户已绑定的身份源。需要已登录。
const { data, error } = await auth.getUserIdentities();
data?.identities.forEach((identity) => {
console.log(identity.provider, identity.identity_id);
});
linkIdentity
linkIdentity(credentials: SignInWithOAuthCredentials): Promise<OAuthResponse>
linkIdentity(credentials: SignInWithIdTokenCredentials): Promise<AuthTokenResponse>
给当前用户绑定一个 OAuth 提供方或一条 ID Token 身份。OAuth 绑定同样会跳转。detectSessionInUrl 默认开启,回跳后恢复会话。设为 false 则关闭。参数与 signInWithOAuth、signInWithIdToken 相同。
const { data, error } = await auth.linkIdentity({
provider: "github",
options: { redirectTo: window.location.href },
});
unlinkIdentity
unlinkIdentity(identity: UserIdentity): Promise<{ data: {} | null; error: AuthError | null }>
解绑一条身份源。参数是 getUserIdentities 返回的对象,不是 provider 字符串。账号只剩一个身份源时,服务端返回 single_identity_not_deletable。
const { data, error } = await auth.getUserIdentities();
const github = data?.identities.find((item) => item.provider === "github");
if (github) {
await auth.unlinkIdentity(github);
}
密码管理
resetPasswordForEmail
resetPasswordForEmail(email: string, options?: { redirectTo?: string; captchaToken?: string }): Promise<{ data: {} | null; error: AuthError | null }>
向邮箱发送重置密码邮件。用户打开链接后,客户端会触发 PASSWORD_RECOVERY。在该事件的回调里调用 updateUser({ password }) 设置新密码。
const { error } = await auth.resetPasswordForEmail("user@example.com", {
redirectTo: "https://example.com/reset-password",
});
auth.onAuthStateChange(async (event) => {
if (event === "PASSWORD_RECOVERY") {
await auth.updateUser({ password: "new-password" });
}
});
reauthenticate
reauthenticate(): Promise<AuthResponse>
向当前用户已确认的邮箱或手机号发送一次验证码,用于修改密码等敏感操作。拿到验证码后,把它作为 nonce 传给 updateUser。
const { error } = await auth.reauthenticate();
验证管理
resend
resend(credentials: ResendParams): Promise<AuthOtpResponse>
重发注册或变更联系方式的验证码。
| type | 必填字段 | 说明 |
|---|---|---|
signup | email | 重发注册确认 |
email_change | email | 重发邮箱变更确认 |
sms | phone | 重发短信验证码 |
phone_change | phone | 重发手机号变更确认 |
const { error } = await auth.resend({
type: "signup",
email: "user@example.com",
});
校验验证码使用 verifyOtp。
多因素认证
auth.mfa 管理 TOTP、手机号和 WebAuthn 因子。注册因子后状态是 unverified,校验通过后变为 verified,当前会话的认证保障级别提升到 aal2,其他会话会被登出。
登录后如果 getAuthenticatorAssuranceLevel 显示 nextLevel 为 aal2 而 currentLevel 为 aal1,需要先 challenge 再 verify。
mfa.enroll
auth.mfa.enroll(params: MFAEnrollParams): Promise<AuthMFAEnrollResponse>
注册一个未验证的因子。TOTP 的返回值包含 totp.qr_code、totp.secret 和 totp.uri,把二维码或密钥交给用户的验证器应用。
参数
返回
示例
const { data, error } = await auth.mfa.enroll({
factorType: "totp",
friendlyName: "Authenticator",
issuer: "CloudBase",
});
mfa.challenge
auth.mfa.challenge(params: { factorId: string }): Promise<AuthMFAChallengeResponse>
对指定因子发起一次挑战。手机号因子会发送短信。返回的 id 是挑战 ID。
const { data, error } = await auth.mfa.challenge({ factorId });
mfa.verify
auth.mfa.verify(params: MFAVerifyParams): Promise<AuthMFAVerifyResponse>
提交验证器或短信中的验证码。成功后会话升级为 aal2,并触发 MFA_CHALLENGE_VERIFIED。
const { data, error } = await auth.mfa.verify({
factorId,
challengeId,
code: "123456",
});
mfa.challengeAndVerify
auth.mfa.challengeAndVerify(params: { factorId: string; code: string }): Promise<AuthMFAVerifyResponse>
对 TOTP 因子一次完成挑战和校验。手机号因子需要用户先收到短信,请分开调用 challenge 和 verify。
const { data, error } = await auth.mfa.challengeAndVerify({
factorId,
code: "123456",
});
mfa.unenroll
auth.mfa.unenroll(params: { factorId: string }): Promise<AuthMFAUnenrollResponse>
删除一个因子。
const { error } = await auth.mfa.unenroll({ factorId });
mfa.listFactors
auth.mfa.listFactors(): Promise<AuthMFAListFactorsResponse>
列出当前用户的因子。返回值按 totp、phone、all 分组,并区分已验证和未验证。
const { data, error } = await auth.mfa.listFactors();
const factorId = data?.totp[0]?.id;
mfa.getAuthenticatorAssuranceLevel
auth.mfa.getAuthenticatorAssuranceLevel(): Promise<AuthMFAGetAuthenticatorAssuranceLevelResponse>
读取当前会话的认证保障级别。currentLevel 是当前级别,nextLevel 是用户已注册因子所能达到的级别。currentLevel === "aal1" 且 nextLevel === "aal2" 时,应进入 MFA 挑战。
const { data, error } = await auth.mfa.getAuthenticatorAssuranceLevel();
if (data?.nextLevel === "aal2" && data.currentLevel !== data.nextLevel) {
// 发起 mfa.challenge
}
其他工具
onAuthStateChange
onAuthStateChange(
callback: (event: AuthChangeEvent, session: Session | null) => void
): { data: { subscription: { unsubscribe: () => void } } }
监听认证状态。回调参数是事件名和会话,没有第三个参数。
| 事件 | 时机 |
|---|---|
INITIAL_SESSION | 客户端完成本地会话恢复 |
SIGNED_IN | 登录或 OAuth 回调恢复会话 |
SIGNED_OUT | 当前会话被清除 |
TOKEN_REFRESHED | access token 已刷新 |
USER_UPDATED | 用户资料已更新 |
PASSWORD_RECOVERY | 用户从重置密码链接返回 |
MFA_CHALLENGE_VERIFIED | MFA 挑战校验通过 |
const { data } = auth.onAuthStateChange((event, session) => {
if (event === "SIGNED_OUT") {
console.log("已退出");
}
});
getClaims
getClaims(jwt?: string): Promise<{
data: { claims: JwtPayload; header: JwtHeader; signature: Uint8Array } | null;
error: AuthError | null;
}>
校验 JWT 并返回声明。不传 jwt 时使用当前会话的 access token。非对称签名的 JWT 会通过 JWKS 校验。
const { data, error } = await auth.getClaims();
console.log(data?.claims.sub, data?.claims.role);
错误码
error.code 是服务端错误码,error.status 是 HTTP 状态码,error.message 是说明。服务端可能返回下表以外的错误码。
登录与注册
| code | 说明 |
|---|---|
invalid_credentials | 邮箱、手机号或密码不正确 |
email_not_confirmed | 邮箱尚未确认 |
phone_not_confirmed | 手机号尚未确认 |
email_exists | 邮箱已注册 |
phone_exists | 手机号已注册 |
user_already_exists | 用户已存在 |
user_not_found | 用户不存在 |
user_banned | 用户被封禁 |
signup_disabled | 注册已关闭 |
email_provider_disabled | 邮箱登录未开启 |
phone_provider_disabled | 手机号登录未开启 |
anonymous_provider_disabled | 匿名登录未开启 |
provider_disabled | 该身份提供方未开启 |
weak_password | 密码强度不足 |
same_password | 新密码与当前密码相同 |
验证码与会话
| code | 说明 |
|---|---|
otp_expired | 验证码过期 |
otp_disabled | 验证码登录未开启 |
over_request_rate_limit | 请求过于频繁 |
over_email_send_rate_limit | 邮件发送过于频繁 |
over_sms_send_rate_limit | 短信发送过于频繁 |
sms_send_failed | 短信发送失败 |
session_not_found | 会话不存在 |
session_expired | 会话已过期 |
refresh_token_not_found | 找不到 refresh token |
refresh_token_already_used | refresh token 已使用 |
bad_jwt | JWT 无效 |
bad_code_verifier | PKCE code verifier 不匹配 |
flow_state_expired | 登录流程已过期 |
reauthentication_needed | 敏感操作前需要重新认证 |
reauthentication_not_valid | 重新认证的验证码无效 |
reauth_nonce_missing | 缺少重新认证 nonce |
OAuth、身份源与 MFA
| code | 说明 |
|---|---|
oauth_provider_not_supported | 不支持该 OAuth 提供方 |
bad_oauth_state | OAuth state 无效 |
bad_oauth_callback | OAuth 回调无效 |
identity_already_exists | 身份源已绑定到其他用户 |
identity_not_found | 找不到该身份源 |
single_identity_not_deletable | 不能删除最后一个身份源 |
manual_linking_disabled | 未开启手动绑定身份源 |
mfa_verification_failed | MFA 验证码错误 |
mfa_challenge_expired | MFA 挑战已过期 |
mfa_factor_not_found | 找不到该 MFA 因子 |
too_many_enrolled_mfa_factors | MFA 因子数量达到上限 |
insufficient_aal | 当前认证保障级别不足 |
完整类型定义
User
interface User {
id: string;
email?: string;
phone?: string;
role?: string;
aud: string;
app_metadata: { provider?: string; providers?: string[]; [key: string]: any };
user_metadata: { [key: string]: any };
identities?: UserIdentity[];
factors?: Factor[];
is_anonymous?: boolean;
is_sso_user?: boolean;
confirmed_at?: string;
email_confirmed_at?: string;
phone_confirmed_at?: string;
last_sign_in_at?: string;
created_at: string;
updated_at?: string;
new_email?: string;
new_phone?: string;
}
Session
interface Session {
access_token: string;
refresh_token: string;
expires_in: number;
expires_at?: number;
token_type: "bearer";
user: User;
provider_token?: string | null;
provider_refresh_token?: string | null;
}
UserIdentity
interface UserIdentity {
id: string;
user_id: string;
identity_id: string;
provider: string;
identity_data?: { [key: string]: any };
created_at?: string;
last_sign_in_at?: string;
updated_at?: string;
}
AuthError
class AuthError extends Error {
code: string | undefined;
status: number | undefined;
name: "AuthError" | "AuthApiError";
}
AuthChangeEvent
type AuthChangeEvent =
| "INITIAL_SESSION"
| "PASSWORD_RECOVERY"
| "SIGNED_IN"
| "SIGNED_OUT"
| "TOKEN_REFRESHED"
| "USER_UPDATED"
| "MFA_CHALLENGE_VERIFIED";