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

CSRF Cookie未设置:Django-allauth无头模式+NextJS+NextAuth登录失败

解决Django Allauth无头模式 + NextAuth登录403 CSRF错误

核心问题分析

登录请求返回403的本质原因有两个:

  1. 跨域场景下Django的CSRF Cookie未被正确传递或识别
  2. Allauth的browser/v1/auth/login端点默认不接收JSON格式请求,当前用JSON.stringify提交的数据不符合要求

步骤1:修正Django配置

补充Cookie跨域参数

修改settings.py,添加以下配置确保跨域时Cookie能被正确传递:

# 跨域Cookie适配
CSRF_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SAMESITE = "Lax"
CSRF_COOKIE_HTTPONLY = False  # 允许前端读取CSRF Cookie(便于调试)
SESSION_COOKIE_HTTPONLY = True  # Session Cookie保持HttpOnly保障安全

优化CSRF令牌视图

无需手动设置Cookie,Django的get_token方法会自动处理,修改views.py中的视图:

from django.http import JsonResponse
from django.middleware.csrf import get_token

def get_csrf_token(request):
    # 强制初始化Session,否则CSRF令牌无法生成
    if not request.session.session_key:
        request.session.create()
    token = get_token(request)
    return JsonResponse({"csrfToken": token})

步骤2:调整NextJS的CSRF获取逻辑

确保前端获取CSRF令牌时,Cookie被正确存储到浏览器:

// ./utils/csrfCookie.ts
export async function getCsrfToken() {
  const server_url = process.env.SERVER_URL;
  const response = await fetch(`${server_url}/csrf-token/`, {
    credentials: "include",
    method: "GET",
    headers: { "Accept": "application/json" },
  });

  if (!response.ok) throw new Error("Failed to fetch CSRF token");

  const data = await response.json();
  // 验证Cookie中的令牌是否与返回值一致
  const cookieToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken='))?.split('=')[1];
  console.log("CSRF Token: API=", data.csrfToken, "Cookie=", cookieToken);
  return data.csrfToken;
}

步骤3:修正NextAuth登录请求格式

Allauth的browser/v1/auth/login端点默认只接受application/x-www-form-urlencoded格式的表单数据,修改auth.ts中的请求逻辑:

// auth.ts
import { getCsrfToken } from "./utils/csrfCookie";
import Credentials from "next-auth/providers/credentials";
import NextAuth from "next-auth";

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Credentials({
      credentials: {
        email: { label: "Email", type: "text" },
        password: { label: "Password", type: "password" },
      },
      authorize: async (credentials) => {
        if (!credentials?.email || !credentials?.password) return null;
        
        const server_url = process.env.SERVER_URL;
        try {
          const csrfToken = await getCsrfToken();
          
          // 用URLSearchParams构建符合要求的表单数据
          const formData = new URLSearchParams();
          formData.append("email", credentials.email);
          formData.append("password", credentials.password);

          const response = await fetch(
            `${server_url}/_allauth/browser/v1/auth/login`,
            {
              method: "POST",
              body: formData,
              credentials: "include",
              headers: { "X-CSRFToken": csrfToken },
            },
          );

          if (!response.ok) {
            const errorData = await response.json().catch(() => null);
            console.error("Login failed:", response.status, errorData);
            throw new Error(errorData?.detail || "Login failed");
          }

          const user = await response.json();
          // 返回NextAuth要求的标准用户结构
          return {
            id: user.user.id,
            email: user.user.email,
            name: user.user.name,
            token: user.access_token,
          };
        } catch (error) {
          console.error("Authorization error:", error);
          return null; // NextAuth自动处理登录失败
        }
      },
    }),
  ],
  session: { strategy: "jwt" }, // 可选:使用JWT会话策略
});

额外排查点

  1. 确认process.env.SERVER_URL在NextJS中正确设置为http://localhost:8000,无多余后缀
  2. 查看浏览器开发者工具Application > Cookies,确认csrftoken和sessionid已存储在localhost域下
  3. 检查请求Headers,确认X-CSRFToken已正确携带,且Cookie头包含csrftoken和sessionid

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:24:55