7.1 Auth.js v5 概述
Auth.js(前身为 NextAuth.js)是 Next.js 生态中最成熟的认证库,v5 版本专为 App Router 重写,支持 Next.js 15 和 React 19。它提供了开箱即用的 OAuth(GitHub、Google 等)、邮箱/密码、魔术链接等多种认证方式,同时内置了 JWT 和数据库 Session 两种 Session 策略。
- Provider 认证提供者,定义如何验证用户身份。内置了 50+ OAuth 提供者(GitHub、Google、Discord 等)和 Credentials(自定义用户名/密码)提供者。
- Session 用户登录后的会话信息,存储用户 ID、邮箱等基本信息。支持 JWT(无状态)和数据库(有状态)两种策略。
- JWT Strategy Session 数据加密存储在 Cookie 中,无需数据库查询,适合无状态部署(如 Edge Functions)。
- Database Strategy Session 数据存储在数据库中,Cookie 中只有 Session ID,支持服务端强制注销。需要 Adapter 适配数据库。
- Adapter 连接 Auth.js 与数据库的适配器,将用户、账号、Session 等数据自动同步到数据库。Prisma Adapter 是最常用选择。
7.2 安装与基础配置
# 安装 Auth.js v5 和 Prisma Adapter
pnpm add next-auth@beta @auth/prisma-adapter
SHELL
// auth.ts — Auth.js 核心配置(项目根目录)
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Google from 'next-auth/providers/google'
import { PrismaAdapter } from '@auth/prisma-adapter'
import { db } from '@/lib/db'
export const {
handlers, // GET/POST Route Handler
signIn, // 服务端调用登录
signOut, // 服务端调用注销
auth, // 获取当前 Session
} = NextAuth({
adapter: PrismaAdapter(db),
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!,
}),
],
session: { strategy: 'jwt' }, // 或 'database'
callbacks: {
// 将 user.id 注入 JWT token
jwt({ token, user }) {
if (user) token.id = user.id
return token
},
// 将 user.id 暴露给 session
session({ session, token }) {
session.user.id = token.id as string
return session
},
},
pages: {
signIn: '/login', // 自定义登录页
error: '/auth/error' // 自定义错误页
},
})
TS
// app/api/auth/[...nextauth]/route.ts — 必须创建此文件
export { handlers as GET, handlers as POST } from '@/auth'
TS
7.3 Credentials 认证(用户名/密码)
Credentials Provider 允许自定义认证逻辑,适用于已有用户名/密码系统的场景。需要注意:Credentials 认证不支持数据库 Session(因为安全考虑),必须使用 JWT Strategy。
import Credentials from 'next-auth/providers/credentials'
import bcrypt from 'bcryptjs'
import { z } from 'zod'
const LoginSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
// 在 providers 数组中添加
Credentials({
async authorize(credentials) {
const parsed = LoginSchema.safeParse(credentials)
if (!parsed.success) return null
const { email, password } = parsed.data
const user = await db.user.findUnique({
where: { email }
})
if (!user || !user.password) return null
// bcrypt 对比密码哈希
const passwordMatch = await bcrypt.compare(
password,
user.password
)
if (!passwordMatch) return null
// 返回用户对象(不要包含密码)
return { id: user.id, email: user.email, name: user.name }
}
})
TS
密码存储安全永远不要以明文存储密码。注册时使用 bcrypt.hash(password, 12) 生成哈希(数字越大越安全,12是推荐值)。永远不要将哈希密码包含在 session 或响应中。
7.4 在不同位置获取 Session
Auth.js v5 提供统一的 auth() 函数,可以在 Server Component、API Route、Server Action 和 middleware 中获取当前用户 Session。
// 1. Server Component(最常用)
import { auth } from '@/auth'
export default async function ProfilePage() {
const session = await auth()
if (!session?.user) {
redirect('/login')
}
return <p>你好,{session.user.name}</p>
}
// 2. Server Action
'use server'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) throw new Error('Unauthorized')
await db.post.create({
data: { ...data, authorId: session.user.id }
})
}
// 3. Client Component — 使用 useSession
'use client'
import { useSession } from 'next-auth/react'
function UserMenu() {
const { data: session, status } = useSession()
if (status === 'loading') return <p>加载中</p>
if (!session) return <a href="/login">登录</a>
return <p>{session.user.name}</p>
}
TSX
7.5 Middleware 路由保护
使用 middleware.ts 可以在请求到达页面之前进行认证检查,是保护整批路由最高效的方式(在 Edge 上运行,无需启动 Node.js 进程)。
// middleware.ts(项目根目录)
export { auth as default } from '@/auth'
// 或者自定义逻辑:
import { auth } from '@/auth'
import { NextResponse } from 'next/server'
export default auth((req) => {
const { nextUrl } = req
const isLoggedIn = !!req.auth
const isProtected = nextUrl.pathname.startsWith('/dashboard')
if (isProtected && !isLoggedIn) {
const redirectUrl = new URL('/login', nextUrl)
redirectUrl.searchParams.set('callbackUrl', nextUrl.pathname)
return NextResponse.redirect(redirectUrl)
}
})
export const config = {
// 匹配所有路径,排除静态资源和 API
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
TS
7.6 登录页与登录按钮
// app/login/page.tsx — 自定义登录页
import { signIn } from '@/auth'
export default function LoginPage() {
return (
<div className="login-page">
<h1>登录</h1>
{/* GitHub OAuth 登录 */}
<form
action={async () => {
'use server'
await signIn('github', { redirectTo: '/dashboard' })
}}
>
<button type="submit">使用 GitHub 登录</button>
</form>
{/* Google OAuth 登录 */}
<form
action={async () => {
'use server'
await signIn('google', { redirectTo: '/dashboard' })
}}
>
<button type="submit">使用 Google 登录</button>
</form>
</div>
)
}
// 退出登录按钮
import { signOut } from '@/auth'
function SignOutButton() {
return (
<form
action={async () => {
'use server'
await signOut({ redirectTo: '/' })
}}
>
<button type="submit">退出登录</button>
</form>
)
}
TSX
SessionProvider 配置如果需要在 Client Component 中使用 useSession,需要在根布局的 Providers 中添加 <SessionProvider>(来自 next-auth/react)。Server Component 直接调用 auth() 无需 Provider。