1

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:

https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=None&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Flogin%2Fcallback&scope=openid+email+profile

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.

jeff
  • 490
  • 1
  • 6
  • 21
  • have a look at this thread https://stackoverflow.com/questions/26980713/solve-cross-origin-resource-sharing-with-flask it may help you – cizario Dec 30 '20 at 09:57
  • @cizario I’ve tried those.. :( have you run into this problem before? – jeff Dec 30 '20 at 15:58
  • Have you tried initialising your app with the cors library and not just your blueprint, and is there a specific reason youre just initialising the blueprint? – Craicerjack Dec 31 '20 at 03:44
  • @Craicerjack Ya, I did that.. I still got the same issue. :( – jeff Dec 31 '20 at 04:18

1 Answers1

0

Instead of CORS(login_page) try CORS(login_page, resources=r'/login')

Edit: To explain why this might work.

The error you're getting indicates that the /login route doesn't have CORS permitted. You're instantiating Flask-CORS correctly so I suspect if you tried delineating the specific route you want CORS to apply to you would fix the issue.

The docs make it seem like CORS(login_page) should automatically open up all routes to CORS but because that's not working trying the above answer might provide better results.

steve
  • 2,488
  • 5
  • 26
  • 39