密钥认证

密钥认证(Passkeys)是一种安全的、无密码的认证方法,使用密码学密钥对,由网络浏览器中的WebAuthn和FIDO2标准支持。它们用唯一的密钥对代替密码:一个存储在用户设备上的私钥和一个与网站共享的公钥。用户可以使用生物识别、PIN码或安全密钥登录,提供强大的、防钓鱼的认证,而无需传统密码。

密钥认证插件的实现在底层由SimpleWebAuthn提供支持。

安装

将插件添加到您的auth配置中

要将密钥认证插件添加到您的auth配置中,您需要导入插件并将其传递给auth实例的plugins选项。

选项

rpID:您网站的唯一标识符。本地开发时可以使用'localhost'

rpName:您网站的可读标题

origin:进行注册和认证的URL。http://localhosthttp://localhost:PORT也是有效的。不要包含任何尾部斜杠/

authenticatorSelection:允许自定义WebAuthn认证器选择标准。不指定时使用默认设置。

  • authenticatorAttachment:指定认证器的类型
    • platform:认证器附加到平台(例如,指纹读取器)
    • cross-platform:认证器不附加到平台(例如,安全密钥)
    • 默认:不设置(允许平台和跨平台认证器,优先考虑平台)
  • residentKey:确定凭证存储行为。
    • required:用户必须在认证器上存储凭证(最高安全性)
    • preferred:鼓励凭证存储但非强制
    • discouraged:不需要凭证存储(最快体验)
    • 默认:preferred
  • userVerification:控制认证期间的生物识别/PIN验证:
    • required:用户必须验证身份(最高安全性)
    • preferred:鼓励验证但非强制
    • discouraged:不需要验证(最快体验)
    • 默认:preferred
auth.ts
import { betterAuth } from "better-auth"
import { passkey } from "better-auth/plugins/passkey"
 
export const auth = betterAuth({
    plugins: [ 
        passkey(), 
    ], 
})

迁移数据库

运行迁移或生成schema,以向数据库添加必要的字段和表。

npx @better-auth/cli migrate

查看Schema部分手动添加字段。

添加客户端插件

auth-client.ts
   import { createAuthClient } from "better-auth/client"
   import { passkeyClient } from "better-auth/client/plugins"
 
   const authClient =  createAuthClient({
       plugins: [ 
           passkeyClient() 
       ] 
   })

使用方法

添加/注册密钥

要添加或注册密钥,请确保用户已经认证,然后调用客户端提供的passkey.addPasskey函数。

auth-client.ts
import { createAuthClient } from "better-auth/client";
import { passkeyClient } from "better-auth/client/plugins";
 
const authClient = createAuthClient({
  plugins: [
    passkeyClient(), 
  ], 
});
 
// 默认行为允许平台和跨平台密钥
const { data, error } = await authClient.passkey.addPasskey();

这将提示用户注册密钥,并将该密钥添加到用户的账户中。

您还可以指定要注册的认证器类型。authenticatorAttachment可以是platformcross-platform

auth-client.ts
// 注册跨平台密钥,仅显示二维码供用户扫描,
// 以及插入安全密钥的选项
const { data, error } = await authClient.passkey.addPasskey({
  authenticatorAttachment: 'cross-platform'
});

使用密钥登录

要使用密钥登录,您可以使用passkeySignIn方法。这将提示用户使用其密钥登录。

登录方法接受:

autoFill:浏览器自动填充,又称条件UI。了解更多

callbackURL:用户登录后重定向的URL。(可选)

auth-client.ts
import { createAuthClient } from "better-auth/client";
import { passkeyClient } from "better-auth/client/plugins";
 
const authClient = createAuthClient({
  plugins: [
    passkeyClient(), 
  ], 
});
const data = await authClient.signIn.passkey();

条件UI

该插件支持条件UI,允许浏览器在用户已经注册密钥的情况下自动填充密钥。

条件UI工作有两个要求:

更新输入字段

向您的输入字段添加值为webauthnautocomplete属性。您可以将此属性添加到多个输入字段,但条件UI至少需要一个才能工作。

webauthn值也应该是autocomplete属性的最后一个条目。

<label for="name">用户名:</label>
<input type="text" name="name" autocomplete="username webauthn">
<label for="password">密码:</label>
<input type="password" name="password" autocomplete="current-password webauthn">

预加载密钥

当您的组件挂载时,您可以通过调用authClient.signIn.passkey方法并将autoFill选项设置为true来预加载用户的密钥。

为了防止不必要的调用,我们还将添加一个检查,看浏览器是否支持条件UI。

useEffect(() => {
   if (!PublicKeyCredential.isConditionalMediationAvailable ||
       !PublicKeyCredential.isConditionalMediationAvailable()) {
     return;
   }
 
  void authClient.signIn.passkey({ autoFill: true })
}, [])

根据浏览器的不同,将出现自动填充密钥的提示。如果用户有多个密钥,他们可以选择要使用的那个。

某些浏览器还要求用户首先与输入字段交互,然后才会出现自动填充提示。

调试

要测试您的密钥实现,您可以使用模拟认证器。这样,您可以测试注册和登录过程,甚至不需要拥有实体设备。

Schema

该插件需要在数据库中添加一个新表来存储密钥数据。

表名: passkey

Field NameTypeKeyDescription
idstring每个密钥的唯一标识符
namestring密钥的名称
publicKeystring-密钥的公钥
userIdstring用户的ID
credentialIDstring-注册凭证的唯一标识符
counternumber-密钥的计数器
deviceTypestring-用于注册密钥的设备类型
backedUpboolean-密钥是否已备份
transportsstring-用于注册密钥的传输方式
createdAtDate-密钥创建的时间

选项

rpID: 您网站的唯一标识符。本地开发时可以使用'localhost'。

rpName: 您网站的可读标题。

origin: 进行注册和认证的URL。http://localhosthttp://localhost:PORT也是有效的。不要包含任何尾部斜杠/。

authenticatorSelection: 允许自定义WebAuthn认证器选择标准。未指定时,允许平台和跨平台认证器,residentKeyuserVerification设置为preferred

On this page