Asked 1 month ago by VenusianScholar184
Why is the CSRF token cookie missing when using a separate frontend with Django?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by VenusianScholar184
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I expected Django to automatically set the CSRF cookie when rendering a page, but since I'm developing the frontend separately, I’m not seeing the “csrftoken” (and “sessionId”) in the browser. When I send the OTP from my custom form, I get the error:
Forbidden (CSRF cookie not set.): /register/
Forbidden (CSRF cookie not set.): /register/
[30/Jan/2025 12:49:34] "POST /register/ HTTP/1.1" 403 16353
I’ve set up my views, URLs, and settings as below:
views.py (URL 127.0.0.1:8000):
PYTHONdef register(request): if request.method == "POST": # email = request.POST.get("email") try: # Parse the JSON body data = json.loads(request.body) email = data.get("email") except json.JSONDecodeError: return JsonResponse({"success": False, "message": "Invalid JSON format."}) try: validate_email(email) except ValidationError: return JsonResponse({"success": False, "message": "Invalid email format."}) email_otp = generate_otp() redis_key = f"otp:{email}" cache.set(redis_key, email_otp) try: message = BaseEmailMessage( template_name="emails/otp_template.html", context={"email_otp": email_otp}, ) message.send([email]) except (BadHeaderError, SMTPException) as e: return JsonResponse( {"success": False, "message": f"Failed to send OTP. Error: {str(e)}"} ) return JsonResponse( { "success": True, "message": "OTP sent successfully. Please check your email.", } ) def verify_otp(request): if request.method == "POST": try: # Parse the JSON body data = json.loads(request.body) email = data.get("email") user_otp = data.get("otp") except json.JSONDecodeError: return JsonResponse({"success": False, "message": "Invalid JSON format."}) if not email or not user_otp: return JsonResponse( {"success": False, "message": "Email and OTP are required."} ) redis_key = f"otp:{email}" stored_otp = cache.get(redis_key) if stored_otp is None: return JsonResponse( {"success": False, "message": "OTP expired or not found."} ) if validate_otp(stored_otp, user_otp): cache.delete(redis_key) return JsonResponse( {"success": True, "message": "OTP verified successfully."} ) else: return JsonResponse({"success": False, "message": "Invalid OTP."}) return JsonResponse({"success": False, "message": "Invalid request method."})
urls.py:
PYTHONfrom django.urls import path from django.views.generic import TemplateView from . import views urlpatterns = [ path("", TemplateView.as_view(template_name="core/index.html")), path("register/", views.register, name="register"), path("verify_otp/", views.verify_otp, name="verify_otp"), ]
settings.py:
PYTHONCSRF_TRUSTED_ORIGINS = [ "http://127.0.0.1:8001", "http://localhost:8001", ] CORS_ALLOW_CREDENTIALS = True CORS_ALLOWED_ORIGINS = ["http://127.0.0.1:8001", "http://localhost:8001"] MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ]
h.html (URL: 127.0.0.1:8001/h.html):
HTML<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Register and Verify OTP</title> </head> <body> <h1>Register</h1> <!-- Email Form --> <form id="registerForm"> <label for="email">Email: </label> <input type="email" id="email" name="email" required> <button type="submit">Send OTP</button> </form> <!-- OTP Verification Form (Initially hidden) --> <div id="otpForm" style="display: none;"> <h2>Enter OTP</h2> <form id="verifyOtpForm"> <label for="otp">OTP: </label> <input type="text" id="otp" name="otp" required> <button type="submit">Verify OTP</button> </form> <div id="otpMessage"></div> </div> <div id="errorMessage" style="color: red;"></div> <script> // Function to get CSRF token from cookies function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== "") { const cookies = document.cookie.split(";"); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } const csrftoken = getCookie("csrftoken"); console.log(csrftoken); // Handle email registration form submission document.getElementById("registerForm").addEventListener("submit", function(event) { event.preventDefault(); // Prevent form submission let email = document.getElementById("email").value; fetch("http://127.0.0.1:8000/register/", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRFToken": csrftoken, // Include CSRF token in the headers }, credentials: "include", body: JSON.stringify({ email: email }), }) .then(response => response.json()) .then(data => { if (data.success) { // Show OTP form on successful OTP sent document.getElementById("otpForm").style.display = "block"; document.getElementById("errorMessage").textContent = ""; // Clear any previous error messages } else { // Show error message if OTP wasn't sent successfully document.getElementById("errorMessage").textContent = data.message; } }) .catch(error => { console.log(error); document.getElementById("errorMessage").textContent = "An error occurred while sending OTP."; }); }); // Handle OTP verification form submission document.getElementById("verifyOtpForm").addEventListener("submit", function(event) { event.preventDefault(); // Prevent form submission let email = document.getElementById("email").value; let otp = document.getElementById("otp").value; fetch("http://127.0.0.1:8000/verify_otp/", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRFToken": csrftoken, // Include CSRF token in the headers }, credentials: "include", body: JSON.stringify({ email: email, otp: otp }), }) .then(response => response.json()) .then(data => { const otpMessageDiv = document.getElementById("otpMessage"); if (data.success) { otpMessageDiv.style.color = "green"; otpMessageDiv.textContent = data.message; } else { otpMessageDiv.style.color = "red"; otpMessageDiv.textContent = data.message; } }) .catch(error => { document.getElementById("otpMessage").textContent = "An error occurred during OTP verification."; }); }); </script> </body> </html>
I’m wondering:
Any insights or solutions to ensure the CSRF cookie is properly set when using a separate frontend are appreciated.
Comments 0
•Answers 1
•Views 33
Answer by TychoAI Agent • 9 days ago
Django typically sets the CSRF token cookie when:
{% csrf_token %}
tag is renderedCsrfViewMiddleware
processes a responseensure_csrf_cookie
decoratorIn your case, since you're developing the frontend separately (at 127.0.0.1:8001) from your Django backend (at 127.0.0.1:8000), Django doesn't have an opportunity to set the CSRF cookie automatically.
Since you're using a separate frontend, you need to explicitly tell Django to set the CSRF cookie. Here's how to fix your issue:
Add a new view that uses the ensure_csrf_cookie
decorator:
PYTHONfrom django.views.decorators.csrf import ensure_csrf_cookie from django.http import JsonResponse @ensure_csrf_cookie def get_csrf_token(request): return JsonResponse({"success": True})
Add this to your urls.py
:
PYTHONfrom django.urls import path from . import views urlpatterns = [ # Your existing URLs path("get-csrf-token/", views.get_csrf_token, name="get_csrf_token"), ]
Before making any POST requests, fetch the CSRF token:
JAVASCRIPT// First, fetch the CSRF token fetch("http://127.0.0.1:8000/get-csrf-token/", { method: "GET", credentials: "include", // Important for cookies }) .then(() => { // Now the CSRF cookie should be set const csrftoken = getCookie("csrftoken"); console.log("CSRF Token:", csrftoken); // Continue with your form submission logic }) .catch(error => { console.error("Error fetching CSRF token:", error); });
CORS Configuration: Your CORS settings look correct with CORS_ALLOW_CREDENTIALS = True
and the appropriate origins.
CSRF Exempt Option: If you prefer to exempt specific views from CSRF protection (not recommended for production), you can use the @csrf_exempt
decorator:
PYTHONfrom django.views.decorators.csrf import csrf_exempt @csrf_exempt def register(request): # Your view code
Session-based CSRF: If you're using CSRF_USE_SESSIONS = True
, you'll need to use a different approach to get the token as described in the Django documentation.
Always fetch the CSRF token first: Make a GET request to your Django backend before attempting any POST requests.
Include credentials: Always use credentials: "include"
in your fetch requests to ensure cookies are sent and received.
Check browser console: Monitor the browser's network tab and console to verify the CSRF cookie is being set correctly.
Security considerations: Remember that CSRF protection is an important security feature. While it might be tempting to disable it during development, it's better to implement it correctly from the start.
By implementing these changes, your separate frontend should be able to properly obtain and use the CSRF token for secure form submissions to your Django backend.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
No comments yet.