🔐

【Next.js】Auth.js v5でGoogleログインを実装する手順

に公開
2025/12/08

はじめに

Next.js(App Router)で Auth.js(旧 NextAuth.js)v5 を使って、Google ログイン機能を実装したときの手順メモです。

Auth.jsとは

Auth.js(旧 NextAuth.js)は、認証まわりをまとめて任せられるライブラリ です。
特に Next.js との相性が良く、公式でも next-auth@beta(v5 系) の利用が案内されています。
https://authjs.dev/

Auth.jsを使うメリット

👍 セキュリティに必要な仕組みがひと通りそろっている
CSRF 対策、JWT / セッション管理など、認証で必須の仕組みがひと通り入っている。

👍 OAuth ログインをサクッと実装できる
数行のコードで Google / GitHub ログインなどを追加できる。
必要に応じて Magic Link や自前のメール+パスワード認証にも拡張可能。

👍 Next.js に最適化されつつも、より汎用的な Auth プラットフォームへ進化中
元は Next.js 専用の NextAuth.js だったものが、v5 からはフレームワーク非依存の Auth.js プロジェクトとして発展している。その上で、Next.js 向けには next-auth として使いやすい統合パッケージが用意されている。

Auth.js / NextAuth.js の概要

  • 対応している認証方法(例)

    • OAuth(Google / GitHub など多数)
    • Magic Link(メールリンクでログイン)
    • Credentials(自前のメール & パスワード)
    • WebAuthn(パスキーなど)
  • Next.js で使う流れ(ざっくり)

    1. next-auth@beta をインストールする
    2. プロジェクト直下に auth.ts を作り、NextAuth({ providers: [...] }) を設定して
      handlers / auth / signIn / signOut をエクスポートする
    3. app/api/auth/[...nextauth]/route.ts で handlers を使って認証用 API ルートを作る
    4. 必要なら middleware.ts で「このURLはログイン必須」にする
    5. ページやコンポーネントで auth() でセッション取得、signIn / signOut をボタンなどから呼び出す
  • DB は必須?

    • 「OAuth でログインしてセッションを持つだけ」なら DB なしでもOK。
    • ユーザー情報を自前 DB に保存したいときは、Prisma などのアダプターを組み合わせる(Neon + Prisma などと相性◎)。

手順

0. 前提

  • Next.js(App Router、app/ ディレクトリ構成)
  • 認証は Google ログイン(Auth.js / next-auth@beta)
  • ローカル(localhost)と Vercel 本番で動かしたい

1. パッケージのインストール

npm install next-auth@beta

2. Google Cloud Console の設定

手順

  1. Google Cloud Console でプロジェクトを用意

  2. 画面左上のハンバーガーメニュー(三本線)をクリック→「API とサービス」→「認証情報」→ OAuth 2.0 クライアントID を作成

  3. 種類:Web アプリケーション

  4. 「承認済みのリダイレクトURI」に 2つ 登録する

    • 開発環境用
      http://localhost:3000/api/auth/callback/google

    • 本番(Vercel)用
      https://your-app.vercel.app/api/auth/callback/google
      (※your-app.vercel.app の部分は、自分の Vercel プロジェクトのドメインに置き換えてください)

  5. 発行された クライアントID と クライアントシークレット をメモしておく

3. ローカルの環境変数 .env.local

プロジェクト直下に .env.local を作成し、以下を記述します。
Prisma を利用する場合は、同じ内容を .env にも記述しておきます。

AUTH_SECRET=(npx auth secret で作った値)
AUTH_GOOGLE_ID=(GoogleのクライアントID)
AUTH_GOOGLE_SECRET=(Googleのクライアントシークレット)
DATABASE_URL=(Neon 等を使うとき)

AUTH_SECRET は次のコマンドで生成できます。

npx auth secret

4. auth.ts(NextAuth 設定のメインファイル)を作成

プロジェクトのルート直下に auth.ts を作成します。

// auth.ts
import NextAuth from "next-auth"
import Google from "next-auth/providers/google"

export const { handlers, auth, signIn, signOut } = NextAuth({
    providers: [
        Google({
            // .env.local の環境変数を利用
            clientId: process.env.AUTH_GOOGLE_ID!,
            clientSecret: process.env.AUTH_GOOGLE_SECRET!,
        }),
    ],
    // Vercel などプロキシ環境向け
    trustHost: true,
    // 必要に応じて callbacks や pages などをここに追加
})

5. 認証用の API ルートを作る

Auth.js に認証フローを任せるためには、
「/api/auth 以下のリクエストを Auth.js に渡す窓口」 を用意する必要があります。

Next.js(App Router)では、それを Route Handler で定義します。

app/api/auth/[...nextauth]/route.ts を作成します。

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

export const { GET, POST } = handlers;

これだけで、Auth.js が内部で用意しているエンドポイントにアクセスできるようになります。

6. middleware でページを保護する

「このページは ログインしている人だけ 見られるようにしたい」というときに使うのが middleware.ts です。Next.js のミドルウェアは、ルートに到達する前に必ず一度通る “関所” のような仕組みだと思うとイメージしやすいです。

ここでは、この“関所役”として Auth.js の auth を使います。


6-1. 基本形:middleware.ts をプロジェクト直下に置く

まずはプロジェクトのルートに middleware.ts を作成します。

// middleware.ts
export { auth as middleware } from "@/auth";

6-2. 特定のパスだけログイン必須にする(matcher)

「全部じゃなくて、/dashboard だけログイン必須にしたい」という場合は、config.matcher を使ってパスを絞り込みます。

// middleware.ts
export { auth as middleware } from "@/auth";

export const config = {
  // 例: /dashboard 以下のページはすべてログイン必須
  matcher: ["/dashboard/:path*"],
};

