Allow CORS between ReactJS and Flask Backend

113 views Asked by At

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():

Network screenshot image

1

There are 1 answers

0
T.J. Crowder On BEST ANSWER

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 the flask_cors documentation, to do that you need to specify supports_crentials = True in your call (it defaults to False):

supports_credentials (bool) –

Allows users to make authenticated requests. If true, injects the Access-Control-Allow-Credentials header in responses. This allows cookies and credentials to be submitted across domains.

note: This option cannot be used in conjunction with a ‘*’ origin

Default : False

So:

CORS(
    app,
    origins='http://localhost:3000',
    methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    supports_credentials=True
)

Note: Once you've fixed that, you may start getting a 415 error because the login handler doesn't distinguish between POST and OPTIONS, see this question's answers to see how to handle that (basically: check the method before trying to read the request body).