I have searched for hours trying to find a decent solution to my problem and I can't seem to find a solution. Basically, I broke up my Flask API into two separate files using blueprints, one that serves as the main Flask application and the other to handle all the authentication routes. I am running into CORS issues... Before I separated them, it worked fine with the flask-cors
library. However, when I broke it into separate files using blueprints, it didn't work. I did a bunch of research and there seems be a couple ways to do it, but the best way seems to be solving the problem on the server side (at least in theory because I haven't found a solution to it yet).
app.py
# Third-party libraries
from flask import Flask
from flask_cors import CORS
# Internal imports
from login import login_page
# Flask app setup
app = Flask(__name__)
app.register_blueprint(login_page)
CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
if __name__ == "__main__":
app.run(host="localhost", debug=True)
login.py
# Third-party libraries
from flask import Flask, redirect, request, url_for, Blueprint, render_template, abort
from flask_login import (
LoginManager,
current_user,
login_required,
login_user,
logout_user
)
from oauthlib.oauth2 import WebApplicationClient
import requests
from flask_cors import CORS
login_page = Blueprint('login_page', __name__)
CORS(login_page)
# Login route
@login_page.route('/login', methods=['GET'])
def login():
# Find out what URL to hit for Google login
google_provider_config = get_google_provider_config()
authorization_endpoint = google_provider_config["authorization_endpoint"]
# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri = request.base_url + "/callback",
scope = ["openid", "email", "profile"]
)
return redirect(request_uri)
From the documentation: https://flask-cors.readthedocs.io/en/latest/api.html#using-cors-with-blueprints
Which seems to be what I am doing... But it's not working.
Edit: React code:
authenticate = async () => {
console.log('authenticate called')
const response = await users.get('/login');
}
I added a test route to see if the way I was handling blue prints was incorrect with the flask-cors
library as follows:
@login_page.route('/test')
def test():
print('test')
return 'test'
Sure enough, I was able to see the text print on the console. So this leaves me to believe it is something else preventing me from accessing Google's authentication servers.
Flask application is running on localhost:5000
and React application is running on localhost:3000
. The string that gets passed into the redirect
method in login
is as follows:
However, this was working prior to working with React.
Chrome console error:
Access to XMLHttpRequest at '...' (redirected from 'http://localhost:3000/login') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have also added a proxy to my package.json file as follows:
"proxy": "http://localhost:5000"
Which I got from: https://blog.miguelgrinberg.com/post/how-to-create-a-react--flask-project/page/3
Update:
I have decided to go with the approach of solving this problem using Nginx since it seems to that flask-cors library is not suitable for this problem as no solution seems to be possible.
Nginx.conf
user www-data;
worker_processes 1;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
upstream react-client {
server react-client;
}
upstream flask-backend {
server flask-backend;
}
server {
listen 80;
server_name localhost;
location / {
# From docker-compose, react-client port 80 is exposed
proxy_pass http://react-client;
}
# location /login/callback
# location /logout
}
server {
listen 3000;
server_name localhost;
location /login {
proxy_pass http://flask-backend;
}
}
}
docker-compose.yml:
version: '3'
services:
reverse-proxy:
image: nginx:1.17.10
container_name: reverse-proxy
depends_on:
- flask-backend
- react-client
volumes:
- ./reverse-proxy/nginx.conf:/etc/nginx/nginx.conf
ports:
- 80:80
react-client:
image: react-client
container_name: react-client
build:
context: ./client
depends_on:
- flask-backend
ports:
- 3000:3000
restart: on-failure
flask-backend:
image: flask-backend
container_name: flask-backend
build:
context: ./api
ports:
- 5000:5000
restart: on-failure
With the Nginx configuration and Docker files, I am able to go to http://localhost:80
which forwards me to the react client. This time, I am not getting the CORS errors, but I do get this message in my inspector, Failed to load resource: The network connection was lost.
My understanding is this:
http://localhost:80
-> http://localhost:3000
, then when a button is pressed, it prompts the new route
http://localhost:3000/login
-> http://localhost:5000/login
I have two server blocks because the web application is running in port 3000 and Nginx server on port 80.