この設定を入れると、/dashboard や /dashboard/settings などにアクセスしたとき

  • ログイン済み → そのまま表示
  • 未ログイン → Auth.js がログイン画面(/api/auth/signin)にリダイレクト

という動きになります。


7. サインイン / サインアウトボタン(サーバーアクション)

ここでは、「Googleでログイン」ボタン と 「ログアウト」ボタンをコンポーネントとして用意します。どちらも サーバーアクション経由で signIn / signOut を呼ぶ形にします。


7-1. サインインボタン SignInButton

ルート直下にcomponents/auth/SignInButton.tsx を作成します。

// components/auth/SignInButton.tsx
import { signIn } from "@/auth";

export function SignInButton() {
  return (
    <form
      action={async () => {
        "use server";
        await signIn("google", { redirectTo: "/" }); // まずはトップに戻す
        // /dashboard に飛ばしたい場合は:
        // await signIn("google", { redirectTo: "/dashboard" });
      }}
    >
      <button type="submit">Googleでログイン</button>
    </form>
  );
}

7-2. サインアウトボタン SignOutButton

components/auth/SignOutButton.tsx を作成します。

// components/auth/SignOutButton.tsx
import { signOut } from "@/auth";

export function SignOutButton() {
  return (
    <form
      action={async () => {
        "use server";
        await signOut();
      }}
    >
      <button type="submit">ログアウト</button>
    </form>
  );
}

こちらも同じく

  • <form> の action にサーバーアクションを指定
  • ボタンを押すと signOut() が実行され、セッションが破棄されます。

8. トップページでの表示

app/page.tsxに以下を記述

// app/page.tsx
import { auth } from "@/auth";
import { SignInButton } from "@/components/auth/SignInButton";
import { SignOutButton } from "@/components/auth/SignOutButton";

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

  return (
    <main style={{ padding: "24px" }}>
      {session?.user ? (
        <>
          <p>{session.user.name} さんとしてログイン中</p>
          <SignOutButton />
        </>
      ) : (
        <>
          <p>ログインしていません</p>
          <SignInButton />
        </>
      )}
    </main>
  );
}

この時点で、ローカルサーバーを起動して、ログインを試してみてください。
「ログイン」ボタンをクリック → Google認証ページに遷移し、アカウントを選択した → app/page.tsxの「〇〇さんとしてログイン中」の内容が表示されたら成功です!

9. Vercel の環境変数

Vercel ダッシュボード → プロジェクト → Settings → Environment Variables で、
Production(必要なら Preview / Development)に下記を登録します。

  • AUTH_SECRET
  • AUTH_GOOGLE_ID
  • AUTH_GOOGLE_SECRET
  • あれば DATABASE_URL)

登録・変更したら 必ず再デプロイ します(環境変数はビルド時に読み込まれるため)。

Vercelのドメインにアクセスして、ログインを試してみてください。試してみてください。
こちらも同様に、
「ログイン」ボタンをクリック → Google認証ページに遷移し、アカウントを選択した → app/page.tsxの「〇〇さんとしてログイン中」の内容が表示されたら成功です!

手順は以上です。

Vercel の URL と Google の redirect URI の関係

ここが特につまづきやすいポイントです。

Vercel が作る URL の種類は複数あります。

  1. 本番用ドメイン(Production)例:your-app.vercel.app
  2. ブランチ / プレビュー用ドメイン 例:your-app-git-main-xxxxx.vercel.app
  3. デプロイごとの一時 URL 例:your-app-xxxxx.vercel.app

🚨 そのため、Google に登録した redirect URI のドメインと「ログインを試している URL のドメイン」が一致していないと、redirect_uri_mismatch が発生します。

実際に出たエラーと原因まとめ

① 401: invalid_client

⚠️ 原因

  • Vercel 側の AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET が未設定 or 間違い
  • 環境変数の名前のスペル違い
    (例:AUTH_GOOGLE_ID vs GOOGLE_CLIENT_ID)

💊 対処

  • .env.local と同じキー・値を Vercel の Environment Variables に登録する
  • 変更後は必ず再デプロイ

② 400: redirect_uri_mismatch

⚠️ 原因

  • Google に登録した redirect URI と、実際にリクエストされている URI のドメイン or パスが違う
  • 特に「プレビュー用ドメインからログインしていた」というパターンが多い

💊 対処

  • 本番用ドメインを1つ決めて、https://{本番ドメイン}/api/auth/callback/googleを Google に登録する
  • ログインテストは その本番ドメインからだけ 行う

③ ログイン後に 404

⚠️ 原因

  • redirectTo: "/dashboard" にしているのに、
  • app/dashboard/page.tsx が存在しないなど、パスのミスマッチ

💊 対処

  • 最初は redirectTo: "/" にしてトップに戻す
  • /dashboard を使いたい場合は、以下のように /dashboard ページを用意する
// app/dashboard/page.tsx
import { auth } from "@/auth";

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

  return (
    <main style={{ padding: "24px" }}>
      <h1>ダッシュボード</h1>
      <p>ログインユーザー: {session?.user?.name ?? "不明"}</p>
    </main>
  );
}

middleware の matcher と redirectTo のパスも /dashboard に揃えておきます。


おわりに

Auth.js v5 + Next.js App Router での Google 認証は、

  • auth.ts と middleware.ts の置き場所(プロジェクト直下)
  • Vercel の環境変数の設定
  • Google の「承認済みのリダイレクトURI」と実際にアクセスしているドメインの不一致

あたりで特につまづきやすいと感じました。

一度手順を通してみて、この記事のステップをそのままなぞれるようにしておくと、
別プロジェクトでもほぼコピペ+ドメイン差し替えで再現できるようになると思います 📝

同じようにハマっている方の参考になれば嬉しいです。

GitHubで編集を提案

Discussion