NextAuth.js v5 (Auth.js) 実践ガイド - Next.js 15対応の認証実装
Next.js向けの認証ライブラリであるNextAuth.jsは、v5でAuth.jsとしてリブランディングされ、より強力で柔軟な認証システムに進化しました。この記事では、Next.js 15でのAuth.js実装を実践的に解説します。
NextAuth.js v5 (Auth.js) とは?
Auth.jsは、Next.jsアプリケーションに認証機能を追加するためのオープンソースライブラリです。v5では以下の改善が行われました。
v5の主な変更点
- App Router完全対応 - Route HandlersとServer Actionsのネイティブサポート
- TypeScript強化 - より型安全な設計
- シンプルな設定 - 設定ファイルの構造が改善
- Edge Runtime対応 - Vercel Edge、Cloudflare Workersなどで動作
- セキュリティ向上 - CSRF保護の強化
インストールと基本設定
インストール
npm install next-auth@beta
v5はまだベータ版ですが、プロダクション利用も可能です。
環境変数の設定
.env.localファイルを作成します。
# Auth.js シークレット(必須)
AUTH_SECRET=your-secret-here
# Google OAuth(オプション)
AUTH_GOOGLE_ID=your-google-client-id
AUTH_GOOGLE_SECRET=your-google-client-secret
# GitHub OAuth(オプション)
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret
# アプリケーションURL
NEXTAUTH_URL=http://localhost:3000
AUTH_SECRETは以下のコマンドで生成できます。
openssl rand -base64 32
Auth.js設定ファイルの作成
auth.ts(またはauth.js)をプロジェクトルートに作成します。
import NextAuth from "next-auth"
import Google from "next-auth/providers/google"
import GitHub from "next-auth/providers/github"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Google({
clientId: process.env.AUTH_GOOGLE_ID,
clientSecret: process.env.AUTH_GOOGLE_SECRET,
}),
GitHub({
clientId: process.env.AUTH_GITHUB_ID,
clientSecret: process.env.AUTH_GITHUB_SECRET,
}),
],
pages: {
signIn: '/auth/signin',
error: '/auth/error',
},
callbacks: {
async jwt({ token, user, account }) {
if (account && user) {
token.accessToken = account.access_token
token.userId = user.id
}
return token
},
async session({ session, token }) {
session.user.id = token.userId as string
session.accessToken = token.accessToken as string
return session
},
},
})
Route Handlerの設定
Auth.js v5では、Route Handlersを使って認証エンドポイントを設定します。
app/api/auth/[...nextauth]/route.tsを作成します。
import { handlers } from "@/auth"
export const { GET, POST } = handlers
これだけで/api/auth/signin、/api/auth/signout、/api/auth/callback/*などのエンドポイントが自動的に作成されます。
サインインページの作成
カスタムサインインページを作成します。
app/auth/signin/page.tsx
import { signIn } from "@/auth"
export default function SignInPage() {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-50">
<div className="w-full max-w-md space-y-8 rounded-lg bg-white p-8 shadow-md">
<div>
<h2 className="text-center text-3xl font-bold">サインイン</h2>
<p className="mt-2 text-center text-gray-600">
アカウントを選択してログイン
</p>
</div>
<div className="space-y-4">
<form
action={async () => {
"use server"
await signIn("google", { redirectTo: "/dashboard" })
}}
>
<button
type="submit"
className="w-full rounded-lg bg-white border border-gray-300 px-4 py-3 text-gray-700 hover:bg-gray-50 flex items-center justify-center gap-2"
>
<svg className="h-5 w-5" viewBox="0 0 24 24">
{/* Google icon SVG */}
</svg>
Googleでサインイン
</button>
</form>
<form
action={async () => {
"use server"
await signIn("github", { redirectTo: "/dashboard" })
}}
>
<button
type="submit"
className="w-full rounded-lg bg-gray-800 px-4 py-3 text-white hover:bg-gray-900 flex items-center justify-center gap-2"
>
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24">
{/* GitHub icon SVG */}
</svg>
GitHubでサインイン
</button>
</form>
</div>
</div>
</div>
)
}
セッション情報の取得
Server Componentで取得
import { auth } from "@/auth"
export default async function Dashboard() {
const session = await auth()
if (!session?.user) {
redirect('/auth/signin')
}
return (
<div>
<h1>ようこそ、{session.user.name}さん</h1>
<p>メール: {session.user.email}</p>
<img src={session.user.image} alt="プロフィール画像" />
</div>
)
}
Client Componentで取得
"use client"
import { useSession } from "next-auth/react"
export default function UserProfile() {
const { data: session, status } = useSession()
if (status === "loading") {
return <div>読み込み中...</div>
}
if (!session) {
return <div>ログインしていません</div>
}
return (
<div>
<p>ログイン済み: {session.user.email}</p>
</div>
)
}
Client Componentで使用する場合は、app/layout.tsxでSessionProviderをラップする必要があります。
import { SessionProvider } from "next-auth/react"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ja">
<body>
<SessionProvider>{children}</SessionProvider>
</body>
</html>
)
}
ミドルウェアでルート保護
認証が必要なページを保護するために、ミドルウェアを設定します。
middleware.ts
import { auth } from "@/auth"
import { NextResponse } from "next/server"
export default auth((req) => {
const { pathname } = req.nextUrl
const isLoggedIn = !!req.auth
// 保護されたルート
const protectedRoutes = ['/dashboard', '/profile', '/settings']
const isProtectedRoute = protectedRoutes.some(route =>
pathname.startsWith(route)
)
if (isProtectedRoute && !isLoggedIn) {
const signInUrl = new URL('/auth/signin', req.url)
signInUrl.searchParams.set('callbackUrl', pathname)
return NextResponse.redirect(signInUrl)
}
// ログイン済みユーザーが認証ページにアクセス
if (pathname.startsWith('/auth') && isLoggedIn) {
return NextResponse.redirect(new URL('/dashboard', req.url))
}
return NextResponse.next()
})
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
}
データベース連携(Prisma)
ユーザー情報をデータベースに保存する場合、Prismaアダプターを使用します。
インストール
npm install @auth/prisma-adapter @prisma/client
npm install -D prisma
Prismaスキーマ
prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
model Account {
id String @id @default(cuid())
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(cuid())
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])
}
Auth.js設定にアダプターを追加
import { PrismaAdapter } from "@auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
const prisma = new PrismaClient()
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
Google({
clientId: process.env.AUTH_GOOGLE_ID,
clientSecret: process.env.AUTH_GOOGLE_SECRET,
}),
],
// ... その他の設定
})
Server Actionsでのサインアウト
import { signOut } from "@/auth"
export default function SignOutButton() {
return (
<form
action={async () => {
"use server"
await signOut({ redirectTo: "/" })
}}
>
<button type="submit">サインアウト</button>
</form>
)
}
まとめ
Auth.js v5は、Next.js 15のApp Routerに最適化された強力な認証ソリューションです。
主要機能:
- OAuth(Google、GitHub等)の簡単統合
- データベース連携
- ミドルウェアによるルート保護
- Server ActionsとRoute Handlersのネイティブサポート
- TypeScriptによる型安全性
公式ドキュメント: https://authjs.dev/
Auth.jsを使えば、セキュアな認証システムを数時間で構築できます。プロダクション環境でも安心して使用できる、実績のあるソリューションです。