WebAuthn (Passkeys)
WebAuthn / Passkeys 프로바이더는 실험적 기능이며, 프로덕션 환경에서 사용하지 않는 것을 권장합니다.
WebAuthn 프로바이더는 프레임워크 통합과 이를 지원하려는 모든 데이터베이스 어댑터에 변경이 필요합니다. 따라서 현재 WebAuthn 프로바이더는 아래의 프레임워크 통합과 데이터베이스 어댑터에서만 지원됩니다. 더 많은 프레임워크와 어댑터에 대한 지원이 곧 추가될 예정입니다.
next-auth@5.0.0-beta.8
이상@auth/prisma-adapter@1.3.0
이상node@20.0.0
이상
peer dependencies 설치
npm install @simplewebauthn/server@9.0.3 @simplewebauthn/browser@9.0.1
@simplewebauthn/browser
peer dependency는 커스텀 로그인 페이지를 사용할 때만 필요합니다. Auth.js 기본 페이지를 사용한다면 이 peer dependency를 설치하지 않아도 됩니다.
필요한 스키마 마이그레이션 적용하기
이것은 PostgreSQL을 위한 원시 SQL 마이그레이션입니다. 다른 데이터베이스에 대한 예제 마이그레이션을 포함한 자세한 내용은 @auth/prisma-adapter
문서에서 업데이트된 Prisma 스키마를 확인하세요.
간단히 말해, Passkeys 프로바이더는 Authenticator
라는 추가 테이블을 필요로 합니다.
-- 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
프로바이더를 설정에 추가하세요. 또한 호환 가능한 데이터베이스 어댑터를 사용하고 있는지 확인하세요.
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
피어 의존성도 함께 설치해야 합니다.
"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" })}>
새로운 Passkey 등록
</button>
) : status === "unauthenticated" ? (
<button onClick={() => signIn("passkey")}>Passkey로 로그인</button>
) : null}
</div>
)
}