CSRF Cookie未设置:Django-allauth无头模式+NextJS+NextAuth登录失败
解决Django Allauth无头模式 + NextAuth登录403 CSRF错误
核心问题分析
登录请求返回403的本质原因有两个:
- 跨域场景下Django的CSRF Cookie未被正确传递或识别
- 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会话策略 });
额外排查点
- 确认
process.env.SERVER_URL在NextJS中正确设置为http://localhost:8000,无多余后缀 - 查看浏览器开发者工具Application > Cookies,确认
csrftoken和sessionid已存储在localhost域下 - 检查请求Headers,确认
X-CSRFToken已正确携带,且Cookie头包含csrftoken和sessionid
内容的提问来源于stack exchange,提问作者Michael Gribben
相关产品推荐
相关产品推荐

