CORS, Laravel Valet, and Socket.io - node.js

This feels like it's all way harder than it should be. I am working on an application for a client and am at the end of my rope trying to get this thing running locally. So, I am using Laravel(served through Valet on my mac) and am writing a Socket.io server for handling different dashboard events. Socket.io runs in an https configuration as it will in production and I've got it running on port 3001. I've got both serving content over https, resolved all configuration issues there. However, now I can not stop getting CORS errors whenever I try to connect.
Here is the error I am getting:
Access to XMLHttpRequest at 'https://ags.test:3001/socket.io/?EIO=3&transport=polling&t=1605106550351-47' from origin 'https://ags.test' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I've done a fair bit of testing on this and found that if I do a GET request to the root (/) of the express server, it works fine and without CORS errors. However, the polling at the url posted in the error above is still providing the same CORS error.
If it isn't abundantly obvious by now, I'm fairly new to the node ecosystem. So what I'm caught up on is what's happening with CORS when it goes from calling 'https://ags.test:3001' to 'https://ags.test:3001/socket.io/*'. Or is it possible that express isn't handling the CORS for socket.io at all?
Something I've seen multiple times in other stackoverflow answers is something along the lines of (from the server) io.origins('*:*') to set the origin policy. However, I can't seem to find documentation on this and calling it (or any of the variations I've seen of it)
So you know what I've already tried, here the server configuration for my Socket.io express server:
app.use(cors());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
// authorized headers for preflight requests
// https://developer.mozilla.org/en-US/docs/Glossary/preflight_request
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
app.options('*', (req, res) => {
// allowed XHR methods
res.header('Access-Control-Allow-Methods', 'GET, PATCH, PUT, POST, DELETE, OPTIONS');
res.send();
});
});
var options = {
key: fs.readFileSync('./ags.test.key'),
cert: fs.readFileSync('./ags.test.crt'),
origins: '*:*',
cors: true,
};
const server = https.createServer(options, app);
I've also made adjustments to my laravel site config in valet as seen here https://gist.github.com/poul-kg/b669a76fc27afcc31012aa0b0e34f738 but applied directly to my site-specific config at ~/.config/valet/Nginx/ags.test and restarted.
As you can see, I've tried quite a few different things and am at the "throw everything at the wall to see what sticks" stage of trying to get these servers communicating. Any recommendations?

Okay, took me way too long to figure out what I was missing. Old stackoverflow answers show configuration methods that are no longer supported in Socket.io v3.
Here's what I was missing to configure this for the most recent version of Socket.io as of the date of posting.
const server = https.createServer(options, app);
const io = require('socket.io')(server, {
cors: {
origin: 'https://ags.test'
}
});

Related

Cloud9 Angular & NodeJS unable to access API running on different port of same machine

