Reputation: 11730
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
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
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
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
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