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,
});
Related
I am trying to send the contents of a flashcard to a backend route http://127.0.0.1:5000/post onClick and it works when I send the data to webhook.site but when I change it to http://127.0.0.1:5000/post I get the error " question = data['question' TypeError: 'NoneType' object is not subscriptable ". Here is the code for the fetch request: async function postData() {
try {
let result = await fetch('http://127.0.0.1:5000/post', {
method: 'POST',
mode: 'no-cors',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
key: `${Date.now()}`,
question: flashcard.question,
answer: flashcard.answer,
options: flashcard.options
})
});
} catch(e) {
console.log(e)
}
}
and here is the code for the backend route in flask:
#app.route('/post', methods=['GET', 'POST'])
def post():
data = request.get_json()
question = data['question']
answer = data['answer']
options = data['options']
key = data['key']
return jsonify({'question' : question, 'answer' : answer, 'options' : options, 'key' : key})
if __name__ == "__main__":
app.run(debug=True)
I get that the error is stating that "data" has no value which I assume means it's not recieving the JSON objects that I'm posting. It's weird because it works perfectly when I use a webhook.site url. can anyone help? thanks for your time!
Here is a project to create a login system using react. However when it connects to the back-end flask to check whether the login is valid or not, it shows the error message below. Hope someone can help me. I spent so many days trying to fix this and search every related post but still couldn't fix it :(
I'm using the chrome browser.
Also, I have installed the Access-Control-Allow-Origin plugin on chrome.
error message
Access to fetch at 'http://localhost:5000/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
front-end code (react)
fetch(domain+"/login", {
body: JSON.stringify(mydata),
credentials: "include",
headers: new Headers({
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials': true
}),
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response=>{
if(response.ok){
return response.json()
}
}
back-end(flask) include below code to allow cross origin
app = Flask(__name__)
CORS(app, supports_credentials=True)
#app.route('/login', methods=['GET', 'POST'])
def login():
data = request.json
print(data)
result = {}
login_info = {
"code": -1,
"message": ""
}
if data:
username = data['username']
password = data['password']
cur = database.cursor(dictionary=True)
cur.execute(
"SELECT username, password, role FROM user WHERE username = %s AND password = %s AND disabled = 0;", (username, password))
# cur.execute("SELECT uid, displayname, rank, disabled FROM user WHERE username = %s AND password = %s;", (username, password,))
# cur.execute("SELECT uid, displayname, rank, disabled FROM user WHERE username = %s AND password = AES_ENCRYPT(%s, UNHEX(SHA2('encryption_key', )));", (username, password,))
account = cur.fetchone()
if account:
# modify_info(1, "Login successfully!")
login_info["code"] = 1
login_info["message"] = "Login successfully!"
account_str = json.dumps(account, cls=MyEncoder)
account_json = json.loads(account_str)
session["username"] = account_json["username"]
result["username"] = username
result["role"] = account_json["role"]
result["isLogin"] = 1
else:
# modify_info(0, "Login not successful")
login_info["code"] = 0
login_info["message"] = "Login not successful"
cur.close()
return jsonify({"code": login_info["code"], "data": result, "message": login_info["message"]})
In the specification, the Access-Control-Allow-Credentials: true header is not allowed to use with
the Access-Control-Allow-Origin: * header.
However, * is the default value for the origin header in flask cors, you should set it to a specific value, for example:
app = Flask(__name__)
CORS(app, origins=['http://localhost:3000'], supports_credentials=True)
Also, here is link to the document about it: https://flask-cors.readthedocs.io/en/latest/api.html
Update
It seems like the samesite of the cookie problem, here's the code to set it:
from flask import Flask, make_response
from flask_cors import CORS
app = Flask(__name__)
CORS(app, supports_credentials=True)
#app.route("/", methods=['POST'])
def index():
res = make_response()
res.set_cookie("name", value="I am cookie", samesite='Lax')
return res, 500
app.run(debug=True)
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 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.
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 !!!