Send post request with flask_jwt_extended - javascript

I am trying to send a post request to a Flask server that uses flask_jwt_extended tokens. I don’t want the page to refresh after each submit and I managed to do this using jquery. But I am unable to send CSRF token in a way that flask_jwt_extended can authenticate user.
function myfunction(action, id, csrf_token){
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:5000/accept", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
action: action,
id: id,
csrf_token: csrf_token
}));
}
$(document).ready(function() {
$(document).on('click', '#my-form', function(event) {
var action = $(event.target).attr('value');
var f = $(this);
csrf_token = f.find('input[id=csrf_token]');
myfunction(action, f.find('[name=song_id]').val(), csrf_token)
return false;
});
});
#app.route('/accept', methods=["POST"])
#jwt_required
def accept():
user = get_jwt_identity()
...
When I try the following I get a 401 error, which is not surprising as I am not passing the csrf token as in a form. If I simply submit a post request on the form, it works fine.
EDIT:
On my backend I am using the following setting for flask_jwt_extended:
app.secret_key = 'loginner'
app.config['JWT_SECRET_KEY'] = 'super-secret'
app.config['JWT_TOKEN_LOCATION'] = ['cookies', 'headers']
app.config['JWT_BLACKLIST_ENABLED'] = True
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']
app.config['JWT_COOKIE_CSRF_PROTECT'] = False # the bearer request works when this is False, but ideally it would be True
app.config['JWT_CSRF_CHECK_FORM'] = True
And I am getting the identify in the following:
#app.route('/accept', methods=["POST"])
#jwt_required
def accept():
user = get_jwt_identity()
...
It works as expected when app.config['JWT_COOKIE_CSRF_PROTECT'] = False

Your question does not specify how you are handling the JWT on the client side, but the basic answer is that you need to pass an Authorization header with your request (see more near the bottom of the page in your flask_jwt_extended docs). There may be some confusion in that CSRF and identity are not the same thing. CSRF just helps your frontend and API understand that they can trust each other. A package like flask_jwt_extended helps your backend identify the user and authorize requests.
In your client example above, you would add another header line as such:
// the 'jwt' variable refers to however you have stored the token. Change as needed
xhr.setRequestHeader('Authorization', 'Bearer ' + jwt);

Related

How to initiate OAuth2.0 flow?

How do I POST to Google's OAuth 2.0 endpoints for authorization?
I'm building a chrome extension in React and have been following Google's documentation. It seems pretty straightforward but I'm not fully grasping the mechanics of the implementation.
For example, in my popup.js file I call my background.js file which performs and axios POST request to the created redirect url. Step 3 in the guide says that Google will prompt the user for consent, however, that never happens. I get a 200 response but not sure where to go after that.
What am I doing wrong? Thank you!
axios
.post(
`https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
include_granted_scopes=true&
response_type=token&
state=state_parameter_passthrough_value&
redirect_uri=https%3A//oauth2.example.com/code&
client_id=client_id` //actual values added
)
.then(function (response) {
console.log('RESPONSE', response);
});
Step 2 in that document is titled
Step 2: Redirect to Google's OAuth 2.0 server
You are trying to do an XHR request with POST.
The document provides sample code both with and without their client library. Without the client library, you can see that it is a GET request using a form (which changes the URL in the browser, effectively redirecting):
/*
* Create form to request access token from Google's OAuth 2.0 server.
*/
function oauthSignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create <form> element to submit parameters to OAuth 2.0 endpoint.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
// Parameters to pass to OAuth 2.0 endpoint.
var params = {'client_id': 'YOUR_CLIENT_ID',
'redirect_uri': 'YOUR_REDIRECT_URI',
'response_type': 'token',
'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
'include_granted_scopes': 'true',
'state': 'pass-through value'};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}

How to access a Django rest Framework API from React

