I have the following code in my react app:
I am sending an update request to rest backed which requires a user to be authenticated to perform PUT/POST/DELETE requests.
const update = (e) => {
e.preventDefault()
const formData = new FormData(form.current);
console.log('Token ' + localStorage.getItem("token")) // valid token
const requestOptions = {
method: 'PUT',
headers : {
// 'Authorization': 'Basic ' + btoa('user:password') // basic authentication works
"Authorization": 'Token ' + localStorage.getItem("token"),
},
body: formData
};
fetch(url, requestOptions)
.then(async response => {
const data = await response.json();
if(!response.ok){
const error = (data && data.message ) || response.status;
return Promise.reject(error)
}
alert('member updated')
history.push("/members")
})
.catch(error => console.error('Some error ', error))
}
Unfortunately, I'm getting these in console logs:
PUT http://localhost:8000/uskindia/56/ 403 (Forbidden)
Some error 403
And this in backed logs:
Forbidden: /uskindia/56/
[... *:*:*] "PUT /uskindia/56/ HTTP/1.1" 403 58
Trying to solve this for the last 24 hours but not getting it right.
From various tries, it seems like:
backend DRF and django-rest-auth is not handling token properly
tried various user agents like curl, httpie and postman to view request and response closely
Even in backed put logs, but request.user == AnonymousUser with token based authorisation.
works well with basic authorizatin, scheme.
if you are using djangorestframework for backend you must send token with this format :
"Authorization": 'Bearer ' + localStorage.getItem("token"),
use Bearer instead of token.
There was a typo in settings.py
# Earlier
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASS': [
'rest_framework.authentication.TokenAuthentication',
],
# ....
}
# Changed to
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
# ...
}
Thanks to Blog by Vitor Freitas
which made it clear that, if response contains
WWW-Authenticaate:Token then it means Token authentication was working.
As this was missing in my case, so I started all over setting REST_FRAMEWORK settings from scratch and found the root cause of issue.
Related
I have a react application which has a post method requesting data from a lambda function. It is working fine in my local machine. But when I deployed the application on s3 bucket and try to access it through s3 endpoint, it gives me 405 Method not allowed error. After search I found that post method will not work with s3 endpoint, So again I tried with rest api endpoint, but it shows file name xml. I am able to access application when I add index page to rest api endpoint, But here also I am getting 412 error in post method. So in any case I am getting error with post method. Can someone help me with that. I don't know what I am missing. Below is my code:
const apiUrl = process.env.NODE_ENV === 'production' ? process.env.REACT_APP_PROD_API_URL : process.env.REACT_APP_DEV_API_URL;
const params = (query: string) => ({
headers: {
"Accept": "application/sparql-results+json",
"Accept-Encoding": "gzip",
"Content-Type": "application/x-www-form-urlencoded"
},
method: "POST",
body: `query=${encodeURIComponent(query)}`
})
const fetchClinicalStudies = async () => {
const clinicalStudiesQuery = `
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX disco: <http://rdf-vocabulary.ddialliance.org/discovery#>
SELECT ?id ?label
WHERE {
?id a disco:Study ;
dcterms:title ?label .
}
ORDER BY ?label`
const response = await fetch(apiUrl ?? '', params(clinicalStudiesQuery))
return response;
}
I am trying to associate my Alexa skill with a catalog that I created by hitting the https://api.amazon.com/v0/catalogs endpoint with an Auth token that I had generated through a LWA profile.
This worked, and I created a catalog like so:
{
associatedSkillIds: [],
createdDate: '2022-01-22T20:50:37.318Z',
id: 'amzn1.ask-catalog.cat.[REDACTED]',
lastUpdatedDate: '2022-01-22T20:50:37.318Z',
title: 'TestCatalog',
type: 'AMAZON.AudioRecording',
usage: 'AlexaTest.Catalog.AudioRecording'
}
However, the next step, associating my Alexa skill with the Catalog is always returning 401 https://developer.amazon.com/en-US/docs/alexa/smapi/catalog-content-upload.html#associate-catalog-with-skill
This is my function to attempt to associate the skill with the catalog:
async associateSkillWithCatalog() {
console.log(`Associating skill...`);
const accessToken = await this.getRefreshToken(); // makes post to https://api.amazon.com/auth/o2/token
console.log(this.alexaEndpoint + this.skillAssoc(cat.id, skillId));
const response = await axios.put(
"https://api.amazonalexa.com/v0/skills/amzn1.ask.skill.[REDACTED]/catalogs/amzn1.ask-catalog.cat.[REDACTED]",
{
headers: {
'Content-type': 'application/json',
'Authorization': `Bearer ${accessToken}`
}
}
);
return response.data;
}
Always receiving back this Error: Request failed with status code 401\n at createError.
Why would I be receiving 401 Error here, despite other requests against this API not failing?
Thanks!
I could really use some help. I can't figure out what I'm doing wrong. I keep getting
Edit : Frontend React application runs on localhost:3000, backend is running on localhost:5000
Access to XMLHttpRequest at 'http://localhost:5000/api/auth/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
def create_app(test_config=None):
logger = logging.getLogger(__name__)
logger.info("Flask App Starting")
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
CORS(app)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
logging.getLogger('flask_cors').level = logging.DEBUG
app.config.from_mapping(
SECRET_KEY="dev",
JWT_SECRET_KEY="super secret key",
JWT_ACCESS_TOKEN_EXPIRES=timedelta(hours=2),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile("config.py", silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
jwt = JWTManager(app)
"""
Adding blueprints
"""
from app.routes import tester
from app.routes import auth
from app.routes import api_routes
from app.routes import similar_routes
app.register_blueprint(tester.bp)
app.register_blueprint(auth.bp)
app.register_blueprint(api_routes.bp)
app.register_blueprint(similar_routes.bp)
#app.before_request
def check_login():
"""Before each request check if token exist."""
pass
logger.info("Checking if token is required")
if (not getattr(app.view_functions[flask.request.endpoint], "is_public", False)):
logger.info("Token required")
try:
result = verify_jwt_in_request(locations="headers")
logger.debug(f"Identity sent in is {get_jwt_identity()}")
except Exception as e:
logger.error("Error occured during checking token")
logger.error(e)
return jsonify(msg="Token Expired"),401
#app.errorhandler(Exception)
def all_exception_handler(error):
logger.error("Error caught" + str(error) )
return jsonify(msg="Oh no! A Server error occured. :,( "), 500
return app
if __name__ == "__main__":
loggingSetup()
app = create_app()
logger.info("App Created")
app.run(debug=True)
logger.info("App Running")
I'm making API calls from my react frontend, using axios
axios.defaults.baseURL = "http://localhost:5000/api"
function getHeaders(token){
return {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8',
"Authorization": "Bearer " + token,
'Access-Control-Allow-Origin': '*'
}
}
async function createCustomObject(token) {
let url = "/ontology/create-object";
let options = {
method: "POST",
url: url,
headers: getHeaders(token),
};
let response = await axios(options).then((response) => {
let data = response.data
}).catch((error) => {
handleError(error.response)
})
return response;
What am I missing?
You would set your origin to http://localhost:3000:
cors = CORS(app, resources={r"/api": {"origins": "http://localhost:3000"}})
'Access-Control-Allow-Origin': 'http://localhost:3000'
I resolved my issue using proxy after trying a couple of failed attempts using CORS solution.
I simply put "proxy": "http://127.0.0.1:5000" in my package.json and therefore, I can then use
fetch(`/test`)
.then((res) => res.json())
.then((data) => {
//do something
});
easily in my app without actually providing the full url to the backend (http://127.0.0.1:5000).
I am working on a Django REST framework project. I am using JWT authentication, in my login view i have set permission classes to AllowAny. '
#decorators.api_view(["POST"])
#decorators.permission_classes([permissions.AllowAny])
def login(request):
print("REQUEST in login = ", request)
try:
username = request.data['username']
print(username)
user = User.objects.get(username=username)
except Exception as e:
return response.Response({'username': "User doesn't exist"} , status.HTTP_400_BAD_REQUEST)
try:
password = request.data["password"]
print(password)
except:
return response.Response({"password": "Password not valid"}, status.HTTP_400_BAD_REQUEST)
user = authenticate(username=username, password=password)
if user is not None:
refresh = RefreshToken.for_user(user)
res = {
"message": "Logged in successfully",
"refresh": str(refresh),
"access": str(refresh.access_token),
}
return response.Response(res, status.HTTP_200_OK)
else:
return response.Response({"password": "Password not valid"}, status=status.HTTP_400_BAD_REQUEST)
When i send a normal request everything works as expected.
But when i set the Authorization header to empty string (or null both have same problem)
Authorization: null or ''
The reason why Authorization is set to null is because of axios in React. It looks for access token in local storage. If not present it sets it to null.
const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 60000,
headers: {
Authorization: localStorage.getItem('access_token')
? 'Bearer ' + localStorage.getItem('access_token')
: null,
'Content-Type': 'application/json',
accept: 'application/json',
},
});
I have set the default authentication to JWT in django.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
I can think of removing the Authorization header and setting new axiosInstance while logging in. But this will make the code less cleaner. Please suggest any fix for this problem.
You can provide a custom subclass of rest_framework_simplejwt.authentication.JWTAuthentication and handle the value of the header any way you like.
import re
from rest_framework_simplejwt.authentication import JWTAuthentication
class CustomJWTAuthentication(JWTAuthentication):
def get_header(self, request):
header = super().get_header(request)
# value is usually "Bearer <token>"
if header is None:
return header
if not re.match("Bearer \S+", header)
return None
return header
We are doing this because JWTAuthentication will only skip checking when JWTAuthentication.get_header() returns None else it will keep going.
So providing values like null, undefined, or an empty string in axios will send it's string representation (e.g. null will be "null") and JWTAuthentication will take it as it is.
Another solution outside django but in axios is to not include it through tranformRequest.
const apiAxios = axios.create({
baseURL: baseURL.href,
transformRequest: [
function (data, headers) {
const accessToken = window.localStorage.getItem("accessToken")
if (accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`
} else {
delete headers.Authorization
}
return JSON.stringify(data)
}
],
headers: defaultApiHeaders,
});
I have a Rest spring-boot API that when a user authenticates the api returns the token jwt, I noticed in the browser that the token appears in the Response Header> Authentication and in the tests with the Postman it shows in the Body.
How can I get this token to store in the Local Storage browser by Reactjs?
My code that makes the requests looks like this:
import { ACCESS_TOKEN, API_BASE_URL } from '../constants';
export function request (options) {
const headers = {
'Content-Type': 'application/json',
}
if (localStorage.getItem(ACCESS_TOKEN)) {
headers.append('Authorzation', 'Bearer ' + localStorage.getItem(ACCESS_TOKEN))
}
return fetch(API_BASE_URL+options.url, {
method: options.method,
headers: headers,
body: options.body
})
.then(function(response){
// Falta pegar o token e gravar na local estorage
if (!response.ok) {
return Promise.reject(json);
}
return json;
});
};
// Save data to the current local store
localStorage.setItem("username", "John");
// Access some stored data
alert( "username = " + localStorage.getItem("username"));
The first argument of setitem is the key
My account got blocked by some down votes questions, the funny thing is I have to re-edit them, even though I already have the accepted answer.I do not understand what's the point to do this.I am so frustrated by this stackoverflow system.
Now, I basically can do nothing but keep editing my questions, and they have all been answered. This is ridiculous !!!