nextjs-authentication について
Next.js 15+ App Router で認証を実装する開発者向けの、Auth.js 5 の実装ガイドです。サインイン、ルート保護、ロールベースのアクセス制御に加え、Server Components、Server Actions、Middleware のセキュリティパターンを扱います。
主な機能
- 01Auth.js 5 と OAuth プロバイダーの設定
- 02Middleware によるルート保護
- 03Server Components でのセッション取得
- 04Server Actions での認証確認
- 05ロールベースのアクセス制御
- 06TypeScript のセッション型拡張
活用シーン
- 01Next.js アプリへの OAuth サインイン追加
- 02認証済みユーザーにダッシュボードを限定
- 03管理パネルのロール権限設定
- 04フォーム操作でのユーザー認証確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill nextjs-authenticationインストール後、Claude Code で/nextjs-authenticationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/authjs-setup.mdbest-practices.mddatabase-adapter.mdexamples.mdoauth-providers.mdtesting-patterns.md詳細
Next.js 認証
概要
Auth.js 5(NextAuth.js)を使用して、Next.js 15 以降の App Router に認証を実装するパターンを提供します。初期設定から、本番環境に対応したロールベースのアクセス制御の実装まで、認証ライフサイクル全体を網羅しています。
使用する場面
- Auth.js 5 を新規に設定する、または OAuth プロバイダーを追加する場合
- Middleware で保護されたルートを実装する場合
- Server Components や Server Actions で認証を処理する場合
- ロールベースのアクセス制御(RBAC)を実装する場合
- 認証情報ベースまたは OAuth によるサインイン/サインアウトのフローを作成する場合
手順
1. 依存関係をインストールする
Next.js App Router 用に Auth.js v5(ベータ版)をインストールします。
npm install next-auth@beta
2. 環境変数を設定する
必要な変数を設定した .env.local を作成します。
# Required for Auth.js
AUTH_SECRET="your-secret-key-here"
AUTH_URL="http://localhost:3000"
# OAuth Providers (add as needed)
GITHUB_ID="your-github-client-id"
GITHUB_SECRET="your-github-client-secret"
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
次のコマンドで AUTH_SECRET を生成します。
openssl rand -base64 32
3. 認証設定を作成する
プロバイダーとコールバックを設定した auth.ts をプロジェクトのルートに作成します。
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import Google from "next-auth/providers/google";
export const {
handlers: { GET, POST },
auth,
signIn,
signOut,
} = NextAuth({
providers: [
GitHub({
clientId: process.env.GITHUB_ID!,
clientSecret: process.env.GITHUB_SECRET!,
}),
Google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (token) {
session.user.id = token.id as string;
}
return session;
},
},
pages: {
signIn: "/login",
error: "/error",
},
});
4. API ルートハンドラーを作成する
app/api/auth/[...nextauth]/route.ts を作成します。
export { GET, POST } from "@/auth";
5. ルート保護用の Middleware を追加する
プロジェクトのルートに middleware.ts を作成します。
import { auth } from "@/auth";
import { NextResponse } from "next/server";
export default auth((req) => {
const { nextUrl } = req;
const isLoggedIn = !!req.auth;
const isApiAuthRoute = nextUrl.pathname.startsWith("/api/auth");
const isPublicRoute = ["/", "/login", "/register"].includes(nextUrl.pathname);
const isProtectedRoute = nextUrl.pathname.startsWith("/dashboard");
if (isApiAuthRoute) return NextResponse.next();
if (!isLoggedIn && isProtectedRoute) {
return NextResponse.redirect(new URL("/login", nextUrl));
}
if (isLoggedIn && nextUrl.pathname === "/login") {
return NextResponse.redirect(new URL("/dashboard", nextUrl));
}
return NextResponse.next();
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.png$).*)"],
};
6. Server Components でセッションにアクセスする
auth() 関数を使用して、Server Components でセッションにアクセスします。
import { auth } from "@/auth";
import { redirect } from "next/navigation";
export default async function DashboardPage() {
const session = await auth();
if (!session) {
redirect("/login");
}
return (
<div>
<h1>Welcome, {session.user.name}</h1>
</div>
);
}
7. Server Actions を保護する
ミューテーションを実行する前に、必ず Server Actions で認証を確認します。
"use server";
import { auth } from "@/auth";
export async function createTodo(formData: FormData) {
const session = await auth();
if (!session?.user) {
throw new Error("Unauthorized");
}
// Proceed with protected action
const title = formData.get("title") as string;
await db.todo.create({
data: { title, userId: session.user.id },
});
}
8. サインイン/サインアウトを処理する
サーバーアクションを使ったログインページを作成します。
// app/login/page.tsx
import { signIn } from "@/auth";
import { redirect } from "next/navigation";
export default function LoginPage() {
async function handleLogin(formData: FormData) {
"use server";
const result = await signIn("credentials", {
email: formData.get("email"),
password: formData.get("password"),
redirect: false,
});
if (result?.error) {
return { error: "Invalid credentials" };
}
redirect("/dashboard");
}
return (
<form action={handleLogin}>
<input name="email" type="email" placeholder="Email" required />
<input name="password" type="password" placeholder="Password" required />
<button type="submit">Sign In</button>
</form>
);
}
クライアント側のサインアウトの場合:
"use client";
import { signOut } from "next-auth/react";
export function SignOutButton() {
return <button onClick={() => signOut()}>Sign Out</button>;
}
9. ロールベースのアクセス制御を実装する
Server Components でロールを確認します。
import { auth } from "@/auth";
import { unauthorized } from "next/navigation";
export default async function AdminPage() {
const session = await auth();
if (session?.user?.role !== "admin") {
unauthorized();
}
return <AdminDashboard />;
}
10. TypeScript の型を拡張する
型安全なセッションを実現するため、types/next-auth.d.ts を作成します。
import { DefaultSession } from "next-auth";
declare module "next-auth" {
interface Session {
user: {
id: string;
role: "user" | "admin";
} & DefaultSession["user"];
}
interface User {
role?: "user" | "admin";
}
}
declare module "next-auth/jwt" {
interface JWT {
id?: string;
role?: "user" | "admin";
}
}
例
例 1:保護されたダッシュボードの実装
入力: 認証済みユーザーだけがアクセスできるダッシュボードが必要です。
実装:
// app/dashboard/page.tsx
import { auth } from "@/auth";
import { redirect } from "next/navigation";
import { getUserTodos } from "@/app/lib/data";
export default async function DashboardPage() {
const session = await auth();
if (!session?.user?.id) {
redirect("/login");
}
const todos = await getUserTodos(session.user.id);
return (
<main>
<h1>Welcome, {session.user.name}</h1>
<p>Email: {session.user.email}</p>
<TodoList todos={todos} />
</main>
);
}
出力: ダッシュボードは認証済みユーザーにのみ表示され、各ユーザー固有のデータが表示されます。
例 2:ロールベースの管理者パネル
入力: 「admin」ロールを持つユーザーだけが管理者パネルにアクセスできるようにします。
実装:
// app/admin/page.tsx
import { auth } from "@/auth";
import { unauthorized } from "next/navigation";
export default async function AdminPage() {
const session = await auth();
if (session?.user?.role !== "admin") {
unauthorized();
}
return (
<main>
<h1>Admin Panel</h1>
<p>Welcome, administrator {session.user.name}</p>
</main>
);
}
出力: 管理者ユーザーにのみパネルが表示され、それ以外のユーザーには 401 エラーが返されます。
例 3:フォームを使った安全な Server Action
入力: 認証済みユーザーだけがフォームを送信できるようにします。
実装:
// app/components/create-todo-form.tsx
"use server";
import { auth } from "@/auth";
import { revalidatePath } from "next/cache";
export async function createTodo(formData: FormData) {
const session = await auth();
if (!session?.user?.id) {
throw new Error("Unauthorized");
}
const title = formData.get("title") as string;
await db.todo.create({
data: {
title,
userId: session.user.id,
},
});
revalidatePath("/dashboard");
}
// Usage in component
export function CreateTodoForm() {
return (
<form action={createTodo}>
<input name="title" placeholder="New todo..." required />
<button type="submit">Add Todo</button>
</form>
);
}
出力: Todo は認証済みユーザーに対してのみ作成され、認証されていないリクエストではエラーがスローされます。
ベストプラクティス
- デフォルトでは Server Components を使う - クライアント側の JavaScript を使わずにセッションへ直接アクセスします
- Client Components を最小限にする - リアクティブなセッション更新が必要な場合にのみ
useSession()を使います - セッション確認をキャッシュする - 同じレンダリング内で繰り返し参照する場合は、React の
cache()を使います - Middleware は楽観的な確認に使う - すばやくリダイレクトしつつ、Server Actions でも必ず再確認します
- Server Actions を API エンドポイントとして扱う - ミューテーションの前に必ず認証します
- シークレットをハードコードしない - すべての認証情報に環境変数を使用します
- 適切なエラー処理を実装する - 適切な HTTP ステータスコードを返します
- TypeScript の型拡張を使う - カスタムフィールド用に NextAuth の型を拡張します
- 認証ロジックを分離する - 一貫した確認を行うため、DAL(データアクセス層)を作成します
- 認証フローをテストする - ユニットテストで
auth()関数をモックします
制約と注意事項
重大な制限事項
- Middleware は Edge ランタイムで実行されます - データベースドライバーなどの Node.js API は使用できません
- Server Components では Cookie を設定できません - Cookie の操作には Server Actions を使います
- セッションコールバックの実行タイミング - セッションの作成時またはアクセス時にのみ呼び出され、リクエストごとには呼び出されません
よくある間違い
// ❌ WRONG: Setting cookies in Server Component
export default async function Page() {
cookies().set("key", "value"); // Won't work
}
// ✅ CORRECT: Use Server Action
async function setCookieAction() {
"use server";
cookies().set("key", "value");
}
// ❌ WRONG: Database queries in Middleware
export default auth(async (req) => {
const user = await db.user.findUnique(); // Won't work in Edge
});
// ✅ CORRECT: Use only Edge-compatible APIs
export default auth(async (req) => {
const session = req.auth; // This works
});
セキュリティに関する考慮事項
- Server Actions では必ず認証を確認します。Middleware だけでは不十分です
- 未認証のアクセスには
unauthorized()を使い、それ以外の場合はredirect()を使います - 機密性の高いトークンは
httpOnlyCookie に保存します - 処理する前に、すべてのユーザー入力を検証します
- 本番環境では HTTPS を使用します
- 適切な Cookie の
sameSite属性を設定します
参考資料
- references/authjs-setup.md - Prisma/Drizzle アダプターを使った Auth.js 5 の完全なセットアップガイド
- references/oauth-providers.md - プロバイダーごとの設定(GitHub、Google、Discord、Auth0 など)
- references/database-adapter.md - Prisma、Drizzle、カスタムアダプターを使ったデータベースセッション管理
- references/testing-patterns.md - Vitest と Playwright を使った認証フローのテスト