I am using an AWS cloud9 IDE for my project.
I have Angular running on port 8080. I have my NodeJS (+Express) server running on port 8081.
I have CORS headers setup in my Node app.js as following:
const app = express();
app.use(express.json());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
I use Angular services with http client module to make requests to api endpoints (https://localhost:8081/blogposts/*).
In one of my Angular components, I make a request to fetch the data inside ngOnInit():
ngOnInit() {
this.blogService.getBlogPosts()
.subscribe((blogposts: BlogPost[]) => {
this.blogposts = blogposts;
});
}
but I get the following error in the browser console:
GET https://localhost:8081/blogposts net::ERR_CONNECTION_REFUSED
I thought this was a problem with setting up CORS headers, but as shown above I have added all required headers.
I read that a proxy can be used as an alternative solution to CORS, but this is not suitable for production environments.
I can confirm my API works fine using curl:
ubuntu:~/environment (develop) $ curl http://localhost:8081/blogposts
[{"title":"Calvin and Hobbes","authorName":"Bill Waterson", ...
I am also concerned whether the Cloud9 environment is preventing me from making these API calls, but as far as I know, both ports 8080 and 8081 are safe to use.
Can I get more help in finding what's wrong with my api?
Cloud9 does not allow http/https connections to any port other than 8080. The team is aware of the friction this causes for developers and they're looking at way to make it better.

post request to Express.js api from another computer

I have a server running on localhost:3000, and I set my app.js to use the angular router when I try to access localhost:3000 in my browser
(example :app.use('/', express.static(path.join(__dirname, '/dist/Client')));)
When I make a post request to my api I do:
const headers = new Headers({
'Content-Type': 'application/json'
});
const options = new RequestOptions({
headers: headers
});
this.http.post('http://localhost:3000/api/someAction',{body},options)
.toPromise()
.then(//function)
Untill now everything is correct, but how can I make my server accessible from another computer. For example if another computer on the same network knows the private IP address of the server I want him to be able to access my app when he navigate to for example 192.168.1.10:3000. Right now I can access but all my http requests fail and I have the following error
Access to XMLHttpRequest at 'http://localhost:3000/api/someFunction'
from origin 'http://192.168.1.10:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: It does not
have HTTP ok status.
In app.js I have the following:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
next();
});
This is a CORS error message you're getting.
You can use the Express CORS middleware to allow calls from other origins to your server. Make sure to choose the right options to allow calls from your other PC.
https://expressjs.com/en/resources/middleware/cors.html
And also make sure to place the CORS middleware at the top of your application definition.
const app = express();
app.use(cors({ origin: "*" })); // Do this first
//... the rest of your routes and handlers and middlewares
From your local machine where things seem to work correctly, open your Chrome Dev Tools, and make sure OPTION calls are being made and fulfilled successfully, and that proper headers are being returned.
Lastly, from a security perspective, remove or restrict the CORS options as much as possible for your production environment. Only use flexible CORS policy during development and testing.

CORS error when sending get reuqest from angular 6 app to elasticsearch, even when its allowed

I have an angular 6 app that sends http requests to elasticsearch DB.
The angular app, nodejs server (that serves the angular app) and elasticsearch are hosted on the same machine, and the browser is on a different machine. The client points to the nodejs IP address using the browser.
The requests are being sent with httpClient from angular (not using any nodejs or expressjs software), and without allowing CORS request with third party package (Chrome and Firefox) or by changing the browser settings (IE 11) the browser doesn't allow the request to go out, and gives me CORS errors.
I want things to work without any adjustments, so I added the following configuration to elasticsearch.yml:
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE
http.cors.allow-headers: "X-Requested-With,X-Auth-Token,Content-Type, Content-Length"
And just in case I added the following lines to my nodejs server:
app.use('*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
The request is sent via service in the angular app, and its simple:
getIndecies(){
return this.http.get(`${this.full_elastic_url}/_cat/indices?format=json`)
}
When I don't enable CORS with chrome/firefox/IE11 I get this message when the request is sent:
Firefox:
Chrome:
IE:
I don't understand what am I doing wrong. I'm enabling CORS in every place that online answers recommended in order to solve this issue, but still - the browser blocks my reuqest.
Why can't I bypass this error?
How can applications, such as kibana, do this without any problem, but my angular app canno't?
Does anyone here has a magic solution for me?
Thanks!
Some networks, browsers also block cors by default, in my experience the best way around this is to proxy your request through your node server.

Angular on localhost gets 404 when consuming GET api from different localhost PORT - CORS issue?

Ever since I switched to the database URL from the in-memory-web-api URL, the Angular app's JavaScript console gives me a 404 error
JavaScript console output 404 error
I've got my angular app running on lite-server using localhost:3035/
I've got a mongodb/nodejs/express database running on localhost:3039/
My angular app was "GET"-ing just fine using the in-memory-web-api url 'api/loggerData'
Any thoughts? Is it CORS? Something else? Do I need to configure the lite-server on the angular side as well?
Here's my angular Code:
private loggerUrl = 'localhost:3039/read/getall/';
getLoggerData(): Promise <Dataset[]> {
return this.http.get(this.loggerUrl)
.toPromise()
.then(response => response.json().data as Dataset[])
.catch(this.handleError);
}
Also, I've tried implementing some CORS solutions on the database side that haven't affected anything -
here's some of the database code I modified:
var app = express();
app.use(function (req, res, next) {
//Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3035');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
No collection errors usually means, that you have left hanging some the equivalent of:
InMemoryWebApiModule.forRoot(InMemoryDataService)
in your ngModule, which interferes when you try and make "real" http-requests. So remove that from your ngModule and you should be good to go! :)
After this you might still run into problems with CORS, but this should fix the current error you get :)
And as mentioned by echonax, you should use the complete url, with http included:
private loggerUrl = 'http://localhost:3039/read/getall/'

Allow CORS REST request to a Express/Node.js application on Heroku

I've written a REST API on the express framework for node.js that works for requests from the js console in Chrome, and URL bar, etc. I'm now trying to get it working for requests from another app, on a different domain (CORS).
The first request, made automatically by the javascript front end, is to /api/search?uri=, and appears to be failing on the "preflight" OPTIONS request.
In my express app, I am adding CORS headers, using:
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
// intercept OPTIONS method
if ('OPTIONS' == req.method) {
res.send(200);
}
else {
next();
}
};
and:
app.configure(function () {
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(allowCrossDomain);
app.use(express.static(path.join(application_root, "public")));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
From the Chrome console I get these headers:
Request URL:http://furious-night-5419.herokuapp.com/api/search?uri=http%3A%2F%2Flocalhost%3A5000%2Fcollections%2F1%2Fdocuments%2F1
Request Method:OPTIONS
Status Code:200 OK
Request Headers
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:origin, x-annotator-auth-token, accept
Access-Control-Request-Method:GET
Connection:keep-alive
Host:furious-night-5419.herokuapp.com
Origin:http://localhost:5000
Referer:http://localhost:5000/collections/1/documents/1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5
Query String Parameters
uri:http://localhost:5000/collections/1/documents/1
Response Headers
Allow:GET
Connection:keep-alive
Content-Length:3
Content-Type:text/html; charset=utf-8
X-Powered-By:Express
Does this look like a lack of proper headers being sent by the API application?
Thanks.
I've cheked your code on a clean ExpressJS app and it works just fine.
Try move your app.use(allowCrossDomain) to the top of configure function.
I'm adding this as an answer only because the original post was put in as a comment and as such it got overlooked by yours truly the first time I went over this page.
As #ConnorLeech points out in his comment to the accepted answer above, there is a very handy npm package called, not surprisingly, cors. It's use is as simple as var cors = require('cors'); app.use(cors()); (again, cribbed from Mr. Leech's answer) and can also be applied in a stricter, more configurable fashion as outlined in their docs.
It may also be worth pointing out that the original comment I refer to above was made in 2014. It's 2019 now and looking at the npm package's github page the repo was updated as recently as nine days ago.
for supporting cookies withCredentials you need this line
xhr.withCredentials = true;
mdn docs xhr.withCredentials
In the Express Server add this block before all the other
`app.all('*', function(req, res, next) {
var origin = req.get('origin');
res.header('Access-Control-Allow-Origin', origin);
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});`
It could not be the case for most people browsing this question but I had this exact same problem and the solution was not related to CORS.
Turns out that JSON Web Token secret string was not defined in the environment variables, so the token could not be signed.
This caused to any POST request that relies on checking or signing a token to get a timeout and return a 503 error, telling the browser that there's something wrong in CORS, which it's not.
Adding the environment variable in Heroku solved the issue.
I hope this helps someone.
Follow the below steps:
npm install cors --save
Inside your root js file:
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
The Cors error could be a cover up for your actual error. Look in the logs of your heroku build to see your potential real error. Mine was this.
Heroku Postgres: "psql: FATAL: no pg_hba.conf entry for host"

Resources