You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决Next.js 13中NextAuth登录302重定向与csrf=true错误

解决NextAuth多路由登录时302重定向404问题

问题概述

在Next.js 13中配置了两个独立的NextAuth路由(管理员/api/auth/admin/[...nextauth]/route.ts和用户/api/auth/user/[...nextauth]/route.ts),调用管理员登录接口时出现302重定向到/api/auth/signin导致404,URL附带csrf=true参数。

核心原因

  1. 多NextAuth实例未做配置隔离,导致会话存储、路由匹配冲突
  2. 登录请求端点错误,使用了跳转用的signin路径而非凭据验证的callback路径
  3. 请求数据格式不符合CredentialsProvider的默认要求,触发登录失败重定向
  4. Middleware规则冲突,同时导出默认和自定义逻辑导致路由判断异常

解决方案

1. 为每个NextAuth实例配置隔离参数

修改管理员路由,添加basePath、独立secret和JWT会话策略,避免多实例冲突:

import { NextAuthOptions } from "next-auth";
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";

const authOption: NextAuthOptions = {
    secret: process.env.NEXTAUTH_ADMIN_SECRET, // 管理员独立密钥
    basePath: "/api/auth/admin", // 指定当前实例的基础路径
    session: {
        strategy: "jwt", // 使用JWT会话,避免共享数据库会话
    },
    providers: [
        CredentialsProvider({
            name: "Credentials",
            credentials: {
                email: { label: "email", type: "text", placeholder: "jsmith" },
                password: { label: "Password", type: "password" }
            },
            async authorize(credentials, req) {
                // 这里替换为实际的管理员验证逻辑
                const user = { id: "1", name: "J Smith", email: "jsmith@example.com" }
                console.log(credentials?.email);

                return user ? user : null;
            }
        })
    ],
    pages: {
        signIn: '/auth/admin',
        error: '/auth/admin/',
    },
    callbacks: {
        async signIn({ user, account, profile, email, credentials }) {
            return true;
        },
    }
}
const handler = NextAuth(authOption);

export { handler as GET, handler as POST }

用户路由同理,仅修改以下参数:

  • secret: process.env.NEXTAUTH_USER_SECRET
  • basePath: "/api/auth/user"
  • pages.signIn: '/auth/user'

同时在.env文件中添加两个独立密钥:

NEXTAUTH_ADMIN_SECRET=your_admin_unique_secret_key
NEXTAUTH_USER_SECRET=your_user_unique_secret_key

2. 修正登录请求端点与数据格式

将登录请求的端点改为凭据验证专用的callback/credentials,并使用form-urlencoded格式(CredentialsProvider默认要求):

const csrf = await fetch('/api/auth/admin/csrf');
const { csrfToken } = await csrf.json();

// 转换formData为表单格式
const formDataParams = new URLSearchParams();
for (const [key, value] of Object.entries(formData)) {
    formDataParams.append(key, value);
}

const response = await fetch('/api/auth/admin/callback/credentials', {
    method: 'POST',
    headers: {
        'X-CSRF-Token': csrfToken,
    },
    body: formDataParams,
    redirect: 'manual'
});

if (response.ok) {
    // 登录成功后获取会话信息
    const sessionRes = await fetch('/api/auth/admin/session');
    const session = await sessionRes.json();
    console.log('管理员登录成功', session);
} else {
    const errorData = await response.json();
    console.error('管理员登录失败', errorData);
}

3. 重构Middleware逻辑,避免规则冲突

移除默认的NextAuth middleware导出,整合自定义验证逻辑,指定每个路由对应的Auth实例参数:

import { NextRequest, NextResponse } from "next/server";
import { getToken } from "next-auth/jwt";

export const config = { matcher: ["/admin/:path*", "/user/:path*"] }

export async function middleware(request: NextRequest) {
    // 管理员路由验证
    if (request.nextUrl.pathname.startsWith('/admin')) {
        const token = await getToken({ 
            req: request, 
            secret: process.env.NEXTAUTH_ADMIN_SECRET,
            basePath: "/api/auth/admin"
        });
        if (!token) {
            return NextResponse.redirect(new URL('/auth/admin', request.url));
        }
    }

    // 用户路由验证
    if (request.nextUrl.pathname.startsWith('/user')) {
        const token = await getToken({ 
            req: request, 
            secret: process.env.NEXTAUTH_USER_SECRET,
            basePath: "/api/auth/user"
        });
        if (!token) {
            return NextResponse.redirect(new URL('/auth/user', request.url));
        }
    }

    return NextResponse.next();
}

4. 自定义登录页面表单提交优化

如果使用自定义登录页面(如app/auth/admin/page.tsx),确保表单提交指向正确的端点:

export default function AdminLoginPage() {
    const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        const formData = new FormData(e.currentTarget);
        const csrfRes = await fetch('/api/auth/admin/csrf');
        const { csrfToken } = await csrfRes.json();

        const response = await fetch('/api/auth/admin/callback/credentials', {
            method: 'POST',
            headers: {
                'X-CSRF-Token': csrfToken,
            },
            body: formData,
            redirect: 'follow'
        });

        if (response.ok) {
            window.location.href = '/admin';
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <input type="email" name="email" required placeholder="管理员邮箱" />
            <input type="password" name="password" required placeholder="管理员密码" />
            <button type="submit">登录</button>
        </form>
    );
}

总结

通过隔离多NextAuth实例的配置参数、修正登录请求的端点与格式、重构Middleware逻辑,即可解决登录时302重定向到错误路径导致的404问题。

内容的提问来源于stack exchange,提问作者jojo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 06:35:13