Credentials Providerとカスタムログイン
credentials providerとカスタムログインフォームで、データベースに対してユーザーを認証します。
「Credentials Providerとカスタムログイン」はCoddyKit上の無料React Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。
Credentialsプロバイダーを使用する場面
OAuthプロバイダーではなく、自分のデータベース(メールアドレスとパスワード)に対して認証する場合はCredentialsプロバイダーを使用します。パスワードのハッシュ化とセキュリティを慎重に扱う必要があります。
Credentialsプロバイダーの定義
プロバイダー配列にCredentialsを追加します。authorize関数は送信された認証情報を受け取り、ユーザーオブジェクトまたはnullを返します。
import Credentials from 'next-auth/providers/credentials';
import bcrypt from 'bcryptjs';
export const { handlers, auth } = NextAuth({
providers: [
Credentials({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
const user = await db.user.findUnique({ where: { email: credentials.email as string } });
if (!user) return null;
const valid = await bcrypt.compare(credentials.password as string, user.passwordHash);
if (!valid) return null;
return { id: user.id, name: user.name, email: user.email };
},
}),
],
});カスタムログインフォーム
next-auth/reactのsignIn('credentials', { email, password })を呼び出すClient Componentとしてログインフォームを作成します。
'use client';
import { signIn } from 'next-auth/react';
export function LoginForm() {
const [error, setError] = useState('');
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const form = new FormData(e.currentTarget);
const result = await signIn('credentials', {
email: form.get('email'),
password: form.get('password'),
redirect: false, // handle redirect manually
});
if (result?.error) setError('Invalid email or password');
else router.push('/dashboard');
}
return (
<form onSubmit={handleSubmit}>
{error && <p className="error">{error}</p>}
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit">Sign in</button>
</form>
);
}エラー処理のためのredirect: false
signIn()にredirect: falseを渡すと、結果オブジェクトを受け取り、エラーページへリダイレクトする代わりにコンポーネント内でエラーを処理できます。
const result = await signIn('credentials', { email, password, redirect: false });
if (result?.error) {
// result.error is 'CredentialsSignin' for auth failures
setError('Invalid email or password');
}authorizeでのZodバリデーション
データベースにアクセスする前に、authorize関数内でZodを使って認証情報を検証します。
import { z } from 'zod';
const schema = z.object({ email: z.string().email(), password: z.string().min(8) });
async authorize(credentials) {
const parsed = schema.safeParse(credentials);
if (!parsed.success) return null;
const { email, password } = parsed.data;
// ... db lookup
}認証情報のセッション戦略
Credentials プロバイダーでは JWT セッション戦略が必要です(セッション用のデータベースアダプターは必要ありません)。session: { strategy: 'jwt' }を明示的に設定してください。
export const { handlers, auth } = NextAuth({
session: { strategy: 'jwt' },
providers: [Credentials({ ... })],
});セッションへのカスタムフィールドの追加
JWT コールバックとセッションコールバックを通して追加のユーザーデータを渡し、useSession()で参照できるようにしてください。
callbacks: {
async jwt({ token, user }) {
if (user) { token.id = user.id; token.role = user.role; }
return token;
},
async session({ session, token }) {
session.user.id = token.id as string;
session.user.role = token.role as string;
return session;
},
},パスワードのハッシュ化
保存する前に、必ず bcrypt でパスワードをハッシュ化してください。平文のパスワードは決して保存しないでください。
import bcrypt from 'bcryptjs';
// On registration:
const passwordHash = await bcrypt.hash(password, 12);
await db.user.create({ data: { email, passwordHash } });
// On login (in authorize):
const valid = await bcrypt.compare(submittedPassword, user.passwordHash);サインイン試行のレート制限
ミドルウェアまたはインメモリーストアを使ってサインイン試行にレート制限を設け、認証情報エンドポイントをブルートフォース攻撃から保護してください。
// middleware.ts or in a rate-limiting library
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({ redis: Redis.fromEnv(), limiter: Ratelimit.slidingWindow(10, '10 s') });
// Check in authorize before DB query:
const { success } = await ratelimit.limit(credentials.email as string);
if (!success) return null;登録フロー
登録処理は Auth.js とは別に行います。Server Action または API ルートを使って、データベースにハッシュ化したパスワードとともにユーザーを作成し、その後signIn('credentials', ...)でサインインさせてください。
確認問題
カスタムログインフォームでsignIn()を呼び出すとき、なぜ redirect: false を渡す必要がありますか?
まとめ
Credentials プロバイダーは、送信されたフォームデータを引数としてauthorize関数を呼び出します。Zod で検証し、bcrypt でハッシュ化されたパスワードを比較して、ユーザーオブジェクトまたは null を返してください。カスタムエラーハンドリングを行うフォームではredirect: falseを使い、Credentials では必ず JWT 戦略を使用してください。
よくある質問
「Credentials Providerとカスタムログイン」レッスンは無料ですか?
はい。「Credentials Providerとカスタムログイン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。
「Credentials Providerとカスタムログイン」で何を学びますか?
credentials providerとカスタムログインフォームで、データベースに対してユーザーを認証します。 ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
React Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「Credentials Providerとカスタムログイン」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このReact Academyレッスンでコードを書いて実行できますか?
はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Next.js App RouterでAuth.jsをセットアップ
- Session管理とuseSession Hook
- Credentials Providerとカスタムログイン
- Middlewareベースのルート保護