認証情報
Auth.jsを外部認証メカニズムでセットアップしたり、従来のユーザー名/メールとパスワードのフローを使用したりするには、Credentials
プロバイダーを使用できます。このプロバイダーは、ログインフォームに入力された認証情報(ユーザー名/パスワードに限らない)を認証サービスに転送するように設計されています。
業界は、ウェブアプリケーションのユーザー認証と認可のメカニズムとして、ユーザー名とパスワードを用いる方法から大きく進歩してきました。そのため、可能な限り、OAuth プロバイダー、メールマジックリンク、またはWebAuthn (パスキー)オプションのような、よりモダンで安全な認証メカニズムをお勧めします。
しかし、私たちは柔軟であり、アプリケーションやユースケースに適したものをサポートしたいと考えているため、このプロバイダーを削除する予定はありません。
デフォルトでは、Credentialsプロバイダーはデータベースにデータを永続化しません。ただし、データベースでデータを引き続き作成および保存できます。パスワードの暗号化、レート制限の追加、パスワードリセット機能の追加など、必要なロジックを提供するだけです。
Credentialsプロバイダー
まず、Auth.jsの設定ファイルでCredentials
プロバイダーを初期化しましょう。プロバイダーをインポートし、providers
配列に追加する必要があります。
import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Credentials({
// You can specify which fields should be submitted, by adding keys to the `credentials` object.
// e.g. domain, username, password, 2FA token, etc.
credentials: {
email: {},
password: {},
},
authorize: async (credentials) => {
let user = null
// logic to salt and hash password
const pwHash = saltAndHashPassword(credentials.password)
// logic to verify if the user exists
user = await getUserFromDb(credentials.email, pwHash)
if (!user) {
// No user found, so this is their first attempt to login
// Optionally, this is also the place you could do a user registration
throw new Error("Invalid credentials.")
}
// return user object with their profile data
return user
},
}),
],
})
TypeScriptを使用している場合は、User
インターフェースを拡張して、authorize
コールバックのレスポンスと一致させることができます。これにより、他のコールバック(jwt
など)でユーザーを読み取るたびに、型が正しく一致します。
サインインフォーム
最後に、簡単なサインインフォームを作成しましょう。
import { signIn } from "@/auth"
export function SignIn() {
return (
<form
action={async (formData) => {
"use server"
await signIn("credentials", formData)
}}
>
<label>
Email
<input name="email" type="email" />
</label>
<label>
Password
<input name="password" type="password" />
</label>
<button>Sign In</button>
</form>
)
}
認証情報の検証
常にサーバー側で認証情報を検証してください。つまり、Zodのようなスキーマ検証ライブラリを活用します。
npm install zod
次に、Credentials
プロバイダーのauthorize
コールバックを使用して、auth.ts
設定ファイルでスキーマと解析を設定します。
import { object, string } from "zod"
export const signInSchema = object({
email: string({ required_error: "Email is required" })
.min(1, "Email is required")
.email("Invalid email"),
password: string({ required_error: "Password is required" })
.min(1, "Password is required")
.min(8, "Password must be more than 8 characters")
.max(32, "Password must be less than 32 characters"),
})
import NextAuth from "next-auth"
import { ZodError } from "zod"
import Credentials from "next-auth/providers/credentials"
import { signInSchema } from "./lib/zod"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"
import { getUserFromDb } from "@/utils/db"
export const { handlers, auth } = NextAuth({
providers: [
Credentials({
// You can specify which fields should be submitted, by adding keys to the `credentials` object.
// e.g. domain, username, password, 2FA token, etc.
credentials: {
email: {},
password: {},
},
authorize: async (credentials) => {
try {
let user = null
const { email, password } = await signInSchema.parseAsync(credentials)
// logic to salt and hash password
const pwHash = saltAndHashPassword(password)
// logic to verify if the user exists
user = await getUserFromDb(email, pwHash)
if (!user) {
throw new Error("Invalid credentials.")
}
// return JSON object with the user data
return user
} catch (error) {
if (error instanceof ZodError) {
// Return `null` to indicate that the credentials are invalid
return null
}
}
},
}),
],
})