I have set up a JWT authentication system for my web application, however I have run into issues whilst trying to implement CSRF protection.
I am POST(ing) the data with the X-CSRF-TOKEN.
let request = $.ajax({
type: "POST",
data: $form.serialize(),
headers: {
"X-CSRF-TOKEN": json_form["csrf_token"],
"Authorization": authHeader
}
})
(json_form: form data converted to json obj)
However I dont know how to access the access_token_cookie.
The HTTP request contains this cookie, however document.cookie does not, I assume this is because the JWT is not sent as part of the response?
My question then is this: how does one access the JWT?
I assumed it would be a matter of accessing this cookie straight from the browser with JS.
Otherwise maybe I could send the JWT as a header with each response, but I have a sneaking suspicion that is superfluous().
My gratitude in advance.
Related
How to form a response from Flask python server which would contain the bearer token in the response. More precisely, I am looking to somehow securely propagate the JWT token from Flask python server back to the client (angular page). I can just return it in form of the querystring in GET redirect. What are other possibilities in terms of returning the JWT access token back to the client? I tried setting the response form python, and to set the jwt token in Authorization field, but nothing worked. This is what I tried:
1.
r = Response(headers={
"Authorization": "bearer jwtcntent",
"Access-Control-Allow-Origin": "*",
},
is_redirect=True,
url="https://localhost:5000/callback",
)
return r
r = redirect("http://localhost:5000/callback")
r.headers = {"authorization": "bearer jwtcntent"}
return r
r = Response(headers={
"Authorization": "Bearer jwtcntent",
"Access-Control-Allow-Origin": "*",
},
allow_redirects=True,
url="https://localhost:5000/callback",
)
return r
Any recommendations?
You can store it in an httponly cookie, but you need to make sure to handle CSRF attacks if you do so. Flask-JWT-Extended has built in support for this which you might find useful, either as a solution or as a reference for whatever you end up doing:
https://flask-jwt-extended.readthedocs.io/en/stable/token_locations/#cookies
You can also just send the token back as part of the JSON body and then storing it in local/session storage, which is probably the most common pattern.
Are you able to implement a regular OAuth flow in your Authorization Server? OAuth flows are standardized and use secure ways of returning an access token to the client.
I don't recommend using the Authorization header for returning responses. This header is a request header, it has no meaning in a response. If you really need to do it through the header you can add Access-Control-Expose-Headers header to let your client read the Authorization header from a response.
I've been looking on how to get the Authorization Header from a Tornado GET/POST Request, but there's none. Anyone can help on this?
The reason is that I want to implement JWT in my Python application.
For example, this tornado API just creates a GET request:
class HeaderHandler(tornado.web.RequestHandler):
def get(self):
headers = tornado.httputil.HTTPHeaders()
response = {
'message': str(headers)
}
self.write(response)
I want to access this API via http://localhost:{port} and I will add a header which is
Authentication: Bearer {token}
But this code doesn't work. It cannot fetch the authorization header passed to the request. Any help is appreciated. Thanks!
In Tornado, accessing the bearer token from JWT can be achieved by:
token = self.request.headers.get('Authorization')
According to the docs, a request object containing headers is available as RequestHandler.request, so try e.g.
self.write(repr(self.request.headers))
While requesting http://www.sonyliv.com/api/v2/vod/search API, I am getting "Invalid csrf token" message in chrome postman.
{
"code": "403",
"name": "Bad Request",
"message": "Invalid csrf token"
}
When I look to Chrome Inspect Element > Network tab in Headers section, I found
"X-XSRF-TOKEN:tGXcHOmy-ro-GQfTestDSAp8EINq85dwHpdU"
as a token but this token is changed in every session, how can i pass X-XSRF-TOKEN value in my request to get the required result.
Please Help.
The idea of the CSRF tokens is that you can't call an API service if you're not doing it from the expected form, that's why it always changes its value.
I'm guessing you're trying to use that API not officially... so what you could try is to GET the base website and store in a cookie jar all the cookies it sends you and then try to query the search endpoint.
That way your request will include the XSRF token and the rest of the cookies and hopefully the server will think you request is legit.
Hope it helps
I have created RESTFul APIs using django-rest-framework. The user endpoint is: /api/v1/users
I want to create a new user, so I send the user data in JSON format:
{
"username": "Test1",
"email": "test1#gmail.com",
"first_name": "Test1",
"last_name": "Test2",
"password":"12121212"
}
I am using Google Chrome extension Postman to test the API. But, after sending the request, the user data is not saving. The response contains this error:
{
"detail": "Unsupported media type \"text/plain;charset=UTF-8\" in request."
}
This is what the request details look like in Postman:
You have missed adding the Content-Type header in the headers section. Just set the Content-Type header to application/json and it should work.
See the below image:
Also, you might also need to include a CSRF token in the header in case you get an error {"detail": "CSRF Failed: CSRF token missing or incorrect."} while making a POST request using Postman. In that case, add an X-CSRFToken header also with value as the CSRF token value.
I'm posting this answer in case someone is facing a problem like mine.
I'm working on a Front-End app using Angular 2 with an API made with Django Rest Framework and I used to send requests with the following headers:
'Content-Type': 'application/json'
And it was working fine until I tried it on Fire Fox and I couldn't load the needed data and I solved it with adding the following headers
'Content-Type': 'application/json',
'Accept': 'application/json'
Here's an explanation, Content-Type tells the server what is the content type of data is while Accept tells it what content type the client side will accpet.
Here's a nice clear answer about this issue:
https://webmasters.stackexchange.com/questions/31212/difference-between-the-accept-and-content-type-http-headers
You need to do two step to done this issue:
Add Content-Type header with application/json value
Add Authorization header with Token {YOUR_CUSTOM_TOKEN} value to pass CSRFToken
Note: if you want to authenticate with session, you don't need to do second step, but if you want use this API for mobile, you have to pass Authorization header to server
I hope it helps
You need to define content type by setting the appropriate headers. In case of Postman you need to set the following values under url field:
Header: "Content-Type"
Value: application/json
I had to add the following to get this to work (I'm using node-fetch btw from the client side to do a POST):
supportHeaderParams: true,
headers: { "Content-Type": "application/json; charset=UTF-8" },
Couple of things to do if you want to accept JSON Data using Django Rest Framework.
Make sure application/json headers are sent:
'Content-Type: application/json'
JSON Parser is selected in settings.py
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
],
}
I'm using Flask with Flask-Security (specifically Flask-WTF regarding my csrf issue) to "ease" the process of register/loggin users (not easy so far). I'm using BackboneJS on the front-end, therefore I kind of hacked the original way to use Flask-WTF. Indeed, I make an AJAX GET request on /register to get the register page (generated by Flask-Security) and I put the resulting HTML in a modal.
render: function () {
var self = this;
$.ajax({
type: 'GET',
url: Config.constants.serverGateway + "/register"
}).done(function(result){
console.log("get register done", result);
var html = self.template({ config: Config, form: result });
self.$el.html(html);
}).fail(function(error){
console.log("Could not get register token", error);
var html = this.errorTemplate({ config: Config });
self.$el.html(html);
});
return this;
}
This way I have the generated csrf, and when I POST the registration data, I send the right csrf along the user data (email and password).
submit: function () {
console.log("submit");
var self = this;
var formData = this.$el.find('form').serialize();
$.ajax({
type: 'POST',
url: Config.constants.serverGateway + "/register",
data: formData,
dataType: 'json'
}).done(function(result){
self.trigger('close');
}).fail(function(error){
console.log("Could not submit register data", error);
});
}
On the server-side, I can debug my python code to see that the csrf_token which has been generated when I requested the register page has disappeared from the session object, therefore leading to the generation of a new one, which of course didn't match the one I send with my form. The session is still the same though, as the _id is the same during the GET and the POST.
You can see the code in flask_wtf/csrf.py::generate_csrf(), which is called when creating the form object in the ::register function from flask_security/views.py
if 'csrf_token' not in session:
session['csrf_token'] = hashlib.sha1(os.urandom(64)).hexdigest()
It results in a CSRF TOKEN MISSING error.
An additionnal information, is that my front-end and back-end are delivered by the same server, as they have a different port number.
Last, when I use an href on front-end and display the page returned by the server on the 'GET' request, submitting the form works well. I just liked to display this registration form in a modal.
Thanks for your help
Okay, I finally figured out the solution to my problem. I feel like a noob (which I am).
The problem lied in the session credentials which were not sent to the server with the requests, so that the server coudldn't access the session cookie.
I found the solution in the following tutorial: http://backbonetutorials.com/cross-domain-sessions/
To send it, i added the following lines in my Backbone router initialize function:
// Use withCredentials to send the server cookies
// The server must allow this through response headers
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.xhrFields = {
withCredentials: true
};
});
This makes all AJAX requests include the withCredentials = true. On the server-side, I had to set Access-Control-Allow-Credentials:true. Since I'm using flask-cors, it is done with [supports_credentials=True][2] when creating the CORS object.
(I'm answering here since I can't comment)
#junnytony Yes I have the token in my modal and I send it in my POSt request. When I debug the Flask application, I can see the toekn I sent with my POST request, the problem is that it should be compared to the one in the session to be validated, but the one in the session has disappearred, so the flask-wtf lib generates a new one, which results in a failure when comparing with the one I sent.