nextjs-authentication
Next.js 15以降のApp RouterにおけるAuth.js 5(NextAuth.js)を使った認証実装パターンを提供します。認証フローのセットアップ、保護されたルートの実装、Server ComponentsやServer Actionsでのセッション管理、OAuthプロバイダーの設定、ロールベースのアクセス制御、またはサインイン・サインアウト処理を実装する際に活用してください。
description の原文を見る
Provides authentication implementation patterns for Next.js 15+ App Router using Auth.js 5 (NextAuth.js). Use when setting up authentication flows, implementing protected routes, managing sessions in Server Components and Server Actions, configuring OAuth providers, implementing role-based access control, or handling sign-in/sign-out flows in Next.js applications.
SKILL.md 本文
Next.js 認証
概要
Next.js 15+ App Router 向けの Auth.js 5 (NextAuth.js) を使用した認証実装パターンを提供します。初期セットアップから本番環境対応のロールベースアクセス制御実装まで、認証ライフサイクル全体をカバーしています。
使用する場面
- Auth.js 5 を最初からセットアップするか、OAuth プロバイダーを追加する場合
- Middleware を使用した保護されたルートの実装
- Server Components と Server Actions での認証処理
- ロールベースアクセス制御 (RBAC) の実装
- 認証情報ベースまたは OAuth サインイン・サインアウトフローの作成
手順
1. 依存関係のインストール
Next.js App Router 用の Auth.js v5 (beta) をインストール:
npm install next-auth@beta
2. 環境変数の設定
.env.local に必要な変数を作成:
# Auth.js に必須
AUTH_SECRET="your-secret-key-here"
AUTH_URL="http://localhost:3000"
# OAuth プロバイダー (必要に応じて追加)
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");
}
// 保護されたアクションを実行
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");
}
// コンポーネント内での使用
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 (Data Access Layer) を作成
- 認証フローをテスト - ユニットテストで
auth()関数をモック
制約事項と警告
重大な制限
- Middleware は Edge ランタイムで実行 - データベースドライバーなどの Node.js API は使用できない
- Server Components はクッキーを設定できない - クッキー操作には Server Actions を使用
- セッションコールバックのタイミング - セッション作成・アクセス時のみ呼び出され、すべてのリクエストで呼び出されない
よくある間違い
// ❌ 間違い: Server Component でクッキーを設定
export default async function Page() {
cookies().set("key", "value"); // 動作しません
}
// ✅ 正しい: Server Action を使用
async function setCookieAction() {
"use server";
cookies().set("key", "value");
}
// ❌ 間違い: Middleware でデータベースクエリ実行
export default auth(async (req) => {
const user = await db.user.findUnique(); // Edge では動作しません
});
// ✅ 正しい: Edge 互換の API のみ使用
export default auth(async (req) => {
const session = req.auth; // これは動作します
});
セキュリティに関する考慮事項
- Server Actions での認証を常に検証 - Middleware だけでは不十分
- 認可されていないアクセスには
unauthorized()を、その他の場合はredirect()を使用 - 機密トークンを
httpOnlyクッキーに保存 - 処理前にすべてのユーザー入力を検証
- 本番環境では HTTPS を使用
- 適切なクッキー
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 を使用した認証フローのテスト
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- giuseppe-trisciuoglio
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/giuseppe-trisciuoglio/developer-kit / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。