NextAuth身份验证配置技能Skill nextauth

这是一个用于在 Next.js 应用中实现专业级身份验证的专家技能。它专注于配置和管理 NextAuth.js (Auth.js),涵盖 OAuth 提供商(如 Google、GitHub)、数据库适配器(Prisma、Drizzle)、JWT 会话处理、自定义回调、受保护路由中间件以及基于角色的访问控制(RBAC)。提供从基础配置到高级安全实践的全套解决方案,帮助开发者快速构建安全、可扩展的用户认证系统。关键词:NextAuth.js, Next.js 身份验证, OAuth 登录, JWT 会话, 数据库适配器, RBAC 权限控制, 安全中间件

前端开发 1 次安装 2 次浏览 更新于 2/26/2026

名称: nextauth 描述: NextAuth.js (Auth.js) 配置,包括身份提供者、适配器、会话管理、回调和 JWT 处理。 允许的工具: 读取、写入、编辑、Bash、Glob、Grep

NextAuth 技能

为使用 NextAuth.js (Auth.js) 在 Next.js 应用程序中实现身份验证提供专家级协助。

能力

  • 配置 OAuth 身份提供者(谷歌、GitHub 等)
  • 设置基于凭据的身份验证
  • 实现数据库适配器(Prisma、Drizzle)
  • 处理 JWT 和会话回调
  • 配置受保护的路由和中间件
  • 实现基于角色的访问控制

使用场景

当您需要时,请调用此技能:

  • 为 Next.js 应用添加身份验证
  • 配置 OAuth 身份提供者
  • 设置数据库会话
  • 实现身份验证中间件
  • 处理用户角色和权限

输入参数

参数 类型 是否必需 描述
providers 数组 要配置的身份验证提供者
adapter 字符串 数据库适配器(prisma, drizzle)
sessionStrategy 字符串 jwt 或 database
callbacks 数组 需要的自定义回调

配置示例

{
  "providers": ["google", "github", "credentials"],
  "adapter": "prisma",
  "sessionStrategy": "jwt",
  "callbacks": ["jwt", "session", "signIn"]
}

实现模式

基础配置(App Router)

// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import { authOptions } from '@/lib/auth';

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

// lib/auth.ts
import { NextAuthOptions } from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import GitHubProvider from 'next-auth/providers/github';
import CredentialsProvider from 'next-auth/providers/credentials';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from '@/lib/prisma';
import bcrypt from 'bcryptjs';

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    GitHubProvider({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
    CredentialsProvider({
      name: 'credentials',
      credentials: {
        email: { label: '邮箱', type: 'email' },
        password: { label: '密码', type: 'password' },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          throw new Error('无效的凭据');
        }

        const user = await prisma.user.findUnique({
          where: { email: credentials.email },
        });

        if (!user || !user.hashedPassword) {
          throw new Error('无效的凭据');
        }

        const isValid = await bcrypt.compare(
          credentials.password,
          user.hashedPassword
        );

        if (!isValid) {
          throw new Error('无效的凭据');
        }

        return user;
      },
    }),
  ],
  session: {
    strategy: 'jwt',
  },
  pages: {
    signIn: '/login',
    error: '/auth/error',
  },
  callbacks: {
    async jwt({ token, user, account }) {
      if (user) {
        token.id = user.id;
        token.role = user.role;
      }
      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user.id = token.id as string;
        session.user.role = token.role as string;
      }
      return session;
    },
    async signIn({ user, account, profile }) {
      // 自定义登录逻辑
      return true;
    },
  },
};

类型扩展

// types/next-auth.d.ts
import { DefaultSession, DefaultUser } from 'next-auth';
import { JWT, DefaultJWT } from 'next-auth/jwt';

declare module 'next-auth' {
  interface Session {
    user: {
      id: string;
      role: string;
    } & DefaultSession['user'];
  }

  interface User extends DefaultUser {
    role: string;
  }
}

declare module 'next-auth/jwt' {
  interface JWT extends DefaultJWT {
    id: string;
    role: string;
  }
}

身份验证中间件

// middleware.ts
import { withAuth } from 'next-auth/middleware';
import { NextResponse } from 'next/server';

export default withAuth(
  function middleware(req) {
    const token = req.nextauth.token;
    const isAdmin = token?.role === 'admin';
    const isAdminRoute = req.nextUrl.pathname.startsWith('/admin');

    if (isAdminRoute && !isAdmin) {
      return NextResponse.redirect(new URL('/unauthorized', req.url));
    }

    return NextResponse.next();
  },
  {
    callbacks: {
      authorized: ({ token }) => !!token,
    },
  }
);

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

服务端身份验证检查

// app/dashboard/page.tsx
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { redirect } from 'next/navigation';

export default async function DashboardPage() {
  const session = await getServerSession(authOptions);

  if (!session) {
    redirect('/login');
  }

  return (
    <div>
      <h1>欢迎,{session.user.name}</h1>
      <p>角色: {session.user.role}</p>
    </div>
  );
}

客户端身份验证钩子

// components/user-menu.tsx
'use client';

import { useSession, signIn, signOut } from 'next-auth/react';

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

  if (status === 'loading') {
    return <div>加载中...</div>;
  }

  if (!session) {
    return (
      <button onClick={() => signIn()}>
        登录
      </button>
    );
  }

  return (
    <div>
      <img src={session.user.image} alt={session.user.name} />
      <span>{session.user.name}</span>
      <button onClick={() => signOut()}>
        退出登录
      </button>
    </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,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Prisma 数据模型

model User {
  id             String    @id @default(cuid())
  name           String?
  email          String?   @unique
  emailVerified  DateTime?
  image          String?
  hashedPassword String?
  role           String    @default("user")
  accounts       Account[]
  sessions       Session[]
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?
  user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

最佳实践

  • 在无服务器部署中使用 JWT 策略
  • 使用必要的用户数据扩展会话
  • 实现适当的 CSRF 保护
  • 使用中间件进行路由保护
  • 将密钥存储在环境变量中

目标流程

  • nextjs-全栈开发
  • oauth-社交登录
  • jwt-身份验证
  • rbac-实现