mcpField
A
auth-patterns

auth-patterns

github.com/davepoon开发3.7k177 次安装今天更新
代码仓库

关于 auth-patterns

auth-patterns 是面向 Next.js 应用开发者的身份验证技能,涵盖 NextAuth.js (Auth.js)、中间件保护、会话和基于角色的访问控制,也介绍会话存储选项与安全实践。

主要功能

  1. 01配置 NextAuth.js v5
  2. 02通过中间件保护路由
  3. 03在服务端和客户端获取会话
  4. 04基于角色的访问控制
  5. 05JWT 与数据库会话
  6. 06身份验证安全实践

适用场景

  1. 01为 Next.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 设置

评论

支持 Markdown