How to access a Django rest Framework API from React - javascript

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]

Related

Receiving HTTP POST request sent by frontend JS using router in Express.js

I am building a web application using the Express framework. I'm trying to send the username of an account (which is stored in a cookie) to the backend. This is the code in the frontend JS that sends the HTTP Post request:
var httpRequest = new XMLHttpRequest();
httpRequest.open('POST', '/loggedInAutoSubmitForm', true);
httpRequest.setRequestHeader("Content-type","application/application/json");
httpRequest.send(JSON.stringify(content));
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
var json = httpRequest.responseText;
console.log(json);
}
else {
console.log("some error happened");
}
};
content is defined and this is what I get in Chrome's console when I log content:
{"username":"testUser1"}
The content of this request is correct, but I'm not receiving it with the router in index.js. This is the backend code:
var express = require('express');
const router = express.Router();
router.post('/loggedInAutoSubmitForm', function(req, res) {
console.log(req.body);
// find user and render logged in page
});
I haven't implemented the code yet, but the problem is that I'm getting an empty request body. This is what happens in my console when I log req.body:
{}
Also, I get nothing logged in Chrome's console after the request has been sent, so I'm thinking maybe the request didn't end?
I'm fairly new to web development and I'm not sure if this is the correct way to send a Post request from the frontend. If it is, I have no idea what is wrong with this code. I have searched online but it seems that this is the proper way to receive the Post request using router. Is there any way to make it work? Any help is appreciated.

Send post request with flask_jwt_extended

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);

AJAX GET request not sending data to Django

I am trying to send data in localStorage via an AJAX GET request to Django, but the Django server never receives it. I am confident that I have data in localStorage("preselection") as console.log shows it. Here is my JavaScript snippet which is inside index.html (am a beginner, so apologies if this is basic and I'm avoiding jQuery for now):
var preselection = localStorage.getItem("preselection");
function previous_selection () {
if (localStorage.getItem("preselection") != null) {
console.log("PRESELECTION IS: ", preselection);
const data = new FormData();
data.append("preselection", preselection);
const request = new XMLHttpRequest();
request.open('GET', '/');
request.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
request.send(data);
request.onload = () => {
var url = '/prebasket';
window.location.href = url;
};
return false;
}
}
previous_selection();
Below is my view in views.py. I tried request.GET.dict(), request.GET.get(), request.GET.copy(), etc. and my data comes as JSON in case it matters, but Django just gets an empty {} or Null for q_preselection:
#login_required
def index(request):
q_preselection = request.GET.dict()
print(q_preselection) # comes back empty
context = {
#irrelevant
}
return render(request, "pizza/index.html", context)
XMLHttpRequest send() does not pass the body data for GET
send() accepts an optional parameter which lets you specify the
request's body; this is primarily used for requests such as PUT. If
the request method is GET or HEAD, the body parameter is ignored and
the request body is set to null.
Use POST instead, you almost never want to have GET request with BODY ( parameters should be passed through URL for GET)

Am i missing steps in setting up my AWS account to sucessfully perform GetFederatedToken?

I'm trying to make a button in an app which automatically logs the user into the AWS console in a browser.
When the user clicks the button, it should prepare a token and compose a URL and then open that URL in a new window which causes the login to happen resulting in the user being in the AWS console.
I'm using the AWS Javascript API to accomplish this (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/top-level-namespace.html).
Specifically, i'm following the "GetFederationToken" steps described here:
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html
And using the sample code here:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#getFederationToken-property
I'm using an IAM User to request the token in the first step. This user has the AdministratorAccess policy and i can successfully call the getFederatedToken API call. I get back the expected token, secret and access keys.
I then compose the URL:
AWS.config.update({
accessKeyId: "ACCESS",
secretAccessKey: "SECRET",
"region": "us-east-1",
accountID: "123456789"
});
var sts = new AWS.STS();
var params = {
Name: 'user',
DurationSeconds: 900,
Policy: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Stmt1\",\"Effect\":\"Allow\",\"Action\":\"s3:ListAllMyBuckets\",\"Resource\":\"*\"}]}"
};
var console_url = "https://console.aws.amazon.com/"
var signin_url = "https://signin.aws.amazon.com/federation"
var issuer_url = "https://myCompanyURL.com/"
sts.getFederationToken(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else{
//Construct the session token
var sessionTokenJSON = {
sessionID : data.Credentials.AccessKeyId,
sessionKey : data.Credentials.SecretAccessKey,
sessionToken : data.Credentials.SessionToken
};
var sessionTokenString = JSON.stringify(sessionTokenJSON);
var encodedSessionToken = encodeURIComponent(sessionTokenString);
console.log(sessionTokenString);
//Get the SignIn Token
var signInURL = signin_url+"?Action=getSigninToken&Session="+encodedSessionToken;
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", signInURL, false );
xmlHttp.send( null );
var result = xmlHttp.responseText;
result = JSON.parse(result);
//Get the login URL
var encodedIssuer = encodeURIComponent(issuer_url);
var encodedDestination = encodeURIComponent(console_url);
var loginURL = signin_url+"?Action=login&Destination="+encodedDestination+"&SigninToken="+result.SigninToken;
console.log(loginURL)
//Open the URl
window.open(loginURL);
}
});
I've verified the access and secret and account ID i am using are valid and corespond to the IAM User i want to use.
I've tried using both the console_url above and the one shown in my AWS Account management section, the URL above produces the error detailed below, the IAM Console login from my AWS Account management section returns a 404.
issuer_url and console_url are properly formatted https URLs. They are URI encoded, as described in the instructions, and my resulting URL looks structurally the same as the example in the site linked above.
The new window opens when i invoke the window.open() call but i always get this response page:
Amazon Web Services Sign In
Some of your credentials are missing. Please contact your administrator.
I'm at a loss for what steps i may have missed or pre-requisites i may have overlooked, any suggestions would be greatly appreciated.
The "sessionID" parameter should have been "sessionId". This misnamed variable still passes for getting a token but then the token is malformed.

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

Categories