🐥

Next.js 15 + Prisma + Google OAuthを使って認証機能を実装してみた

に公開
2025/11/25

はじめに

業務でGoogle OAuthを使ったログイン機能に触れる機会がありましたが、一から実装したとこがなかったため、Next.js 15とApp Router、Prisma、そしてGoogle OAuthを使って、ログイン機能を自分なりに構築してみました!

この記事では、その実装手順をまとめています。
初心者の方でも再現できるよう、詳細に書いてみましたので、ぜひ参考になれば嬉しいです。

1.環境構築

1.1 必要なパッケージのインストール

npm install next-auth@beta @auth/prisma-adapter

1.2 Google Cloud Consoleでの設定

  1. プロジェクトを作成
  2. 「APIとサービス」→「認証情報」→「OAuth2.0クライアントID」を作成
  3. リダイレクトURLの設定:
    • 開発環境:http://localhost:3000/api/auth/callback/google
    • 本番環境:https://your-domain.com/api/auth/callback/google
  4. クライアントIDとクライアントシークレットを取得

1.3 環境変数の設定

AUTH_SECRET=<ランダムな文字列>
AUTH_GOOGLE_ID=<GoogleクライアントID>
AUTH_GOOGLE_SECRET=<Googleクライアントシークレット>
AUTH_TRUST_HOST=true
  • AUTH_SEDRETの生成方法:
openssl rand -base64 32

2.Prismaスキーマの設定

2.1 Prismaモデルの定義

model User {
id String @id @default(uuid())
name String?
email String @unique
emailVerified DateTime?
image String?
posts Post[] @relation("UserPosts")
accounts Account[]
sessions Session[]
createdAt DateTime @default(now())
}


model Account {
id String @id @default(uuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}


model Session {
id String @id @default(uuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}


model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}

2.2 マイグレーションの実行

npx prisma migrate dev --name init_auth_tables

3.認証設定ファイル

3.1 src/auth.ts

import { PrismaClient } from '@/src/generated/prisma/client';
import { PrismaAdapter } from '@auth/prisma-adapter';
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';

const prisma = new PrismaClient();

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    Google({
      clientId: process.env.AUTH_GOOGLE_ID,
      clientSecret: process.env.AUTH_GOOGLE_SECRET,
    }),
  ],
  pages: {
    signIn: '/auth/signin',
  },
});

4.APIルートの設定

4.1 src/api/auth/[...nextauth]/route.ts

import { handlers } from '@/auth';

export const { GET, POST } = handlers;

5. UIコンポーネントの実装

5.1 サインインページ``src/app/auth/signin/page.tsx`

import { signIn } from '@/auth';

export default function SignIn() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <div className="w-full max-w-md space-y-8 rounded-lg border p-8">
        <div className="text-center">
          <h2 className="text-3xl font-bold">Tech Blog CMS</h2>
          <p className="mt-2 text-gray-600">サインインしてください</p>
        </div>

        <form
          action={async () => {
            'use server';
            await signIn('google', { redirectTo: '/' });
          }}
        >
          <button
            type="submit"
            className="w-full rounded-lg bg-blue-600 px-4 py-3 text-white hover:bg-blue-700"
          >
            Googleでサインイン
          </button>
        </form>
      </div>
    </div>
  );
}

5.2 認証状態とサインアウトUI

import { auth, signOut } from '../../auth';

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

  return (
    <div className="flex min-h-screen flex-col items-center justify-center p-8">
      <div className="w-full max-w-md space-y-8 rounded-lg border p-8">
        <h1 className="text-center text-3xl font-bold">Tech Blog CMS</h1>

        {session?.user ? (
          <div className="space-y-4">
            <div className="text-center">
              <p className="text-sm text-gray-600">ログイン中</p>
              <p className="mt-2 text-xl font-semibold">{session.user.name}</p>
              <p className="text-sm text-gray-600">{session.user.email}</p>
            </div>

            {session.user.image && (
              <div className="flex justify-center">
                <img
                  src={session.user.image}
                  alt={session.user.name || 'User'}
                  className="h-20 w-20 rounded-full"
                />
              </div>
            )}

            <form
              action={async () => {
                'use server';
                await signOut({ redirectTo: '/' });
              }}
            >
              <button
                type="submit"
                className="w-full rounded-lg bg-red-600 px-4 py-3 text-white hover:bg-red-700"
              >
                サインアウト
              </button>
            </form>
          </div>
        ) : (
          <div className="text-center">
            <p className="mb-4 text-gray-600">ログインしていません</p>
            <a
              href="/auth/signin"
              className="inline-block rounded-lg bg-blue-600 px-6 py-3 text-white hover:bg-blue-700"
            >
              サインイン
            </a>
          </div>
        )}
      </div>
    </div>
  );
}

5.3 動作確認

コマンドを実行して開発サーバーを起動します:

npm run dev

その後、http://localhost:3000/auth/signinにアクセス


「Googleでサインイン」ボタンを押すと、アカウント選択画面が表示されます。


ログイン後、ユーザー情報とログアウトボタンが表示されます。


「サインアウト」を押すと、再びログイン前の状態に戻ります。

まとめ

本記事では、Next.js 15 + App Router + Prisma + Google OAuth を活用した認証システムの構築手順をご紹介しました。
自分の手で実装してみたことで、内部の仕組みや動きもより理解を深めることができました。
ログイン機能の実装は一見むずかしそうに見えますが、next-authとPrismaを使えば思ったよりスムーズに構築できると実感しています。
同じように少し試してみたいなと思っている方の参考になれば幸いです!

Discussion