Next.js 認証(Auth.js v5)
ルール
- 設定は
auth.ts に集約する(NextAuth() のエクスポート: handlers、auth、signIn、signOut)。プロバイダーのシークレットを分散させないこと。
- ルートは
middleware.ts(エッジセーフな auth ラッパー)で保護し、Server Actions / ミューテーション内でセッションを再チェックする。ミドルウェアは UX であり、認可ではない。
- Server Components では
await auth() でセッションを読み取る。Client Components では <SessionProvider> と共に next-auth/react の useSession() を使用する。
- シークレット(
AUTH_SECRET、プロバイダーキー)を NEXT_PUBLIC_ プレフィックスで公開しない。起動時に Zod で env を検証する。
- ロールチェック: モジュール拡張(
user.role)を介してセッションタイプを拡張し、サーバー側で強制する。
パターン
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [GitHub],
callbacks: {
async session({ session, token }) {
if (session.user) (session.user as { role?: string }).role = token.role as string;
return session;
},
async jwt({ token, user }) {
if (user) token.role = (user as { role?: string }).role ?? 'user';
return token;
},
},
});
export { auth as middleware } from '@/auth';
export const config = { matcher: ['/dashboard/:path*', '/admin/:path*'] };
import { handlers } from '@/auth';
export const { GET, POST } = handlers;
import { auth } from '@/auth';
import { redirect } from 'next/navigation';
export default async function AdminPage() {
const session = await auth();
if (!session?.user) redirect('/login');
if ((session.user as { role?: string }).role !== 'admin')
redirect('/dashboard');
return <AdminView />;
}
import 'next-auth';
declare module 'next-auth' {
interface User { role?: string }
}
チェックリスト