I started to use django recently and I also tried to use React js and I wanted to do something simple like a nav var in React. I have accounts and log in implemented using django forms which work well. For the nav I would need to fetch the user information which I have on a DRF API as follows in the views file:
#api_view(['GET'])
#authentication_classes((SessionAuthentication, TokenAuthentication))
#ensure_csrf_cookie
#permission_classes((IsAuthenticated,))
def user_details_view(request, *args, **kwargs): #REST API for detailing some basic info about the user that is using the system at the moment
current_user = request.user
id = current_user.id
status = 200
try:
obj = CustomUser.objects.get(id=id)
data = UserSerializer(obj)
return Response(data.data, status=status)
except:
status = 404
return Response(status=404)
return Response(data.data, status=status)
The urls are set up and if I access it in the django server it works fine but when I try on react by:
function loadUserInfo(callback){
const xhr = new XMLHttpRequest();
const method = 'GET';
const url = "http://127.0.0.1:8000/userdetails/";
const responseType = "json";
xhr.responseType = responseType; // Let the xhr request know that its getting a json
xhr.open(method, url); //This opens the request with the method and url entered
xhr.onload = function(){
console.log("This is the response: ",xhr.response)
callback(xhr.response, xhr.status)
}
xhr.onerror = function(){
callback({"message":"The request was an error"}, 400)
}
xhr.send();//Trigger that request
}
I get:
GET http://127.0.0.1:8000/userdetails/ 403 (Forbidden)
This is the response: {detail: 'Authentication credentials were not provided.'}
How can I make React access the data? I only need to have some minor components made by react and also have it so that if I open it on another browser I can log in with a different user. I have looked into many resources but they do not seem to work for my case. I am using the django server.
You have to get the auth token in your react app. The token is generated once you log in to your Django from react app, and then you need to pass that token along with the get method.
axios
.get('http://www.exapmle.com', {
headers: {
Authorization: `Token ${props.token}`,
},
})
or in your Django app set the permission class as any for that model, or remove IsAuthenticated from the permission class.
permission_classes = [IsAuthenticated]

XHR Get request doesn't load the received HTML string from Express app

