mcpField
A
auth-patterns

auth-patterns

github.com/davepoon開発3.7k177 回インストール今日更新
リポジトリ

auth-patterns について

auth-patterns は、Next.js アプリケーション開発者向けの認証スキルです。NextAuth.js (Auth.js)、ミドルウェアによる保護、セッション、ロールベースのアクセス制御に加え、保存方式やセキュリティの実践方法を扱います。

主な機能

  1. 01NextAuth.js v5 の設定
  2. 02ミドルウェアによるルート保護
  3. 03サーバーとクライアントでのセッション取得
  4. 04ロールベースのアクセス制御
  5. 05JWT とデータベースセッション
  6. 06認証のセキュリティ対策

活用シーン

  1. 01Next.js アプリへのログイン追加
  2. 02ダッシュボードや API ルートの保護
  3. 03ユーザーロール別のページ制限
  4. 04セッション保存方式の選択

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/davepoon/buildwithclaude --skill auth-patterns

インストール後、Claude Code で/auth-patternsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

examples/
nextauth-setup.md
references/
middleware-auth.md
session-management.md

詳細

Next.js の認証パターン

概要

Next.js では、複数の認証方式を利用できます。このスキルでは、NextAuth.js(Auth.js)、ミドルウェアベースの保護、セッション管理など、一般的なパターンを取り上げます。

認証ライブラリ

ライブラリ最適な用途
NextAuth.js (Auth.js)プロバイダーを備えた多機能な認証
Clerkマネージド認証サービス
Lucia軽量で柔軟な認証
Supabase AuthSupabase エコシステム
Custom JWT完全な制御

NextAuth.js v5 のセットアップ

インストール
npm install next-auth@beta
設定
// auth.ts
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Credentials from 'next-auth/providers/credentials'

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    GitHub({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
    Credentials({
      credentials: {
        email: { label: 'Email', type: 'email' },
        password: { label: 'Password', type: 'password' },
      },
      authorize: async (credentials) => {
        const user = await getUserByEmail(credentials.email)
        if (!user || !verifyPassword(credentials.password, user.password)) {
          return null
        }
        return user
      },
    }),
  ],
  callbacks: {
    authorized: async ({ auth }) => {
      return !!auth
    },
  },
})
API ルートハンドラー
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'

export const { GET, POST } = handlers
ミドルウェアによる保護
// middleware.ts
export { auth as middleware } from '@/auth'

export const config = {
  matcher: ['/dashboard/:path*', '/api/protected/:path*'],
}

セッションデータの取得

サーバーコンポーネントの場合
// app/dashboard/page.tsx
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>
  )
}
クライアントコンポーネントの場合
// components/user-menu.tsx
'use client'

import { useSession } from 'next-auth/react'

export function UserMenu() {
  const { data: session, status } = useSession()

  if (status === 'loading') {
    return <div>Loading...</div>
  }

  if (!session) {
    return <SignInButton />
  }

  return (
    <div>
      <span>{session.user?.name}</span>
      <SignOutButton />
    </div>
  )
}
セッションプロバイダーのセットアップ
// app/providers.tsx
'use client'

import { SessionProvider } from 'next-auth/react'

export function Providers({ children }: { children: React.ReactNode }) {
  return <SessionProvider>{children}</SessionProvider>
}

// app/layout.tsx
import { Providers } from './providers'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}

サインイン/サインアウトコンポーネント

// components/auth-buttons.tsx
import { signIn, signOut } from '@/auth'

export function SignInButton() {
  return (
    <form
      action={async () => {
        'use server'
        await signIn('github')
      }}
    >
      <button type="submit">Sign in with GitHub</button>
    </form>
  )
}

export function SignOutButton() {
  return (
    <form
      action={async () => {
        'use server'
        await signOut()
      }}
    >
      <button type="submit">Sign out</button>
    </form>
  )
}

ミドルウェアベースの認証

基本パターン
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const protectedRoutes = ['/dashboard', '/settings', '/api/protected']
const authRoutes = ['/login', '/signup']

