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)
Related
I am trying to connect my react frontend to my flask api backend. Note that flask-cors is already installed.
I initiated CORS as CORS(app) . the login passes but I keep getting this error when I go to project route:
Access to fetch at 'http://192.168.101.4:5000/project' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
My init.py file:
from flask import Flask, request, make_response
from .extensions import db, migrate, jwt, mail, CORS
from .models import TokenBlocklist
from .routes.base import base
from .routes.auth import auth
from .routes.core import core
from .routes.admin import admin
from .routes.user import user
from .routes.project import project
def create_app(config_file='config.py'):
app = Flask(__name__)
app.config.from_pyfile(config_file)
#jwt.token_in_blocklist_loader
def check_if_token_revoked(jtw_header, jwt_payload: dict)->bool:
jti = jwt_payload['jti']
token = db.session.query(TokenBlocklist.id).filter_by(jti=jti).scalar()
return token is not None
# initiate
db.init_app(app)
migrate.init_app(app, db)
jwt.init_app(app)
mail.init_app(app)
CORS(app)
app.register_blueprint(base)
app.register_blueprint(auth, url_prefix='/auth')
app.register_blueprint(core, url_prefix='/core')
app.register_blueprint(admin, url_prefix='/admin')
app.register_blueprint(user, url_prefix='/user')
app.register_blueprint(project, url_prefix='/project')
return app
my auth.py:
"""
Route: endpoints for authentication
"""
from flask import Blueprint, jsonify, request, current_app, url_for, make_response
import datetime
import uuid
import validators
from itsdangerous import URLSafeTimedSerializer
from flask_jwt_extended import create_access_token, create_refresh_token, get_jwt_identity, jwt_required, unset_jwt_cookies, get_jwt, set_access_cookies, set_refresh_cookies
from werkzeug.security import generate_password_hash
from flask_cors import CORS
from ..utils import send_mail
from ..extensions import db, status, jwt
from ..models import User, TokenBlocklist
auth = Blueprint('auth', __name__)
CORS(auth)
#auth.route('/confirm-email/<token>')
def confirm_email(token):
s = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
try:
email = s.loads(token, salt='email-confirm', max_age=200)
# mark user mail as confirmed
user = User.query.filter_by(email=email).first()
if not user.is_confirmed:
user.is_confirmed = True
db.session.commit()
return jsonify(msg='email confirmed'), status.ok
except:
return jsonify(msg = False), status.bad
#auth.route('/register', methods=['POST'])
def register():
"""
recieve request->username, email, password
check username & email exists or not, if not hash password and store.
send email with confirmation link
"""
data = request.get_json()
username = data['username']
email = data['email']
password = data['password']
# check existance of requirements in request json
if not username or not email or not password:
return jsonify(msg='missing json data'), status.bad
# email validations
if not validators.email(email):
return jsonify(msg='invalid email address'),
# check if username and email is taken
user = User.query.filter_by(username=username).first()
if user:
return jsonify(msg='username already taken')
user = User.query.filter_by(email=email).first()
if user:
return jsonify(msg='email already taken')
# save new user in db
new_user = User(
username = username,
email = email,
password = generate_password_hash(password, 'sha256'), # probably not a good practice to use User
public_id = uuid.uuid4()
)
try:
db.session.add(new_user)
db.session.commit()
except:
return jsonify(msg='could not save')
# create email confirmation link
token = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
token = token.dumps(email, salt='email-confirm')
link = url_for('auth.confirm_email', token=token, _external=True)
# send mail
body = f'confirmation link: {link}'
mail_sent = send_mail(recipient=email, body=body, subject='Linkage Email Confirmation')
if not mail_sent:
return jsonify(msg= 'email could not be sent')
return jsonify(msg= 'mail sent' ,email=email, token=token), 201
#auth.route('/login', methods=['POST'])
def login():
"""
recieve json data->email, password
check if email exists,
check user's hashed password and if email is confirmed.
generate refresh and access token and return
"""
data = request.get_json()
email = data['email']
password = data['password']
user = User.query.filter_by(email=email).first()
# email not found
if not user:
return make_response(
'User not found',
401,
{'WWW-Authenticate' : 'Basic realm ="could not verify"'}
)
# email found
verified = user.verify_password(password)
if verified is False:
return make_response(
'password mismatch',
401,
{'WWW-Authenticate' : 'Basic realm ="could not verify"'}
)
# authenticated, now generate tokens
user_public_id = user.public_id
refresh_token = create_refresh_token(identity=user_public_id)
access_token = create_access_token(identity=user_public_id)
# i am totally not sure about the following, but the fe dev *insists*
response = jsonify({"x-access-token": access_token})
response.set_cookie("refresh_token_cookie", refresh_token)
return response, 200
And Finally my project.py:
from datetime import datetime
from flask import Blueprint, jsonify, request, make_response
from flask_jwt_extended import get_jwt_identity, jwt_required
from ..extensions import db
from ..models import Project
project = Blueprint('project', __name__)
# this route must be deleted before production
#project.route('/', methods=['GET'])
#jwt_required()
def all_projects():
user_public_id = get_jwt_identity()
projects = Project.query.filter_by(user_public_id=user_public_id).all()
projects_data = []
for project in projects:
project_data = {}
project_data['id'] = project.id
project_data['domain'] = project.domain
project_data['name'] = project.name
project_data['user_public_id'] = project.user_public_id
project_data['wp_password'] = project.wp_password
project_data['wp_username'] = project.wp_username
project_data['date_added'] = project.date_added
projects_data.append(project_data)
return jsonify(projects = projects_data), 20
CORS related configs in my config.py file:
#CORS
CORS_ALLOW_HEADERS = ['Content-Type', 'Authorization']
CORS_SUPPORTS_CREDENTIALS = True
CORS_HEADERS = ['Content-Type', 'Authorization']
I am totally lost here. I don't know where is the redirect is happening.
Thank you for your time.
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 the following problem. I am trying to set httpOnly cookie and nothing happens. I spent a few hours trying to solve this issue and I have no idea what is going on... My architecture is the following:
Backend: Python fast-api hosted on Heroku, available at https://api.mysuperdomain.com.
Frontend: GatsbyJs hosted on Netlify, available at https://mysuperdomain.com
When I call login request from React component:
const handleSubmit = async (e) => {
e.preventDefault()
const config = {
headers: {
crossDomain: true,
withCredentials: true,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
const requestBody = {
username: emailRef.current.value,
password: passwordRef.current.value
}
try {
const data = await axios.post('https://api.mysuperdomain.com/login', qs.stringify(requestBody), config)
I get response from my backend with headers, set-cookie:
set-cookie: Authorization="Bearer somethinghere"; Domain=.mysuperdomain.com; expires=Tue, 28 Jul 2020 20:40:32 GMT; Max-Age=1800; Path=/; SameSite=lax
unfortunately in browser storage I cannot see this cookie.
My backend(API) sets the cookie in the following way:
#app.post("/login")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=400, detail="Incorrect username or password")
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": form_data.username}, expires_delta=access_token_expires
)
token = jsonable_encoder(access_token)
response = JSONResponse({'status': 'authenticated'})
response.set_cookie(
key="Authorization",
value=f"Bearer {token}",
domain=".mysuperdomain.com",
httponly=True,
max_age=1800,
expires=1800,
)
return response
My DNS records are within the Cloudflare, and CNAME record for backend is proxied:
Typ Name Content TTL Proxy status
CNAME api limitless-starfish-something.herokudns.com Auto Proxied
SSL/TLS encryption mode is Flexible (Encrypts traffic between the browser and Cloudflare). Backend at Heroku has no SSL Certificate therefore I set flexible SSL/TLS encryption mode.
Maybe it is somehow related to above config?
I think this happens because you didn't add a CORS middleware to your app, in FastAPI, allow_credentials is set to bool = False in default. But you can change that easily.
First you need to import CORSMiddleware from fastapi.middlewares
from fastapi.middleware.cors import CORSMiddleware
Then we can add a middleware to our app
app.add_middleware(
CORSMiddleware,
allow_credentials=True,
)
Also you can add origins and all other stuff with CORSMiddleware, for more related info check FastAPI-CORS out.
I have to subscribe to cometD Salesforce channel and hence building cometD client in python. I am using the below python library.
https://github.com/dkmadigan/python-bayeux-client
And below is the handshake response I am getting
{'Host': ['xxxxx.my.salesforce.com/cometd/42.0/'], 'Content-Type': ['application/x-www-form-urlencoded'], 'Authorization': ['admin#123Pi6s9Y2QVergfergregpqqY']} message={"channel":"/meta/handshake","id":"1",
"supportedConnectionTypes":["callback-polling", "long-polling"],
"version":"1.0","minimumVersion":"1.0"} Headers({'host': ['xxxxx.my.salesforce.com/cometd/42.0/'], 'content-type': ['application/x-www-form-urlencoded'], 'authorization': ['admin#123Pi6s9Y2QVergfergregpqqY']}) {u'successful': False, u'advice': {u'reconnect': u'none'}, u'ext': {u'replay': True, u'sfdc': {u'failureReason': u'401::Request requires authentication'}, u'payload.format': True}, u'error': u'403::Handshake denied', u'id': u'1', u'channel': u'/meta/handshake'}
And I am getting 401::Request requires authentication.
In the Authorization key, I have concatenated password and Access token i.e. admin#123Pi6s9Y2QVergfergregpqqY where admin#123 is the password I use to login to Salesforce.
I have been banging my head since 2 days but not able to figure out why handshake is failing. Any suggestions?
I believe that the authorization key is incorrect. It is not your password that is expected but an OAuth access token or session id that you receive after you log into salesforce. See the different OAuth flows, if you are testing you can use the username password flow.
The following method u can use to get the session id when needed
import requests
import json
LOGIN_INSTANCE_URL = 'https://test.salesforce.com/services/oauth2/token'
LOGIN_USER_NAME = 'username_here'
CLIENT_ID = 'connected app consumer key'
CLIENT_SECRET = 'connected app consumer secret'
PASSWORD = 'password token'
def connect(authUrl, clientId, secret, username, password):
headers = {
}
postBody = {
'grant_type': 'password',
'client_id': clientId,
'client_secret':secret,
'username': username,
'password': password
}
try:
response = requests.post(authUrl, data = postBody, headers = headers)
#response.raise_for_status()
if (response.status_code == 200):
authResponse = response.json()
return authResponse['access_token']
else: #if not 200 see what the problem was
print response.text
except requests.exceptions.RequestException as e:
print e
print(connect(LOGIN_INSTANCE_URL, CLIENT_ID, CLIENT_SECRET, LOGIN_USER_NAME, PASSWORD))
This is just sample code that should work, but you need to create a connected app first. for stand alone app without user intervention JWT flow is better.