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.
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.
Related
I have an issue with #react-oauth/google npm package.
When I using react app on port 3000 and backend django on port 8000 every thing work's, but after I build react app and using port 8000, I try to log in via google, I get that Erros:
Failed to load resource: the server responded with a status of 400 m=credential_button_library:45 [GSI_LOGGER]: The given origin is not allowed for the given client ID.
I did double check on 'Authorised JavaScript origins' and 'Authorised redirect URIs' (image attached)
but the giving origin are allowed, so whats can be the problem?
I read about similar problems here on the site and also tried CHAT GPT but nothing helped.
This is my configurations:
CORS_ALLOWED_ORIGINS = [
"http://localhost:8000",
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://127.0.0.1:8000"
]
class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
callback_url = ['http://localhost:8000', 'http://localhost:3000', 'http://127.0.0.1:8000', 'http://localhost:8000/accounts/google/login/callback/'] # !
client_class = OAuth2Client
Comment the CORS_ALLOWED_ORIGINS and try with CORS_ALLOW_ALL_ORIGINS = True and if this doesn't work try to remove this http://localhost:3000 and http://127.0.0.1:8000 from both ( CORS_ALLOWED_ORIGINS and GoogleLogin ) and If the above things doesn't work then try to run your react app on PORT - 3000 and on the URL just change your port to 8000 and then try.
Currently I have two container inside AWS EC2 instance.One of them is React app. It uses nginx. This app should send request to golang web service which is inside another container. They are both in same EC2 instance. When i open browser and go to EC2 instance's public IP address i am able to see my React app is working. When i try to send request to golang web service it says that "(failed)net::ERR_CONNECTION_REFUSED". I am able to use curl request inside EC2 and receive response. How can i do the same on React Request.
Here is the my axios post
axios.post('http://localhost:9234/todos', { "name": todo, "completed": false },).then((res) => {
console.log(res)
if (res.data.todo.name === todo) {
setTodos([...todos, todo]);
}
}).catch((err) => { console.log(err) });
Here is my request with curl
curl -d '{"id":9,"name":"baeldung"}' -H 'Content-Type: application/json' http://localhost:9234/todos
Thanks for helps
from your details it seems likely that one or more of the following is true:
you do not have port 9234 forwarded to the container
you do not have port 9234 open in the EC2 instance's security group
Furthermore, localhost as #Jaroslaw points out is from the persective of the browser. That localhost should also be the IP of the ec2 instance or dns that resolves to that IP.
To be clear, the react webapp doesn't run on the ec2 instance. Its static assets such as DOM elements and Javascript get served to the browser and it runs there.
As #Daniel said javascript gets served to the browser and it runs there. So when your browser requesting the address localhost it actually means your computer's localhost. To access the golang server you need to forward the 9234 port from the docker container.
services:
web:
ports:
- "9234:9234"
And then also you need to open the 9234 port in firewall of your ec2 instance then you can access your go server using the public address of your ec2 from your browser.
axios.post('http://{{public_address_of_Ec2}}:9234/todos', { "name": todo, "completed": false },).then((res) => {
console.log(res)
if (res.data.todo.name === todo) {
setTodos([...todos, todo]);
}
}).catch((err) => { console.log(err) });
But this is not recommended to expose the ports to access your server. You may use nginx to listen on your port 80 and then load balance the requests to your go server. Here is a yaml you can add in your docker-compose to use nigx:
nginx:
image: nginx:latest
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web #your web app service name
ports:
- "80:80"
networks:
- "web.network" #your existing network name
And the nginx conf should be:
user nginx;
# can handle 1000 concurrent connections
events {
worker_connections 1000;
}
# forwards http requests
http {
# http server
server {
# listens the requests coming on port 80
listen 80;
access_log off;
# / means all the requests have to be forwarded to api service
location / {
# resolves the IP of api using Docker internal DNS
proxy_pass http://web:9234;
}
}
}
I'm trying to make a Flask server (port 5000) that has a socket.io connection with a React client (port 3000). When I try to execute the server script (shown below), I get an error that says "http://localhost:3000 is not an accepted origin" even though I am using CORS.
server-test.py:
from flask import Flask
from flask_socketio import SocketIO, emit
from flask_cors import CORS, cross_origin
import os
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())
app = Flask(__name__)
app.config['CORS_HEADERS'] = 'Content-Type'
app.config['SECRET_KEY'] = os.environ.get('SECRET')
socketio = SocketIO(app)
CORS(app)
#socketio.on('connect')
#cross_origin()
def handle_connection():
emit('server-client', 'Test message')
#socketio.on('client-server')
#cross_origin()
def handle_client_msg(msg):
print("\n" + str(msg))
if __name__ == '__main__':
app.run(host="localhost", port=os.environ.get('PORT'))
socketio.run(app)
App.jsx:
import { io } from 'socket.io-client';
// ...
useEffect(() => {
const socket = io('http://localhost:5000');
socket.on('server-client', msg => {
alert(msg);
socket.emit('client-server', 'Client: Message received!');
});
}, []);
Error message in WSL terminal:
* Serving Flask app 'server-test' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://localhost:5000/ (Press CTRL+C to quit)
http://localhost:3000 is not an accepted origin. (further occurrences of this error will be logged with level INFO)
127.0.0.1 - - [16/May/2021 00:27:31] "GET /socket.io/?EIO=4&transport=polling&t=NboMpZQ HTTP/1.1" 400 -
127.0.0.1 - - [16/May/2021 00:27:31] "GET /socket.io/?EIO=4&transport=polling&t=NboMpZS HTTP/1.1" 400 -
127.0.0.1 - - [16/May/2021 00:27:31] "GET /socket.io/?EIO=4&transport=polling&t=NboMpZR.0 HTTP/1.1" 400 -
127.0.0.1 - - [16/May/2021 00:27:31] "GET /socket.io/?EIO=4&transport=polling&t=NboMpZR HTTP/1.1" 400 -
The Flask SocketIO documentation says this:
If an incoming HTTP or WebSocket request includes the Origin header, this header must match the scheme and host of the connection URL. In case of a mismatch, a 400 status code response is returned and the connection is rejected.
and this:
If necessary, the cors_allowed_origins option can be used to allow other origins. This argument can be set to a string to set a single allowed origin, or to a list to allow multiple origins. A special value of '*' can be used to instruct the server to allow all origins, but this should be done with care, as this could make the server vulnerable to Cross-Site Request Forgery (CSRF) attacks.
So instead of this:
socketio = SocketIO(app)
you could do this:
socketio = SocketIO(app, cors_allowed_origins="*")
Unrelated to the cors issue, but I also added return statements to the functions. Without returning something I got:
TypeError: The view function did not return a valid response.
Seems to be required if using the #cross_origin() decorators, so you could also remove those and then you don't need the return statements.
The Error
When deploying to Azure Web Apps with Multi-container support, I receive an "Invalid Host Header" message from https://mysite.azurewebsites.com
Local Setup
This runs fine.
I have two Docker containers: client a React app and server an Express app hosting my API. I am using a proxy to host my API on server.
In client's package.json I have defined:
"proxy": "http://localhost:3001"
I use the following docker compose file to build locally.
version: '2.1'
services:
server:
build: ./server
expose:
- ${APP_SERVER_PORT}
environment:
API_HOST: ${API_HOST}
APP_SERVER_PORT: ${APP_SERVER_PORT}
ports:
- ${APP_SERVER_PORT}:${APP_SERVER_PORT}
volumes:
- ./server/src:/app/project-server/src
command: npm start
client:
build: ./client
environment:
- REACT_APP_PORT=${REACT_APP_PORT}
expose:
- ${REACT_APP_PORT}
ports:
- ${REACT_APP_PORT}:${REACT_APP_PORT}
volumes:
- ./client/src:/app/project-client/src
- ./client/public:/app/project-client/public
links:
- server
command: npm start
Everything runs fine.
On Azure
When deploying to Azure I have the following. client and server images have been stored in Azure Container Registry. They appear to load just fine from the logs.
In my App Service > Container Settings I am loading the images from Azure Container Registry (ACR) and I'm using the following configuration (Docker compose) file.
version: '2.1'
services:
client:
image: <clientimage>.azurecr.io/clientimage:v1
build: ./client
expose:
- 3000
ports:
- 3000:3000
command: npm start
server:
image: <serverimage>.azurecr.io/<serverimage>:v1
build: ./server
expose:
- 3001
ports:
- 3001:3001
command: npm start
I have also defined in Application Settings:
WEBSITES_PORT to be 3000.
This results in the error on my site "Invalid Host Header"
Things I've tried
• Serving the app from the static folder in server. This works in that it serves the app, but it messes up my authentication. I need to be able to serve the static portion from client's App.js and have that talk to my Express API for database calls and authentication.
• In my docker-compose file binding the front end to:
ports:
- 3000:80
• A few other port combinations but no luck.
Also, I think this has something to do with the proxy in client's package.json based on this repo
Any help would be greatly appreciated!
Update
It is the proxy setting.
This somewhat solves it. By removing "proxy": "http://localhost:3001" I am able to load the website, but the suggested answer in the problem does not work for me. i.e. I am now unable to access my API.
Never used azure before and I also don't use a proxy (due to its random connection issues), but if your application is basically running express, you can utilize cors. (As a side note, it's more common to run your express server on 5000 than 3001.)
I first set up an env/config.js folder and file like so:
module.exports = {
development: {
database: 'mongodb://localhost/boilerplate-dev-db',
port: 5000,
portal: 'http://localhost:3000',
},
production: {
database: 'mongodb://localhost/boilerplate-prod-db',
port: 5000,
portal: 'http://example.com',
},
staging: {
database: 'mongodb://localhost/boilerplate-staging-db',
port: 5000,
portal: 'http://localhost:3000',
}
};
Then, depending on the environment, I can implement cors where I'm defining express middleware:
const cors = require('cors');
const config = require('./path/to/env/config.js');
const env = process.env.NODE_ENV;
app.use(
cors({
credentials: true,
origin: config[env].portal,
}),
);
Please note the portal and the AJAX requests MUST have matching host names. For example, if my application is hosted on http://example.com, my front-end API requests must be making requests to http://example.com/api/ (not http://localhost:3000/api/ -- click here to see how I implement it for my website), and the portal env must match the host name http://example.com. This set up is flexible and necessary when running multiple environments.
Or if you're using the create-react-app, then simply eject your app and implement a proxy inside the webpack production configuration.
Or migrate your application to my fullstack boilerplate, which implements the cors example above.
So, I ended up having to move off of containers and serve the React app up in more of a typical MERN architecture with the Express server hosting the React app from the static build folder. I set up some routes with PassportJS to handle my authentication.
Not my preferred solution, I would have preferred to use containers, but this works. Hope this points someone out there in the right direction!
I'm building a web application with Angular.js 2, Elasticsearch, and Docker.
I'm trying to access Elasticsearch via its REST API from my Angular app using the linked url "http://search:9200/_cat/health", but I'm getting a GET http://search:9200/_cat/health net::ERR_NAME_NOT_RESOLVED error.
The weird thing is that when I do curl -XGET "http://search:9200/_cat/health" from inside the web app's container everything is fine and I'm getting a correct response.
Here is my respective docker-compose.yml file
version: '2'
services:
search:
image: elasticsearch:latest
command: elasticsearch -Des.network.host=0.0.0.0
ports:
- "9200:9200"
- "9300:9300"
web:
build: .
command: npm start
volumes:
- .:/visfx
ports:
- "3000:3000"
links:
- search
also here's the call to the elasticsearch service
#Injectable()
export class ElasticsearchService {
private _esUrl = 'http://search:9200';
constructor(private http: Http) { }
getHealthString() {
let healthUrl = '/_cat/health';
return this.http.get(this._esUrl + healthUrl)
.map(res => <string>res.text())
.do(data => console.log(data))
.catch(this.handleError);
}
...
}
If that javascript is running on the client side you'll have to use a real DNS name which routes to the docker host as docker's names are only resolved using docker's internal resolver.