How do I get a CSRF token for Django's auth contrib views if my frontend app is not served by Django?

2.3k views Asked by At

I'm using Django 3.1.1 with Django's auth contrib module for managing users. I'm not using Django templates, but rather creating a Django API application to respond to requests from my React frontend. I have this in my settings.py file

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'directory.middleware.extend_token_response.ExtendTokenResponse'
]

#CORS_ORIGIN_ALLOW_ALL = True

ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'dev.mywebsite.com', 'prod.mywebsite.com', 'map.mywebsite.com']

CORS_ORIGIN_WHITELIST = [
    'http://localhost:3000', 'http://localhost:3001', 'http://127.0.0.1:3000', 'http://127.0.0.1:3001'
]

CORS_EXPOSE_HEADERS = [
    'Refresh-Token', 'Content-Type', 'Authorization', 'X-CSRFToken'
]
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = ['localhost', '127.0.0.1']

I have this set up in my views.py file

class ResetPasswordView(SuccessMessageMixin, PasswordResetView):
    email_template_name = 'users/password_reset_email.html'
    subject_template_name = 'users/password_reset_subject'
    success_message = "We've emailed you instructions for setting your password, " \
                      "if an account exists with the email you entered. You should receive them shortly." \
                      " If you don't receive an email, " \
                      "please make sure you've entered the address you registered with, and check your spam folder."
    success_url = reverse_lazy('users-home')

    def post(self, request, *args, **kwargs):
        print("request data %s" % request.data)
        email = request.data.get('email')
        try:
            if User.objects.get(email=email).active:
                print("email: %s " % email)
                return super(ResetPasswordView, self).post(request, *args, **kwargs)
        except:
            # this for if the email is not in the db of the system
            return super(ResetPasswordView, self).post(request, *args, **kwargs)

In my frontend, I submit password reset requests to my backend using this fetch code

  const handleFormSubmit = (e) => {
    e.preventDefault()
    const csrftoken = getCsrfToken();
    fetch(REACT_APP_PROXY + '/reset_password', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-CSRFToken': csrftoken
      },
      body: JSON.stringify({username})
    }).then(resp => resp.json())
    .then(jsob => {
      if(!jsob.ok){
        console.log(jsob)
        setErrors(jsob)
      }
      if(jsob.token){
        sessionStorage.setItem('token', jsob.token)
        setRedirect(true)
      }
    })
  }

The problem is, I don't have a CSRF token the first time I submit the form because Django is not serving my React application (it is served by Apache). How do I get a CSRF token from Django so that I can submit it back during the submission of my password reset form?

2

There are 2 answers

3
JPG On

Disclaimer

This answer doesn't have an implementation in JS/ReactJS, but it has general instructions on how to have them.


Django will return the CSRF token with the cookies if you access valid views rendered with TemplateResponse. To get the CSRF token in your case, you need to send the HTTP GET request to the same URL (response may look like this). After completing the request, you will have the CSRF token in your cookies' section.

Here, I demonstrate a simple example with Python's requests library.

In [23]: import requests

In [24]: url = "http://127.0.0.1:1234/pwd-reset/"

In [25]: response = requests.get(url)

In [26]: response.status_code
Out[26]: 200

In [27]: response.cookies.get_dict()
Out[27]: {'csrftoken': 'PABeYmF2k7Pvzhzu2UgqB0P63Ann5WeKqsODLmpx8VUtQhdn1DbhFT1Gs52pOEO5'}
1
Mahammadhusain kadiwala On

You can get csrf token value by targeting csrf hidden input like this...

views.py

def CreditSaveView(request):
    if request.method == 'POST':
        creditform = CreditForm(request.POST)
        if creditform.is_valid():
            amt = request.POST['amt']
            tag = request.POST['tag']
            crs = request.POST['csrfmiddlewaretoken']
            CreditModel(amt=amt,tag=tag).save()
            return JsonResponse({'status':'done'})

In tag

<script>
    document.getElementById("creditbtn").addEventListener("click", function () {
        let id_amt = document.getElementById('id_amt').value;
        let id_tag = document.getElementById('id_tag').value;
        let crs = document.getElementsByName('csrfmiddlewaretoken')[0].value
        my_data = { csrfmiddlewaretoken: crs, amt: id_amt, tag: id_tag };

        console.log(my_data)

        $.ajax({
            url: "/creditsave/",
            method: "POST",
            data: my_data,
            // dataType: "json",
            success: function (data) {
                if (data.status == 'done') {
                    // document.getElementById('creditform').reset()

                    document.getElementsByTagName('form')[1].reset()
                }
            }
        });
    });
</script>