WebAuthn(Passkeys)
⚠️
WebAuthn / Passkeys 提供商处于实验阶段,不建议用于生产环境。
WebAuthn 提供商需要对所有框架集成以及计划支持它的任何数据库适配器进行更改。因此,WebAuthn 提供商目前仅在以下框架集成和数据库适配器中受支持。对更多框架和适配器的支持即将推出。
[email protected]
或更高版本@auth/[email protected]
或更高版本[email protected]
或更高版本
安装对等依赖项
npm install @simplewebauthn/[email protected] @simplewebauthn/[email protected]
@simplewebauthn/browser
对等依赖项**仅对自定义登录页面** 是必需的。如果您使用的是 Auth.js 默认页面,则可以跳过安装该对等依赖项。
应用所需的模式迁移
这是 PostgreSQL 的原始 SQL 迁移,有关更多详细信息,包括其他数据库的示例迁移,请查看 @auth/prisma-adapter
文档 中的更新 Prisma 模式。
简而言之,Passkeys 提供商需要一个名为 Authenticator
的附加表。
./migration/add-webauthn-authenticator-table.sql
-- CreateTable
CREATE TABLE "Authenticator" (
"credentialID" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"credentialPublicKey" TEXT NOT NULL,
"counter" INTEGER NOT NULL,
"credentialDeviceType" TEXT NOT NULL,
"credentialBackedUp" BOOLEAN NOT NULL,
"transports" TEXT,
PRIMARY KEY ("userId", "credentialID"),
CONSTRAINT "Authenticator_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "Authenticator_credentialID_key" ON "Authenticator"("credentialID");
更新 Auth.js 配置
将 Passkeys
提供商添加到您的配置中。还要确保您使用的是兼容的数据库适配器。
./auth.ts
import Passkey from "next-auth/providers/passkey"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
const prisma = new PrismaClient()
export default {
adapter: PrismaAdapter(prisma),
providers: [Passkey],
experimental: { enableWebAuthn: true },
}
如果您使用的是内置的 Auth.js 页面,那么您就可以开始使用了!导航到您的 /signin
路由应该包含一个“使用 Passkeys 登录”按钮。
自定义页面
如果您使用的是自定义登录页面,则可以使用 next-auth
signIn
函数,并使用以下代码启动 WebAuthn 注册和登录流程。
💡
使用 WebAuthn signIn
函数时,还需要安装 @simplewebauth/browser
对等依赖项。
app/login/page.tsx
"use client"
import { useSession } from "next-auth/react"
import { signIn } from "next-auth/webauthn"
export default function Login() {
const { data: session, update, status } = useSession()
return (
<div>
{status === "authenticated" ? (
<button onClick={() => signIn("passkey", { action: "register" })}>
Register new Passkey
</button>
) : status === "unauthenticated" ? (
<button onClick={() => signIn("passkey")}>Sign in with Passkey</button>
) : null}
</div>
)
}