export function middleware(request: NextRequest) {
  const token = request.cookies.get('session')?.value
  const { pathname } = request.nextUrl

  // Redirect authenticated users away from auth pages
  if (authRoutes.some(route => pathname.startsWith(route))) {
    if (token) {
      return NextResponse.redirect(new URL('/dashboard', request.url))
    }
    return NextResponse.next()
  }

  // Protect routes
  if (protectedRoutes.some(route => pathname.startsWith(route))) {
    if (!token) {
      const loginUrl = new URL('/login', request.url)
      loginUrl.searchParams.set('callbackUrl', pathname)
      return NextResponse.redirect(loginUrl)
    }
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}
JWT 検証を使用する場合
// middleware.ts
import { NextResponse } from 'next/server'
import { jwtVerify } from 'jose'

const secret = new TextEncoder().encode(process.env.JWT_SECRET)

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('token')?.value

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  try {
    const { payload } = await jwtVerify(token, secret)
    // Token is valid, continue
    return NextResponse.next()
  } catch {
    // Token is invalid
    return NextResponse.redirect(new URL('/login', request.url))
  }
}

ロールベースのアクセス制御

セッション型の拡張
// types/next-auth.d.ts
import { DefaultSession } from 'next-auth'

declare module 'next-auth' {
  interface Session {
    user: {
      role: 'user' | 'admin'
    } & DefaultSession['user']
  }
}

// auth.ts
export const { handlers, auth } = NextAuth({
  callbacks: {
    session: ({ session, token }) => ({
      ...session,
      user: {
        ...session.user,
        role: token.role,
      },
    }),
    jwt: ({ token, user }) => {
      if (user) {
        token.role = user.role
      }
      return token
    },
  },
})
ロールベースのコンポーネント
// components/admin-only.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'

export async function AdminOnly({ children }: { children: React.ReactNode }) {
  const session = await auth()

  if (session?.user?.role !== 'admin') {
    redirect('/unauthorized')
  }

  return <>{children}</>
}

// Usage
export default async function AdminPage() {
  return (
    <AdminOnly>
      <AdminDashboard />
    </AdminOnly>
  )
}

セッションの保存オプション

JWT(ステートレス)
// auth.ts
export const { auth } = NextAuth({
  session: { strategy: 'jwt' },
  // JWT stored in cookies, no database needed
})
データベースセッション
// auth.ts
import { PrismaAdapter } from '@auth/prisma-adapter'
import { prisma } from '@/lib/prisma'

export const { auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  session: { strategy: 'database' },
  // Sessions stored in database
})

カスタムログインページ

// app/login/page.tsx
'use client'

import { signIn } from 'next-auth/react'
import { useSearchParams } from 'next/navigation'

export default function LoginPage() {
  const searchParams = useSearchParams()
  const callbackUrl = searchParams.get('callbackUrl') || '/dashboard'

  return (
    <div className="flex flex-col gap-4">
      <button
        onClick={() => signIn('github', { callbackUrl })}
        className="btn"
      >
        Sign in with GitHub
      </button>
      <button
        onClick={() => signIn('google', { callbackUrl })}
        className="btn"
      >
        Sign in with Google
      </button>
    </div>
  )
}

セキュリティのベストプラクティス

  1. 本番環境では HTTPS を使用します
  2. セキュアな Cookie フラグ(HttpOnly、Secure、SameSite)を設定します
  3. CSRF 保護を実装します(NextAuth に組み込み済み)
  4. オープンリダイレクトを防ぐために、リダイレクト URL を検証します
  5. シークレットには環境変数を使用します
  6. 認証エンドポイントにレート制限を実装します
  7. bcrypt または argon2 でパスワードをハッシュ化します

リソース

詳しいパターンについては、以下を参照してください。

  • references/middleware-auth.md - 高度なミドルウェアパターン
  • references/session-management.md - セッション方式
  • examples/nextauth-setup.md - NextAuth.js の完全なセットアップ

コメント