user590028
user590028

Reputation: 11730

Django authenticate with python-requests

Having trouble sorting out how to correctly authenticate a python-request client against a django app to permit POST. Our code tries to login first, and then use our established python-request session to POST data, but our POST request is always unauthenticated.

Here's what we are trying

# client.py
LOGIN_URL = 'http://localhost/accounts/login'
ADD_URL = 'http://localhost/add'

import requests
rqst = requests.session()
rsp = rqst.get(LOGIN_URL)

token = rsp.cookies['csrftoken']
rsp = rqst.post(LOGIN_URL, auth=(uname, pwd),
        data={'csrfmiddlewaretoken':token, 'next':'/'})
# at this point, rsp.status_code == 200 and we are logged in

payload = {'foo':'bar', 'csrfmiddlewaretoken':token}
rsp = rqst.post(ADD_URL, json=payload)
# Error -- this post always returns 403 -> HttpResponseForbidden

# view.py
def view_add(request):
    if request.user.is_authenticated:
        return HttpResponseForbidden('not authenticated')
    ...

We have enabled django sessions in settings.py. We have no problem accessing GET views using this same pattern. Its only POST where we are having trouble.

Any ideas, pointers?

Upvotes: 1

Views: 3914

Answers (4)

ESR
ESR

Reputation: 46

I wanted to add that user590028's example helped me, but it's important for some reason to add the / after your LOGIN_URL, if that's how your server is set up.

In my project's urls.py:

    urlpatterns = [
    path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
    ]

Thus, this didn't work: http://localhost:8000/login <-- no slash!

This worked: http://localhost:8000/login/

... it took me a long time to figure this out, so I hope this helps someone else.

Also - there's a very good Python scraping article that also helped me find the csrf token: https://kazuar.github.io/scraping-tutorial/

Upvotes: 1

davenenglish
davenenglish

Reputation: 108

I have had issues with requests.sessions before with my django projects. I am sure it is doable, but I found it easier to use Selenium as a headless browser. There is very good python support for selenium and it is pretty straight-forward to get started with it.

https://selenium-python.readthedocs.io/

Upvotes: -1

user590028
user590028

Reputation: 11730

The issue was caused by my confusion about django authentication. Django authentication is forms based, whereas the rqst.post(..., auth=(uname, pwd)) is basic authentication by default.

The error was therefore in the first invocation of rqst.post(). The corrected code client.py is:

# client.py
LOGIN_URL = 'http://localhost/accounts/login'
ADD_URL = 'http://localhost/add'

import requests
rqst = requests.session()
rsp = rqst.get(LOGIN_URL)

token = rsp.cookies['csrftoken']
rsp = rqst.post(LOGIN_URL, 
        data={'username':uname, 'password': pwd,  # <---- HERE IS FIX
        'csrfmiddlewaretoken':token, 'next':'/'})

Upvotes: 1

AKX
AKX

Reputation: 168996

Your second POST request doesn't have a CSRF token (even if the login request does).

Unless your view is marked @csrf_exempt, all non-GET requests require the CSRF token.

Upvotes: 1

Related Questions