Skip to content
Auth

密钥认证

Allow users to sign in with passkeys (WebAuthn)

Passkeys 是一种基于 WebAuthn 标准的无密码凭证。用户通过生物识别、PIN 码或硬件安全密钥来证明自己拥有存储在设备(或密码管理器)上的私钥。相应的公钥会注册到 Supabase Auth,用于验证后续登录。Passkeys 具有防钓鱼功能,并且无需管理共享密码。

它是怎么运作的? #

🌐 How does it work?

每次登录或注册都是一次 WebAuthn 流程,分三步进行:

🌐 Each sign-in or registration is a WebAuthn ceremony with three steps:

  1. 选项:客户端请求来自 Supabase 身份验证的挑战。
  2. 仪式:平台的通行密钥 API(网页版的 navigator.credentials.create() / get(),或 iOS、Android 和 macOS 上的通行密钥插件)会提示用户使用生物识别或安全密钥。
  3. 验证:已签名的响应会返回到 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.

命令行接口 #

🌐 CLI

把以下内容加到 supabase/config.toml

🌐 Add the following to supabase/config.toml:

1
[auth.passkey]
2
enabled = true
3
4
[auth.webauthn]
5
rp_display_name = "My App"
6
rp_id = "example.com"
7
rp_origins = ["https://example.com", "https://app.example.com"]

auth.passkey.enabledtrue 时,必须填写 [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/tokens
2
export SUPABASE_ACCESS_TOKEN="your-access-token"
3
export PROJECT_REF="your-project-ref"
4
5
# Read the current passkey configuration
6
curl -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}'
9
10
# Enable passkeys and set the WebAuthn relying party
11
curl -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

1
import { createClient } from '@supabase/supabase-js'
2
3
const 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 验证响应。

1
const { data, error } = await supabase.auth.registerPasskey()
2
3
if (error) {
4
// User cancelled, browser doesn't support WebAuthn, or verification failed
5
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 passkey
3
friendly_name?: string // Derived from the authenticator's AAGUID
4
created_at: string
5
}

一个友好的名称会自动从认证器的认证器证明 GUID(AAGUID)生成。例如,iCloud KeychainGoogle Password Manager1Password。用户之后可以重命名他们的密码钥匙——参见 管理密码钥匙

🌐 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() 运行完整的可发现凭证认证流程。用户可以从认证器的界面中选择一个账户——你的应用不需要事先要求提供电子邮件或电话号码。

1
const { data, error } = await supabase.auth.signInWithPasskey()
2
3
if (error) {
4
console.error(error)
5
} else {
6
// data.session and data.user are set; the client also dispatches a SIGNED_IN event
7
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:

1
const { data: options } = await supabase.auth.passkey.startRegistration()
2
// Run the WebAuthn ceremony yourself (e.g.: using a native WebAuthn library)
3
const credential = await runRegistrationCeremony(options.options)
4
await supabase.auth.passkey.verifyRegistration({
5
challengeId: options.challenge_id,
6
credential,
7
})

身份验证:

🌐 Authentication:

1
const { data: options } = await supabase.auth.passkey.startAuthentication()
2
// Run the WebAuthn ceremony yourself (e.g.: using a native WebAuthn library)
3
const credential = await runAuthenticationCeremony(options.options)
4
const { data } = await supabase.auth.passkey.verifyAuthentication({
5
challengeId: options.challenge_id,
6
credential,
7
})

options 字段从启动方法返回的值与 WebAuthn PublicKeyCredentialCreationOptionsPublicKeyCredentialRequestOptions 的结构匹配(其中 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
// List
2
const { data: passkeys } = await supabase.auth.passkey.list()
3
// [{ id, friendly_name, created_at, last_used_at? }, ...]
4
5
// Rename
6
await supabase.auth.passkey.update({
7
passkeyId: passkeys[0].id,
8
friendlyName: 'Work laptop',
9
})
10
11
// Delete
12
await 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.

1
import { createClient } from '@supabase/supabase-js'
2
3
const supabase = createClient(supabaseUrl, supabaseSecretKey, {
4
auth: { experimental: { passkey: true } },
5
})
6
7
const { data } = await supabase.auth.admin.passkey.listPasskeys({ userId })
8
9
await 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_confirmedphone_not_confirmeduser_banned

🌐 In addition, signInWithPasskey() returns the usual sign-in failure modes: email_not_confirmed, phone_not_confirmed, and user_banned.

限制 #

🌐 Limitations

  • SSO 用户不能注册通行密钥。
  • 匿名用户无法注册通行密钥——请先绑定邮箱或手机号。