I have an app which needs to redirect to another url from outside with some POST data. I have the CSRF token value to the other app. How do I construct a simple POST request with requests library in Python??
csrf_token = "kjsbfckjsdnfcksdnkl"
post_data = {'email': email, 'answer': answer}
response = request.post(URL, data=post_data)
Where do I add the CSRF token?
You can either send the CSRF token as a POST parameter or a HTTP header.
Edit: a Referer HTTP header is also required by Django's CSRF protection. It needs to have the same origin as the request.
Using POST parameters:
post_data = {'email': email, 'answer': answer, 'csrftoken': csrf_token_value}
headers = {'Referer': URL}
response = request.post(URL, data=post_data, headers=headers)
Using HTTP headers:
post_data = {'email': email, 'answer': answer}
headers = {'X-CSRFToken': csrf_token_value, 'Referer': URL}
response = request.post(URL, data=post_data, headers=headers)
Another workaround is to use csrf_exempt decorator.
https://docs.djangoproject.com/en/3.2/ref/csrf/#django.views.decorators.csrf.csrf_exempt .
In case you are using a recent Django with CsrfMiddleware add it to the post_data dict:
post_data = {'email': email, 'answer': answer, 'csrfmiddlewaretoken': 'yourtoken'}
Check a form if the variable name is correct.
If you want to redirect on the same server, just call die other view function.
Take a look at the official documentation, which covers sending a POST request with a CSRF token.
CSRF tokens are stored in cookies (as far as I know). Since that's the case, you can store the cookie value as some variable, then use that value in your request.
I suggest you to use session objects of request library.
Moreover, if you’re making several requests to the same host, the underlying TCP connection will be reused, which can result in a significant performance increase and A Session object has all the methods of the main Requests API.
Related
I've got a tiny function that just looks to get a response from my DRF API Endpoint.
My DRF settings look like this:
"DEFAULT_AUTHENTICATION_CLASSES": [
# Enabling this it will require Django Session (Including CSRF)
"rest_framework.authentication.SessionAuthentication"
],
"DEFAULT_PERMISSION_CLASSES": [
# Globally only allow IsAuthenticated users access to API Endpoints
"rest_framework.permissions.IsAuthenticated"
],
I'm using this to try and hit the endpoint:
def get_car_details(car_id):
headers = {"X-Requested-With": "XMLHttpRequest"}
api_app = "http://localhost:8000/"
api_model = "cars/"
response = requests.get(api_app + api_model + str(car_id), headers=headers)
json_response = response.json()
return json_response
I keep getting 'detail': 'Authentication credentials were not provided'
Do I need to generate a CSRF token and include it in a GET request? The only time this gets hit is when a user goes to a view that requires they are logged in. Is there a way to pass that logged-in user to the endpoint??
When you make your request from the get_car_details function you need to be sure that the request is authenticated. This does not look like a CSRF issue.
When using session based authentication a session cookie is passed back after logging in. So before you call get_car_details you would need to first make a request to login, keep that session, and use that session when calling the API.
Requests has a session class requests.Session() that you can use for this purpose. Details here: https://docs.python-requests.org/en/latest/user/advanced/
Many people find token based authentication easier partly a session cookie does not need to be maintained.
Background:
I'm trying to prototype a quick token-based authentication using Flask-Restful and PyJWT. The idea is that I will have a form with email and password and when user clicks submit, it will generate a token and save it in client side browser and use it in any subsequent requests until the token expires.
Trouble
In my prototype, I was able to create a token using JWT but I don't have an idea of how to pass the JWT into a subsequent request. When I do it in the Postman, it works because I can specify the Authorization header with token in there. But when I login in through UI and token is generated, I do not know how to pass the token generated into a subsequent request (/protected) by making the token persists in the header until it expires. Currently when I login from UI and go to /protected, the Authorization header is missing in /protected header.
Code
class LoginAPI(Resource):
# added as /login
def get(self):
"""
renders a simple HTML with email and password in a form.
"""
headers = {'Content-Type': 'text/html'}
return make_response(render_template('login.html'), 200, headers)
def post(self):
email = request.form.get('email')
password = request.form.get('password')
# assuming the validation has passed.
payload = {
'user_id': query_user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=10)
}
token = jwt\
.encode(payload, current_app.config['JWT_SECRET'], current_app.config['JWT_ALGORITHM'])\
.decode('utf-8')
# Is below the right way to set the token into header to be used in subsequent request?
# request.headers.authorization = token
# when {'authorization': token} below as a header, the header only shows up for /login not for any subsequent request.
return make_response({'result': 'success', 'token': token}, 200, {'authorization': token} )
class ProtectedAPI(Resource):
#check_auth
def get(self):
return jsonify({'result': 'success', 'message': 'this is a protected view.'})
# decorator to check auth and give access to /protected
def check_auth(f):
#wraps(f)
def authentication(*args, **kws):
# always get a None here.
jwt_token = request.headers.get('authorization', None)
payload = jwt.decode(jwt_token, 'secret_key', algorithms='HS512'])
# other validation below skipped.
return f(*args, **kws)
return authentication
To persist the access_token you have to store on the client and pass it into your header every time you call your backend and check the authenticity of the token every time.
My website uses Django's default auth module to do user authentications. Usually, user just needs to fill out a html form in the login page with his/her username and password and click submit, then the CSRF-protected data will be posted to /auth/login/, the Django auth endpoint.
But now for some reason I also need to do this on my server. This is the same server as the backend authentication server. After researching and trials and errors, I finally got:
from django.views.generic import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect
import requests
class fb_login(View):
"login view for Facebook OAuth"
#method_decorator(csrf_protect)
def get(self, request):
''' code that gets username and password info. from user's FB profile
is not shown here '''
# login code
url = 'http://localhost:8000/auth/login/'
client = requests.session()
# Retrieve the CSRF token first
client.get(url) # sets cookie
csrftoken = client.cookies['csrftoken']
form = {'username': uname, 'password': pw}
header = {'X-CSRFToken': csrftoken}
resp = requests.post(url, data=form, headers=header)
I also tried to add csrf token as part of the form field:
form = {'username': uname, 'password': pw , 'csrfmiddlewaretoken': csrftoken}
resp = requests.post(url, data=form)
I pretty much just followed the Django doc. But I still get the 403 error saying CSRF verification failed, CSRF cookie not set. I wonder if I missed something here? I've double-checked the process against the Django doc but I cannot find anything that might be wrong or missing.
As suggested by #rodrigo, cookies are also needed to pass the CSRF security check. Here's a simple code snippet:
csrftoken = requests.get(url).cookies['csrftoken']
form = {'username': uname, 'password': pw}
header = {'X-CSRFToken': csrftoken}
cookies = {'csrftoken': csrftoken}
resp = requests.post(url, data=form, headers=header, cookies=cookies)
I have a question about the HttpRequest and HttpResponse
consider the following scenerio:
def articles_cookies(request):
language = 'en-us'
session_language = 'en-us'
if 'lang' in request.COOKIES:
language = request.COOKIES['lang']
return render_to_response('articles.html',
{'articles':Article.objects.all(),
'language':language})
def language(request,language='en-us'):
response = HttpResponse('setting language to %s' % language)
response.set_cookie('lang',language)
return response
which is in the views.py in Django.
Is the response returned by the language same as the request passed into the articles_cookies function? because i can access the cookies in the request.
Thanks in advance.
No they aren't the same object.
The response is returned to the browser after being serialized as HTTP response. This HTTP response can contain a Set-Cookie header which instructs the browser to send back the given Cookie when making subsequent requests.
At the next requests, the browser sends the new request with a Cookie header and Django then extracts that header into a new request object.
I have a django project, using django-rest-framework to create api.
Want to use token base authentication system so api call for (put, post, delete) will only execute for authorized user.
I installed 'rest_framework.authtoken' and created token for each users.
So, now from django.contrib.auth.backends authenticate, it returns user, with auth_token as attribute. (when loged in successfully).
Now my question is how can I send the token with post request to my api and
at api side how can I verify if token is valid and belongs to the correct user?
Are there any methods in app rest_framework.authtoken to validate given user and its token?
not found this very useful!
Update (changes I made):
Added this in my settings.py:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
Also sending Token in my header but its still not working:
if new_form.is_valid:
payload= {"createNewUser":
{ "users": request.POST["newusers"],
"email": request.POST["newemail"]
}
}
headers = {'content-type' : 'application/json',
'Authorization': 'Token 6b929e47f278068fe6ac8235cda09707a3aa7ba1'}
r = requests.post('http://localhost:8000/api/v1.0/user_list',
data=json.dumps(payload),
headers=headers, verify=False)
"how can I send the token with post request to my api"
From the docs...
For clients to authenticate, the token key should be included in the Authorization HTTP header. The key should be prefixed by the string literal "Token", with whitespace separating the two strings. For example:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
"at api side how can I verify if token is valid and belongs to the correct user?"
You don't need to do anything, just access request.user to return the authenticated user - REST framework will deal with returning a '401 Unauthorized' response to any incorrect authentication.
To answer the first half of your question:
how can I send the token with post request to my api
You can use the Python requests library. For the django-rest-framework TokenAuthentication, the token needs to be passed in the header and prefixed by the string Token (see here):
import requests
mytoken = "4652400bd6c3df8eaa360d26560ab59c81e0a164"
myurl = "http://localhost:8000/api/user_list"
# A get request (json example):
response = requests.get(myurl, headers={'Authorization': 'Token {}'.format(mytoken)})
data = response.json()
# A post request:
data = { < your post data >}
requests.post(myurl, data=data, headers={'Authorization': 'Token {}'.format(mytoken)})
I finally have the django "rest-auth" package working for token authentication.
If this helps, here is the client-side jQuery code that worked for me, after you successfully log in and receive the "auth_token":
var user_url = {API URL}/rest-auth/login
var auth_headers = {
Authorization: 'Token ' + auth_token
}
var user_ajax_obj = {
url : user_url,
dataType : 'json',
headers: auth_headers,
success : function(data) {
console.log('authorized user returned');
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log('error returned from ' + user_url);
}
};
$.ajax(
user_ajax_obj
);
If you are using coreapi. To add the Authorisation you do
import coreapi
auth = coreapi.auth.TokenAuthentication(scheme='Token', token=token_key)
Then you can do
client = coreapi.Client(auth=auth)
response = client.get(my_url)