Why am I getting a CSRF Token missing when in fact it's there?

I have a Vue app and I am making requests to a Django Rest Framework endpoint, well a few. It was working fine yesterday and I was able to submit and save content. Today I keep getting the same error
CSRF Failed: CSRF token missing.

But if I look in the dev tools I see this:

|csrftoken|“wzWBo6N66F1HTp2To67We5GppNWfbXdm”|
|sessionid|“1vq4vj73b1b6l80gt2ckba6zwxqj8wwu”|

So how is it missing if it is right there?

Second question:

What is the use of this setting if no matter what it still gives you a CSRF error?

CORS_ORIGIN_WHITELIST = (“localhost:8080”,“127.0.0.1:8080”)

CSRF_TRUSTED_ORIGINS = [“http://localhost:8080”, “http://*.127.0.0.1”, “http://127.0.0.1:8080”,“http://192.168.1.44:8080”,]

I’m assuming that these:

are the cookies being submitted with the request.

What about in the request itself, does your POST data contain the csrfmiddlewaretoken field?

I have several websites that have a Vue app that works with a Django and the Vue app makes requests to a Rest Framework API while the pages on the Django app accesses the data from Django directly. None of my Vue apps ever use the csrfmiddlewaretoken field and two of them work fine and I am able to make requests to the API without any issue, so really I have no idea why or even how to include that field in my POST data.

I would really like to understand the process of how Django validates the csrf token. How can I check if the front end is actually sending the cookie or header? How can I check what the value of the cookie or header is Django validates a request against?

This has been my single biggest headache of all the things I battled with using Django

and again what’s the use of the setting:

CSRF_TRUSTED_ORIGINS = [
http://localhost:8080”,
“http://*.127.0.0.1”,
http://127.0.0.1:8080”,
http://192.168.1.40:8080”,
]

Because clearly it’s not working?

I can’t even try to explain anything you’re seeing from other projects without seeing the code.

That’s all handled in django.middleware.csrf.CsrfViewMiddleware

At either end. Either in the network tab of the browser’s developer tools, or on the server by logging (or printing) the data from the raw request coming in. Or, if you’re working in a non-https environment, using something like wireshark or tcpdump.

There’s nothing on the server being checked here. The token is derived from the cookie. (See the get_token method in django.middleware.csrf)

It’s documented here: Settings | Django documentation | Django

It has to do with where the page that is submitting the request was loaded from. It has nothing to do with the data in the request itself.

Ok I understand and it’s really too much code to post here. All I know it works and there isn’t any field called csrfmiddlewaretoken so don’t know why it works then because I read the field must be there but it’s not and I make post, put and delete requests without issues.

As far as this goes if the request comes from a domain (localhost) and localhost is in that list of CSRF_TRUSTED_ORIGINS = [
then why is the access still be denied because of CSRF?

I log in on the Django side of things using a form and a normal view:

def loginpage(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')

        if '@' in username:
            user = authenticate(request, email=username, password=password)
        else:
            user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            messages.success(request, 'Login successful')
            return redirect('homepage')
        else:
            messages.error(request, 'Invalid credentials. Please try again.')

    logged_in = request.user.is_authenticated
    if logged_in:
        return redirect('homepage')
    else:
        return render(request, 'pages/login.html', {'form': LoginForm, 'resetform': CustomPasswordResetForm})

it works perfectly and it then creates two cookie and they are:

csrftoken:"k3rDF6l6jSIA9DoTwoZDyDNNs2uvBax2"

sessionid:"gnb8osj0ww615coqvh3b9ppllw218urq"

When I make a POST, PUT, DELETE request to the back end then I get a 403 error and here are the request headers:

POST
	http://localhost:8000/api/v1/users/logout/
Status
403
Forbidden
VersionHTTP/1.1
Transferred502 B (45 B size)
Referrer Policystrict-origin-when-cross-origin
Request PriorityHighest
DNS ResolutionSystem

	
access-control-allow-credentials
	true
access-control-allow-origin
	http://localhost:8080
Allow
	POST, OPTIONS
Content-Language
	en
Content-Length
	45
Content-Type
	application/json
Cross-Origin-Opener-Policy
	same-origin
Date
	Tue, 09 Jan 2024 15:55:57 GMT
Referrer-Policy
	same-origin
Server
	WSGIServer/0.2 CPython/3.10.12
Vary
	origin, Accept-Language, Cookie
X-Content-Type-Options
	nosniff
X-Frame-Options
	DENY
	
Accept
	application/json, text/plain, */*
Accept-Encoding
	gzip, deflate, br
Accept-Language
	en-US,en;q=0.5
Cache-Control
	no-cache
Connection
	keep-alive
Content-Length
	0
Cookie
	csrftoken=k3rDF6l6jSIA9DoTwoZDyDNNs2uvBax2; sessionid=gnb8osj0ww615coqvh3b9ppllw218urq
Host
	localhost:8000
Origin
	http://localhost:8080
Pragma
	no-cache
Referer
	http://localhost:8080/
Sec-Fetch-Dest
	empty
Sec-Fetch-Mode
	cors
Sec-Fetch-Site
	same-site
User-Agent
	Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0

As you can see the token and session id is in the headers and I can make get requests fine but any request where the cookie must be sent (which I presume is being sent because it’s in the header) gives me 403 error.

Why is the session id cookie being sent and accepted but the csrf cookie is not?

How do I possibly fix this?

When you create a standard Django form, you should be rendering the {% csrf_token %} tag. This creates a hidden input field in the form that looks like this:

<input type="hidden" name="csrfmiddlewaretoken" value="rPPcth2yDvyGLgCdagVFkemXdtBnaXNVuQQK6OM73Khr0Nyg5RBA0v9Lq6r07E8l">

This is submitted in the POST data and not as a cookie or header.

If you don’t supply this value, CSRF is going to reject the request, when CSRF protection is active.

(Side note: I believe this only applies to POST. Since a browser will not issue a PUT or DELETE, I think this protection is not necessary in those cases.)

This has nothing to do with what we’re talking about here, unless you’re loading your front end from a different server than this one. This is a separate, and to some degree, unrelated topic.

Follow the instructions as documented at Using CSRF protection with ajax

I am not submitting a standard Django form. In fact the only form I submit is on the Django side when I log in. I am using Vue to send requests to the API so no Django form is ever used on the Vue side, which is where the problem lies.

Any case I see the cookie in the request and I presume it is being sent but Django obviously doesn’t get it or somewhere some setting is wrong and nobody can tell me what and I have followed the instructions in the Django docs and I am still here asking questions. But thanks for the help I will find a framework that isn’t so frustrating to work with or hire someone to fix this as I am not getting this right and just makes me dislike Django

I didn’t say you were in this situation. I said that when you do, you render the form with the csrf_token, which generates an additional (hidden) field in the form.

When you don’t submit a standard Django form, it’s up to you to supply the csrfmiddlewaretoken field in the POST data.

And that is what I think is missing here.

The issue is the front end is not sending this header:
X-CSRFToken (with csrftoken cookie as its value)

Which Axios is supposed to send because of this setting in the Vue main.js file:

axios.defaults.baseURL = "http://localhost:8000";

axios.defaults.xsrfHeaderName = "X-CSRFToken";

axios.defaults.xsrfCookieName = "csrftoken";

axios.defaults.withCredentials = true;

So I have to find out why Axios is not sending the header. Pure frustration because it worked fine a few days ago and I did not change any of those settings.