0Pricing
React Academy · レッスン

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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Next.js App RouterでAuth.jsをセットアップ
  2. Session管理とuseSession Hook
  3. Credentials Providerとカスタムログイン
  4. Middlewareベースのルート保護
← React Academyに戻る