I'm having an issue with CORS. I've implemented my RESTful backend using Flask and the frontend with React JS. When I attempt to make any request by calling an API, I'm blocked by CORS.
This is the API.js file
const APIURL = new URL('http://127.0.0.1:5000/api/');
async function logIn(credentials) {
let response = await fetch(APIURL + 'login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials),
});
if (response.ok) {
const user = await response.json();
return user;
} else {
const errDetail = await response.json();
throw errDetail.message;
}
}
Server side we have:
main.py:
from src import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True )
init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from os import path
from flask_login import LoginManager
from flask_jwt_extended import JWTManager
from flask_cors import CORS
from dotenv import load_dotenv
import os
load_dotenv()
db = SQLAlchemy()
DB_NAME = "db"
DB_USERNAME = "root"
DB_PASSWORD = "root"
def create_app():
app = Flask(__name__)
CORS(app, origins='http://localhost:3000', methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
app.config['SECRET_KEY'] = os.getenv("SECRET_KEY")
app.config['CORS_HEADERS'] = 'Content-Type'
app.config["JWT_SECRET_KEY"] = os.getenv("JWT_SECRET_KEY")
app.config['SQLALCHEMY_DATABASE_URI'] = f'mysql://{DB_USERNAME}:{DB_PASSWORD}@localhost/{DB_NAME}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
jwt = JWTManager(app)
db.init_app(app)
from .views import views
from .auth import auth
app.register_blueprint(views, url_prefix='/api')
app.register_blueprint(auth, url_prefix='/api')
from .models import User
with app.app_context():
db.create_all()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
@login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
return app
and auth.py:
from flask import Blueprint, render_template, request, flash, redirect, url_for, jsonify
from flask_cors import cross_origin
from .models import User
from werkzeug.security import generate_password_hash, check_password_hash
from . import db
from flask_login import login_user, login_required, logout_user, current_user
from flask_jwt_extended import create_access_token
from flask_jwt_extended import get_jwt_identity
from flask_jwt_extended import jwt_required
auth = Blueprint('auth', __name__)
@auth.route('/login', methods=['POST', 'OPTIONS'])
def login():
data = request.json
print(data)
email = data.get('email')
password = data.get('password')
user = User.query.filter_by(email=email).first()
if user:
if check_password_hash(user.password, password):
access_token = create_access_token(identity=user.pe_id)
return jsonify(access_token=access_token), 200
else:
return jsonify({'error': 'Incorrect user or password, try again.'}), 400
else:
return jsonify({'error': 'Incorrect user or password, try again.'}), 400
I continue to obtain this error:
Access to fetch at 'http://127.0.0.1:5000/api/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'.
On the server terminal I see:
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 134-368-434
127.0.0.1 - - [24/Nov/2023 14:51:38] "OPTIONS /api/login HTTP/1.1" 415 -
I have tryed the simply case reported by doc of flask-cors so i have tried todo that:
def create_app():
app = Flask(__name__)
CORS(app)
but nothing changed. After i tried also to specify more info like that:
def create_app():
app = Flask(__name__)
CORS(app, origins='http://localhost:3000', methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
but also in this case still nothing.
I have also tried to add OPTIONS method to login api:
@auth.route('/login', methods=['POST', 'OPTIONS'])
def login():
As the error says, your server needs to reply with the
Access-Control-Allow-Credentials: true
header if you're goign to pass credentials. According to theflask_cors
documentation, to do that you need to specifysupports_crentials = True
in your call (it defaults to False):So:
Note: Once you've fixed that, you may start getting a 415 error because the
login
handler doesn't distinguish betweenPOST
andOPTIONS
, see this question's answers to see how to handle that (basically: check the method before trying to read the request body).