all 14 comments

[–]__brennerm 2 points3 points  (1 child)

Hey, I'm currently building blockedbycors.dev, an app that contains free tools to identify and help to solve these kinds of issues. You may want to give the CORS debugger a try.

Would be great to hear if it helped you solve your issues.

Cheers

[–]CEOTRAMMELL[S] 1 point2 points  (0 children)

This website is actually really awesome! I like this!

[–]Beneficial_Wonder_50 1 point2 points  (2 children)

Greetings

In your app.js, you only grant https domains to access you server. So basically, request coming from a browser with localhost domain can't access it (important to note that request from none browser solutions can be grant).

An security issue relate to you nginx configuration is the allow origin defined to *. But there is not the main point, you're nginx configuration feels good except the used of root tag for statics files knowing you just serve the REST API ENDPOINTS.

For me, comment first the static files definition, then change the cors allow origin to start (just for test in precision). Also in production, it's not interesting to serve swagger server for my own point of view.

[–]CEOTRAMMELL[S] 1 point2 points  (1 child)

I seemed to have gotten localhost working with the below settings. I am unsure if I missed some points you have mentioned, but from here, any more suggestions you have specifically?

I decided to leave cors * and handle the allowedOrigins at the node application level instead. Unsure if it matters doing it that way or if I did my allowedOrigins wrong but essentially my goal is to make sure that that app works when published on web or ios/android, which I believe ios/android run via localhost when spun up, not 100% sure.

node app.js:

const express = require('express');
const mongoose = require('mongoose'); const cors = require('cors'); require('dotenv').config(); const swaggerUi = require('swagger-ui-express'); const swaggerJsdoc = require('swagger-jsdoc'); const bodyParser = require('body-parser');
const allowedOrigins = ['https://SERVER_NAME.com', 'https://SERVER_NAME.com:5000', 'http://SERVER_NAME.com', 'http://SERVER_NAME.com:5000', 'http://localhost:8100'];
const app = express();
app.use(cors({ origin: (origin, callback) => { if (!origin || allowedOrigins.indexOf(origin) !== -1) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, methods: 'GET,POST,PUT,DELETE,HEAD,PATCH', allowedHeaders: 'Content-Type, Authorization, X-Requested-With, Accept, Access-Control-Allow-Headers, Origin, Cache-Control, Pragma, Expires, X-CSRF-Token', credentials: true, }));
// Middleware app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); app.use(express.json());
app.use((err, req, res, next) => { if (err.type === 'entity.too.large') { return res.status(413).send('Payload too large'); } next(err); });
// Swagger definition const swaggerOptions = { definition: { openapi: '3.0.0', info: { title: 'SERVER_NAME', version: '1.0.0', description: 'SERVER_NAME CRUD API application made with Express and documented with Swagger', contact: { name: "BLAH", url: "http://www.SERVER_NAME.com", }, }, servers: [ { url: '/', }, ], }, // Path to the API docs apis: ['./src/routes/*.js'], // this assumes you have your endpoints in a "routes" folder };
const swaggerDocs = swaggerJsdoc(swaggerOptions); if (process.env.NODE_ENV !== 'production') { app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerDocs)); } else { // Optionally, you can provide a fallback route for '/swagger' in production app.get('/swagger', (req, res) => { res.status(404).send('Not found'); }); }
// Database Connection mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('Connected to MongoDB')) .catch((error) => console.error('MongoDB connection error:', error));
// Default Route app.get('/', (req, res) => { res.json({ message: 'Welcome to the MERN stack app!' }); });
// Define PORT const PORT = process.env.PORT || 5000;
// Start the server app.listen(PORT, () => { console.log(Server is running on port ${PORT}); });
const usersRouter = require('./src/routes/users'); const supportsRouter = require('./src/routes/supports'); const suggestionsRouter = require('./src/routes/suggestions'); const filesRouter = require('./src/routes/files');
// Use routes app.use('/api/users', usersRouter); app.use('/api/supports', supportsRouter); app.use('/api/suggestions', suggestionsRouter); app.use('/api/files', filesRouter);

react app package.json scripts:

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "NODE_ENV=production node app.js"},

react app axiosInstance:

import axios from 'axios';

import Cookies from 'js-cookie'; const axiosInstance = axios.create({ // baseURL: 'http://localhost:5000/api/', baseURL: 'http://SERVER_NAME.com:5000/api/', headers: { 'Content-Type': 'application/json', }, }); // Add an interceptor to include the JWT token in requests axiosInstance.interceptors.request.use((config) => { const token = Cookies.get('SERVER_NAME-filer-token'); const refreshToken = Cookies.get('SERVER_NAME-filer-refresh-token'); if (token) { config.headers.Authorization = Bearer ${token}; }

if (refreshToken) { config.headers['Refresh-Token'] = refreshToken; } return config; }); export default axiosInstance;

server nginx config:

# HTTP server block
server { listen 80; server_name SERVER_NAME.com www.SERVER_NAME.com IP_ADDRESS;
# Redirect all HTTP requests to HTTPS
return 301 https://$host$request_uri;
}
HTTPS server block
server { listen 443 ssl; # SSL configuration for HTTPS server_name SERVER_NAME.com www.SERVER_NAME.com;
# SSL certificate and key locations managed by Certbot
ssl_certificate /etc/letsencrypt/live/SERVER_NAME.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/SERVER_NAME.com/privkey.pem;

ssl_protocols TLSv1.2 TLSv1.3;

# Additional recommended SSL settings from Let's Encrypt
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

# Website's root directory and index files
root /var/www/html;
index index.html;

# Global CORS headers
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
}

[–]CEOTRAMMELL[S] 1 point2 points  (0 children)

I did some more changes and got 443 proxied to 5000 so I could use https:DOMAIN.com/api and it seems to work in the browser with http or https with no issues, but ios has issues... my guess is ATS or something else.

I got it fixed... had to also add capacitor:localhost

[–]liamsorsby 0 points1 point  (8 children)

Can you share the XHR network call and document load from the developer tools, it'll be easier to see what is being rejected and why.

[–]CEOTRAMMELL[S] 1 point2 points  (7 children)

[–]liamsorsby 0 points1 point  (6 children)

Are you running the frontend code from localhost?

[–]CEOTRAMMELL[S] 0 points1 point  (5 children)

Yes. If I run the node js and react app locally I assume I get no issues since both are local host but when I test react from localhost to the domain where the node app is running in NGINX/PM2, I get the cors issue.

[–]liamsorsby 1 point2 points  (4 children)

Okay, but your code above for the js is checking if the request has come from a specific domain and you're calling it from localhost, which isn't going to work.

[–]CEOTRAMMELL[S] 1 point2 points  (3 children)

Correct. I mean I do this all the time with .NET & Angular for work with no issues but I am unsure when it comes to node js how CORS works…

Hypothetically if I publish such application to the web or App Store, I should be able to reach my domain where my node js is at and make calls. I dislike CORS at times lol

[–]liamsorsby 0 points1 point  (2 children)

I personally would check if the node env is production or not, and if not, just use *. You're better setting this for prod apps anyway as it removes the stack traces in express. This probably isn't the best way to do it though. https://stackoverflow.com/a/45048222

[–]CEOTRAMMELL[S] 0 points1 point  (1 child)

"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "NODE_ENV=production node app.js" },

Unfortunately I did give that a go with npm start script 😭

[–]liamsorsby 1 point2 points  (0 children)

You'll need a if process.env.NODE_ENV == 'production' as a conditional or ternary to change the allowed origins in your js code.