I am building my first express app.
So far all my requests from the front-end where triggered by anchor elements for the GET requests and elements for the POST requests. All the responses from the server are generated with express function res.render() or res.redirect().
Now I am trying to implement user authentication with json web tokens and localStorage. Once my user is logged in, I generate a token on the back-end side and send it to the front-end like so:
res.render('loggedIn', {token: token});
Then I save this token in the localStorage (within a pug template):
const token = !{JSON.stringify(token)};
window.localStorage.setItem('token', token);
All good so far, but now I have to send my token back to the back end - From what I have read it is straight forward to do so by including it in the request header. But as my request where only made through anchor element , I now have to make my own "custom" request right ??
So with XMLHttpRequest my code is:
function sendGetWithAuth(endUrl) {
return new Promise((resolve, reject) => {
let request = new XMLHttpRequest();
request.open('GET', endUrl);
request.onreadystatechange = () => {
if (request.readyState === 4){
resolve(request.response);
}
}
request.setRequestHeader('x-auth-token', window.localStorage.getItem('token'));
request.send();
});
}
And instead of using anchor element , I set up a click event listener on my target:
target.addEventListener('click', () => {
const endUrl = '/job/6789'
sendGetWithAuth(endUrl).then(
(response) => {
console.log(response);
}
)
});
When I go back to the browser, click my target the XHR request sends fine, the Headers contain my token, and the response payload contains the HTML send by the res.render(), but nothing happens on the page, I am still on the same page where I clicked my target and have not been redirected to the requested page. The status of the request is: [HTTP/1.1 304 Not Modified 1ms]
( When my target is a link <a href='/job/6789'>TARGET </a>, it works fine (I am redirected to the /job/6789 page. (But no headers are set with the anchor element)).
My first question was: How to load and HTML string response in the browser after and XHR GET request ?
[EDIT 30-10-2020]
My new question is: How is an XHR GET to and end url different than a link (with anchor tag) to that same end url.
Is there a way to place a "custom" request that will redirect to the end url of the request ?
I have tried adding window.location = endUrl after placing the XHR GET request (within the same event handler). It places the XHR GET then redirects me to the endUrl but I loose my token on the way.
(in the browser's console I see the XHR GET endUrl then "Browser navigated to: /endUrl" then another GET endUrl (caused by the redirection I assume but which overrides the headers of my XHR request) so I land on the requested page but without my token.
Using localStorage works well for front-end apps, but in your case it might be better to use a cookie. Plant a cookie by calling res.cookie(<name>, <value>, <options>) in your expressjs route handler, then install the npm package cookie-parser, require it, use it as a middleware in your app, then you can simply use req.cookies.<name> in your job route handler. With this approach you can use an anchor that goes directly to that URL.

Request current users API token Django REST Framework

I have a Django web app that is using the Django REST framework to generate various API endpoints.
I can ensure only logged in users can view/read these endpoints, but now I am at the stage of development where I want users to post to the API using tokens. I have successfully done this, however, I have hard-coded the users token into the post request in Javascript... This worked for testing but obviously is not a good final solution.
Is it possible to request the current users token somehow? Could I then include this token in the POST request head automatically?
Thanks for any help/feedback in advance!!
EDIT:
I think I am close, but I am getting a few errors in my chrome console, and still can't retrieve token.
Console Errors:
toggleScript.js:25 Uncaught DOMException: Failed to execute
'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
at getToken (http://127.0.0.1:8000/static/defaults/toggleScript.js:25:7)
at manageDefaults
(http://127.0.0.1:8000/static/defaults/toggleScript.js:62:5)
at HTMLInputElement.onclick (http://127.0.0.1:8000/defaults/:1:1)
getToken # toggleScript.js:25
manageDefaults # toggleScript.js:62
onclick # (index):1
toggleScript.js:24 POST http://127.0.0.1:8000/api-token-auth/ 415
(Unsupported Media Type)
I have a button when pressed, will trigger the function to retrieve the token, and this is what is causing the error stack above.
toggleScript.js
function getToken(){
var xhr = new XMLHttpRequest();
var url = 'http://127.0.0.1:8000/api-token-auth/';
xhr.open("POST", url, true);
var data = JSON.stringify({"username": "myusername", "password": "mypassword"});
xhr.send(data);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var json = JSON.parse(xhr.responseText);
console.log(json.token);
}
};
}
Django Rest Framework provides an API endpoint for requesting a user's token, given a username and password. You can wire the view into your urls.py:
from rest_framework.authtoken import views
urlpatterns += [
url(r'^auth-token/', views.obtain_auth_token)
]
Then when you POST a valid username and password to that view it will return the token in a JSON response:
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
Your app can then store that and send it in subsequent requests.
An example of retrieving the token using JQuery (assuming the view was mapped to the path ^auth-token/ in your urls.py):
$.post('/auth-token/', { username: 'admin', password: 'whatever' }, function(data) {
// Token available as data.token
});
If you try and post to the auth-token view from within an already authenticated session, Django will likely reject the request with a CSRF token missing or incorrect response. You should either ensure that the session is not authenticated when you retrieve the token, or you could potentially include the X-CSRFToken header in the request. You'd need to extract the value from the csrftoken cookie. For example (using JQuery and the JQuery Cookie plugin):
$.ajax({
url: "/auth-token/",
type: "POST",
headers: {
"X-CSRFToken": $.cookie("csrftoken") # Extract the csrftoken from the cookie
},
data:{ username: "admin", password: "whatever" },
dataType:"json"
}).done(function(data) {
// Token available as data.token
});
More info on obtaining an auth token here

sending cookies with BreezeJS

I am doing a login to a server (different IIS then the one which the client is), the response of this login is a cookie
Set-Cookie:session-token=7ed240cd-fd41-464c-9ccd-d43097ef4d7c; domain=x.x.x.x; path=/
the login is done via JQuery POST, the server is ODATA server - I am initilizing breeze with
breeze.config.initializeAdapterInstances({
modelLibrary: "backingStore",
dataService: "OData"
});
var breezeDataServiceSettings = {
serviceName: serverUrl + 'odata',
hasServerMetadata: true
};
var dataService = new breeze.DataService(breezeDataServiceSettings);
manager = new breeze.EntityManager({ dataService: dataService });
manager.metadataStore.fetchMetadata(dataService).then(succeded, failed);
so far all is ok, however when I am sending requst to get entity (also fetch metadata) the cookie isnt being sent, i have tried to send request with JQuery and the cookie is sent
also I have tried to add headers to the ajax breeze adapter (add the cookie) but it is being ignored.
How can it be solved?
I think I found the solution -
in datajs-1.1.1 under
request: function (request, success, error)
when creating the createXmlHttpRequest object I added
xhr.withCredentials = true;

Categories