密钥认证
Allow users to sign in with passkeys (WebAuthn)
Passkeys 是一种基于 WebAuthn 标准的无密码凭证。用户通过生物识别、PIN 码或硬件安全密钥来证明自己拥有存储在设备(或密码管理器)上的私钥。相应的公钥会注册到 Supabase Auth,用于验证后续登录。Passkeys 具有防钓鱼功能,并且无需管理共享密码。
实验性的
Passkey 支持目前是实验性的。API 可能会在不通知的情况下发生更改。创建 Supabase 客户端时,你必须明确选择启用。参见 在客户端启用。
🌐 Passkey support is experimental. The API may change without notice. You must explicitly opt-in when creating the Supabase client. See Enable in the client.
需要 @supabase/supabase-js v2.105.0 及以上版本、supabase_flutter v2.15.0 及以上版本,或 supabase-swift v2.48.0 及以上版本。 升级你的客户端库以使用密码钥匙认证。
它是怎么运作的? #
🌐 How does it work?
每次登录或注册都是一次 WebAuthn 流程,分三步进行:
🌐 Each sign-in or registration is a WebAuthn ceremony with three steps:
- 选项:客户端请求来自 Supabase 身份验证的挑战。
- 仪式:平台的通行密钥 API(网页版的
navigator.credentials.create()/get(),或 iOS、Android 和 macOS 上的通行密钥插件)会提示用户使用生物识别或安全密钥。 - 验证:已签名的响应会返回到 Supabase Auth,它会验证挑战,并要么存储新的凭证,要么颁发一个会话。
Supabase Auth 使用可发现的凭证进行登录。用户无需提供邮箱、电话号码或用户名——认证器会根据它存储的凭证来识别账号。
🌐 Supabase Auth uses discoverable credentials for sign-in. The user does not need to provide an email, phone, or username — the authenticator resolves the account from the credential it stores.
注册通行密钥需要一个已有的、已确认的、非匿名用户。只要用户之前注册过通行密钥,并且邮箱或手机号已确认且账号未被封禁,就可以登录。
🌐 Registering a passkey requires an existing, confirmed, non-anonymous user. Sign-in works for any user that has previously registered a passkey, provided their email or phone is confirmed and the account is not banned.
启用密码钥匙认证 #
🌐 Enable passkey authentication
仪表盘 #
🌐 Dashboard
从仪表板的 认证 → 密钥 部分打开 密钥设置,开启 启用密钥认证,并填写 WebAuthn 依赖方 详细信息:
🌐 Open the Passkeys settings from the Authentication → Passkeys section of the Dashboard, turn on Enable Passkey authentication, and fill in the WebAuthn relying party details:
- 依赖方显示名称:在密钥提示时显示的应用可读名称(例如,“我的应用”)。
- 依赖方ID:你的应用的裸域名(例如,“example.com”)。不要包含协议、端口或路径。这决定了可以使用哪些通行密钥。
- 依赖方来源:允许的来源列表,用逗号分隔(例如 "https://example.com,https://app.example.com")。最多 5 个来源。
- 除了回环地址("localhost"、"127.0.0.1"、"[::1]")外,必须使用 HTTPS。
- 每个来源的主机名必须与依赖方 ID 匹配或是其子域名。
- Android 原生应用可以使用形式为
android:apk-key-hash:<base64url SHA-256 of the signing certificate>的应用来源。
仪表板会根据你项目的站点 URL 和项目名称预填这些信息。如果你的生产应用从不同的域名提供服务,可以进行调整。
🌐 The dashboard pre-fills these from your project's Site URL and project name. Adjust them if your production app is served from a different domain.
更改依赖方ID会使现有的通行密钥失效
通行密钥是以加密方式绑定到它们注册的依赖方(RP)ID的。更改 RP ID 会导致所有现有通行密钥无法登录,用户需要注册新的通行密钥。在用户开始注册之前,仔细选择 RP ID,并且在用户注册后保持稳定。
🌐 Passkeys are cryptographically bound to the Relying Party (RP) ID they were registered against. Changing the RP ID makes every existing passkey unusable for sign-in, and users will need to register a new one. Pick the RP ID carefully before users start enrolling, and keep it stable once they do.
命令行接口 #
🌐 CLI
把以下内容加到 supabase/config.toml:
🌐 Add the following to supabase/config.toml:
1[auth.passkey]2enabled = true34[auth.webauthn]5rp_display_name = "My App"6rp_id = "example.com"7rp_origins = ["https://example.com", "https://app.example.com"]当 auth.passkey.enabled 为 true 时,必须填写 [auth.webauthn] 部分。
🌐 The [auth.webauthn] section is required when auth.passkey.enabled is true.
管理 API #
🌐 Management API
你也可以通过 管理 API 配置通行密钥:
🌐 You can also configure passkeys via the Management API:
1# Get your access token from https://supabase.com/dashboard/account/tokens2export SUPABASE_ACCESS_TOKEN="your-access-token"3export PROJECT_REF="your-project-ref"45# Read the current passkey configuration6curl -X GET "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \7 -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \8 | jq '{passkey_enabled, webauthn_rp_id, webauthn_rp_display_name, webauthn_rp_origins}'910# Enable passkeys and set the WebAuthn relying party11curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \12 -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \13 -H "Content-Type: application/json" \14 -d '{15 "passkey_enabled": true,16 "webauthn_rp_display_name": "My App",17 "webauthn_rp_id": "example.com",18 "webauthn_rp_origins": "https://example.com,https://app.example.com"19 }'在客户端启用 #
🌐 Enable in the client
实验性的
通行密钥支持目前还在试验阶段,需要明确选择加入,因为 API 可能会随时更改。
🌐 Passkey support is currently experimental and requires explicit opt-in as the API may change without notice.
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient(supabaseUrl, supabaseKey, {4 auth: {5 experimental: { passkey: true },6 },7})注册通行密钥 #
🌐 Register a passkey
用户必须先登录才能注册密钥。通常,你会在安全设置页面调用这个,或者在注册后直接调用。
🌐 A user must be signed in before they can register a passkey. Typically, you call this from a security settings page, or directly after sign-up.
auth.registerPasskey() 执行完整的 WebAuthn 流程。它获取挑战,调用平台密钥 API,并使用 Supabase Auth 验证响应。
1const { data, error } = await supabase.auth.registerPasskey()23if (error) {4 // User cancelled, browser doesn't support WebAuthn, or verification failed5 console.error(error)6} else {7 console.log('Registered passkey', data.id)8}返回的通行密钥包含新凭证的元数据:
🌐 The returned passkey contains the new credential's metadata:
1{2 id: string // UUID — use this to update or delete the passkey3 friendly_name?: string // Derived from the authenticator's AAGUID4 created_at: string5}一个友好的名称会自动从认证器的认证器证明 GUID(AAGUID)生成。例如,iCloud Keychain、Google Password Manager、1Password。用户之后可以重命名他们的密码钥匙——参见 管理密码钥匙。
🌐 A friendly name is automatically derived from the authenticator's Authenticator Attestation GUID (AAGUID). For example, iCloud Keychain, Google Password Manager, 1Password. Users can rename their passkey afterwards — see Manage passkeys.
查看 registerPasskey 参考 (JavaScript · Dart · Swift) 以获取完整 API。
🌐 See the registerPasskey reference (JavaScript · Dart · Swift) for the full API.
使用密钥登录 #
🌐 Sign in with a passkey
auth.signInWithPasskey() 运行完整的可发现凭证认证流程。用户可以从认证器的界面中选择一个账户——你的应用不需要事先要求提供电子邮件或电话号码。
1const { data, error } = await supabase.auth.signInWithPasskey()23if (error) {4 console.error(error)5} else {6 // data.session and data.user are set; the client also dispatches a SIGNED_IN event7 console.log('Signed in as', data.user?.email)8}查看 signInWithPasskey 参考 (JavaScript · Dart · Swift) 以获取完整 API。
🌐 See the signInWithPasskey reference (JavaScript · Dart · Swift) for the full API.
两步 API #
🌐 Two-step API
对于原生流程、自定义界面或对 WebAuthn 操作的完全控制,可以使用更底层的 auth.passkey 命名空间。每个操作都分为“start”和“verify”。
🌐 For native flows, custom UI, or full control over the WebAuthn ceremony, use the lower-level auth.passkey namespace. Each operation is split into "start" and "verify".
注册:
🌐 Registration:
1const { data: options } = await supabase.auth.passkey.startRegistration()2// Run the WebAuthn ceremony yourself (e.g.: using a native WebAuthn library)3const credential = await runRegistrationCeremony(options.options)4await supabase.auth.passkey.verifyRegistration({5 challengeId: options.challenge_id,6 credential,7})身份验证:
🌐 Authentication:
1const { data: options } = await supabase.auth.passkey.startAuthentication()2// Run the WebAuthn ceremony yourself (e.g.: using a native WebAuthn library)3const credential = await runAuthenticationCeremony(options.options)4const { data } = await supabase.auth.passkey.verifyAuthentication({5 challengeId: options.challenge_id,6 credential,7})options 字段从启动方法返回的值与 WebAuthn PublicKeyCredentialCreationOptions 和 PublicKeyCredentialRequestOptions 的结构匹配(其中 ArrayBuffer 字段以 base64url 编码)。
🌐 The options field returned from the start methods matches the WebAuthn PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions shapes (with ArrayBuffer fields encoded as base64url).
查看 auth.passkey 参考 (JavaScript · Dart · Swift) 以获取完整 API。
🌐 See the auth.passkey reference (JavaScript · Dart · Swift) for the full API.
管理通行密钥 #
🌐 Manage passkeys
列出、重命名并删除当前用户的通行密钥:
🌐 List, rename, and delete the current user's passkeys:
1// List2const { data: passkeys } = await supabase.auth.passkey.list()3// [{ id, friendly_name, created_at, last_used_at? }, ...]45// Rename6await supabase.auth.passkey.update({7 passkeyId: passkeys[0].id,8 friendlyName: 'Work laptop',9})1011// Delete12await supabase.auth.passkey.delete({ passkeyId: passkeys[0].id })friendlyName 限制为 120 个字符。每次使用通行码登录时,lastUsedAt 都会更新。
查看 auth.passkey 参考 (JavaScript · Dart · Swift) 以获取完整 API。
🌐 See the auth.passkey reference (JavaScript · Dart · Swift) for the full API.
管理员 API #
🌐 Admin API
服务器端管理端点让你可以查看和撤销用户的通行密钥。这些端点需要项目的密钥,并且只能从受信任的服务器调用。
🌐 Server-side admin endpoints let you inspect and revoke a user's passkeys. These require the project's secret key and must only be called from a trusted server.
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient(supabaseUrl, supabaseSecretKey, {4 auth: { experimental: { passkey: true } },5})67const { data } = await supabase.auth.admin.passkey.listPasskeys({ userId })89await supabase.auth.admin.passkey.deletePasskey({ userId, passkeyId })查看 auth.admin.passkey 参考资料(JavaScript · Dart)以获取完整 API。Swift SDK 不提供管理员密钥方法。
🌐 See the auth.admin.passkey reference (JavaScript · Dart) for the full API. The Swift SDK does not expose admin passkey methods.
错误代码 #
🌐 Error codes
| 代码 | 含义 |
|---|---|
passkey_disabled | 这个项目没有启用通行密钥登录。 |
too_many_passkeys | 用户已经达到每个账户允许的通行密钥数量上限。 |
webauthn_credential_exists | 这个认证器已经注册到账户了。 |
webauthn_credential_not_found | 声明中的凭证没有在 Supabase Auth 注册。 |
webauthn_challenge_not_found | 挑战 ID 不存在或已经被使用过了。 |
webauthn_challenge_expired | 客户端返回凭证前,挑战已过期。 |
webauthn_verification_failed | 签名、证明或声明没有通过挑战验证。 |
另外,signInWithPasskey() 会返回常见的登录失败类型:email_not_confirmed、phone_not_confirmed 和 user_banned。
🌐 In addition, signInWithPasskey() returns the usual sign-in failure modes: email_not_confirmed, phone_not_confirmed, and user_banned.
限制 #
🌐 Limitations
- SSO 用户不能注册通行密钥。
- 匿名用户无法注册通行密钥——请先绑定邮箱或手机号。