【Next.js】Auth.js(NextAuth)で認証機能を実装する

JavaScript

こんにちは、かつコーチです。

ここまでのシリーズでは、App Routerの基本構造やServer Components・Client Componentsの使い分け、外部APIとのデータ連携まで扱ってきました。

アプリの骨格ができてくると、次に必ず必要になるのが「誰がログインしているか」を管理する認証機能です。

今回から2回にわたって認証編に入ります。

まずはAuth.js(旧NextAuth.js)を使って、Next.jsアプリに認証機能を実装する方法を解説します。

Auth.jsとは?前提知識

Next.js向けの認証ライブラリ

Auth.jsは、Next.jsをはじめとするフルスタックフレームワーク向けに設計された認証ライブラリです。

もともとはNextAuth.jsという名前でNext.js専用でしたが、SvelteKitなど他フレームワークにも対応する過程でAuth.jsに改称されました。

パッケージ名は現在もnext-authで、App Routerとの統合を前提に設計されています。

自前実装よりAuth.jsを選ぶ理由

認証を自前で実装する場合、パスワードのハッシュ化、セッション管理、CSRF対策など考慮すべき点が非常に多くなります。

Auth.jsはこれらをライブラリ側で吸収してくれるうえ、Google・GitHubなどのOAuthプロバイダー(外部サービスのアカウントでログインできる仕組み)との連携もほぼ設定だけで実現できます。

セキュリティ要件の高い認証まわりは、車輪の再発明を避けて信頼できるライブラリに任せるのが上級者の判断としても妥当です。

基本の書き方・実装手順

手順1:パッケージのインストール

npm install next-auth@beta

App Routerに対応した最新版はベータ系列のバージョンなので、@betaタグを指定してインストールします。

手順2:認証設定ファイルの作成

プロジェクトルートにauth.tsを作成し、認証の中心設定を書きます。

// auth.ts
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    GitHub({
      clientId: process.env.AUTH_GITHUB_ID,
      clientSecret: process.env.AUTH_GITHUB_SECRET,
    }),
  ],
});

handlersはAPIルート用、authはサーバー側でセッションを取得するための関数、signIn/signOutはログイン・ログアウト処理に使います。

手順3:APIルートの作成

Auth.jsが内部で使うエンドポイントを、Route Handlerとして公開します。

// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";

export const { GET, POST } = handlers;

[...nextauth]というキャッチオールルート(複数階層のパスを1つのファイルで受け取る仕組み)が、ログインコールバックなど複数のエンドポイントをまとめて処理します。

手順4:環境変数の設定

# .env.local
AUTH_SECRET=openssl-randで生成したランダムな文字列
AUTH_GITHUB_ID=GitHub OAuth AppのClient ID
AUTH_GITHUB_SECRET=GitHub OAuth AppのClient Secret

AUTH_SECRETはセッション情報の暗号化に使われる値で、以下のコマンドで生成できます。

npx auth secret

手順5:ログイン・ログアウトボタンの実装

// app/components/AuthButton.tsx
import { auth, signIn, signOut } from "@/auth";

export default async function AuthButton() {
  const session = await auth();

  if (session?.user) {
    return (
      <form
        action={async () => {
          "use server";
          await signOut();
        }}
      >
        <p>{session.user.name}さん、こんにちは</p>
        <button type="submit">ログアウト</button>
      </form>
    );
  }

  return (
    <form
      action={async () => {
        "use server";
        await signIn("github");
      }}
    >
      <button type="submit">GitHubでログイン</button>
    </form>
  );
}

auth()はServer Component内で直接呼び出せるため、useEffectでセッションを取得するような回りくどい実装が不要です。

signInsignOutServer Actions(クライアントから直接呼び出せるサーバー側の関数)として"use server"付きの関数に渡す形が推奨パターンです。

つまずきやすい設定・注意点

コールバックURLはOAuthプロバイダー側の管理画面にも登録が必要です。

GitHubの場合はhttp://localhost:3000/api/auth/callback/githubを開発時のコールバックURLとして登録します。

本番環境用には、本番ドメインの同じパスも別途登録しておく必要があります。

よくあるつまずきポイント・エラー対処

実際にハマった「MissingSecret」エラー

私が最初に検証環境を構築したとき、ログインボタンを押した瞬間にこのエラーで止まりました。

[auth][error] MissingSecret: Please define a `secret`.

原因は単純で、.env.localAUTH_SECRETを設定し忘れていたことでした。

さらに厄介だったのが、.env.localを追加した後もエラーが消えなかった点です。

開発サーバーを再起動していなかったため、環境変数が読み込まれていませんでした。

❌ Before:環境変数追加後もサーバーを起動したまま

# .env.localを編集した後も
# npm run devのプロセスをそのまま放置していた

✅ After:環境変数変更後は必ず開発サーバーを再起動する

# .env.local編集後
# 一度Ctrl+Cで停止してから再度起動する
npm run dev

Next.jsは.env.localをサーバー起動時にしか読み込まないため、環境変数を追加・変更した際は必ず再起動する癖をつけておくと、このタイプのつまずきを防げます。

middlewareでのセッションチェックとの違い

auth()はServer Componentやページ単位でのチェックには便利ですが、複数ページをまとめて保護したい場合は非効率になりがちです。

その場合の解決策は次の記事で扱うmiddleware.tsです。

まとめ

この記事のポイント

  • Auth.js(旧NextAuth.js)はNext.js向けに設計された認証ライブラリで、OAuthプロバイダー連携をほぼ設定だけで実現できる
  • auth.tsで設定を定義し、app/api/auth/[...nextauth]/route.tsでRoute Handlerとして公開する
  • auth()はServer Componentから直接呼び出してセッションを取得できる
  • signInsignOutはServer Actionとして呼び出すのが推奨パターン
  • 環境変数を変更したら開発サーバーの再起動を忘れないこと

次に読むべき記事

Auth.jsでログイン機能自体は作れましたが、「ログインしていないユーザーを特定のページから締め出す」処理はまだ実装していません。

次回は、middleware.tsを使ってログイン必須ページを一括で保護する方法を解説します。

→ 次の記事:middleware.tsでログイン必須ページを保護する

タイトルとURLをコピーしました