Express proxy + axios + cors: still cors issue - node.js

I've created the following proxy with express:
import express from "express";
import cors from "cors";
import proxy from "express-http-proxy";
const app = express();
app.use(
cors({
origin: "http://localhost:8081",
credentials: true,
})
);
app.use("/", proxy("http://my-website:8810"));
app.listen(3000, () => {
console.log("server listening on port 3000");
});
From the frontend I'm using axios:
axios.defaults.withCredentials = true;
const res = await axios.get("http://localhost:3000", {
auth: {
username: "xxxxx",
password: "xxxxx",
},
headers: {
"Content-Type": "application/json",
},
});
But I still have the following cors issue:
Access to XMLHttpRequest at 'http://my-website:8810' (redirected from
'http://localhost:3000/') from origin 'http://localhost:8081' has been
blocked by CORS policy: Response to preflight request doesn't pass
access control check: No 'Access-Control-Allow-Origin' header is
present on the requested resource.
And after many hours facing this issue... here I am.
Could anyone explain to me what I'm doing wrong here?

Finally it works by using "request", something like:
app.use(
cors({
origin: "http://localhost:8081",
credentials: true,
})
);
router.get("/", (req, res) => {
request(
"http://my-website:8810",
{
auth: {
username: "node_proxy",
password: "password",
},
headers: { Accept: "application/json" },
},
(error, response, body) => {
res.send(body);
}
);
});

Related

Cannot solve CORS Blocking Issue between React and Node

I've been working on website using react.js in the front and node.js in the back and whenever I send a patch request, I am getting Blocked by CORS like the following:
"Access to XMLHttpRequest at 'http://localhost:4000/admin/appointment/63efc1cc0eecac628d653b65' from origin 'http://localhost:3000' has been blocked by CORS policy: Method PATCH is not allowed by Access-Control-Allow-Methods in preflight response.
Appointments.js:50 AxiosError {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}
xhr.js:247
PATCH http://localhost:4000/admin/appointment/63efc1cc0eecac628d653b65 net::ERR_FAILED"
My code for front end using axios to handle request is:
axiosInstance
.patch(
`/admin/appointment/${id}`,
{ status: "Finished" },
{
headers: {
Authorization: token,
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
)
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
and My code from node js is:
router.patch('/appointment/:id', auth, async (req, res) => {
try {
const id = req.params.id;
const AppointmentToUpdate = req.body;
const updatedAppointment = await appointmentModel.update({ _id: id }, number, { runValidators: true });
res.json(updatedAppointment);
}catch (err) {
res.status(500).send(err.message);
}
})
and I have already installed cors and required it in my backend:
const cors = require("cors");
const app = express();
mongoose.connect(`mongodb://localhost:27017/frontdesk`);
app.use(
cors({
origin: "*",
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
})
);
I've should mention that I used other methods like login "POST" request and "GET" request and all are working fine
Replace cors with the following code. It should solve your issue.
const options = {
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}
app.use(cors(options));

No 'Access-Control-Allow-Origin' header is present on the requested resource. React App Heroku API

I'm trying to integrate passport-google-oauth20 in my MERN application and although everything works fine in development on my local host but in production, it keeps throwing this error;
No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I have searched and gone through multiple stack overflow posts and tried some of the answers and suggestions but nothing seems to work. This my index.js and the codes commented out are some of the solutions and CORS settings I've tried. I also tried putting the URL directly instead of an environment variable, but that didn't work either.
const express = require("express");
const cookieSession = require("cookie-session");
const passport = require("passport");
const cors = require("cors");
const helmet = require("helmet");
const connectDB = require("./config/db");
const authRoute = require("./routes/auth.route");
const userRoute = require("./routes/user.route");
const adminRoute = require("./routes/admin.route");
const transactionRoute = require("./routes/transaction.route");
//Passport setup
require("./passport");
const path = require("path");
// require("dotenv").config();
const app = express();
require("dotenv").config({
path: "./config/config.env",
});
//Connect to Database
connectDB();
//Use bodyParser
app.use(express.json());
app.use(
helmet({
contentSecurityPolicy: false,
frameguard: true,
})
);
app.use(
cookieSession({
name: "session",
keys: ["ccurves"],
maxAge: 24 * 60 * 60 * 100,
})
);
app.use(passport.initialize());
app.use(passport.session());
//Config for only development
// if (process.env.NODE_ENV === "development") {
// app.use(
// cors({
// origin: process.env.CLIENT_URL,
// methods: "GET,POST,PUT,DELETE",
// credentials: true,
// })
// );
// }
// const corsOptions = {
// origin: [`${process.env.CLIENT_URL}`],
// methods: "GET,HEAD,PUT,OPTIONS,POST,DELETE",
// allowedHeaders: [
// "Access-Control-Allow-Headers",
// "Origin",
// "X-Requested-With",
// "Content-Type",
// "Accept",
// "Authorization",
// "token",
// "Access-Control-Request-Method",
// "Access-Control-Request-Headers",
// "Access-Control-Allow-Credentials",
// ],
// credentials: true,
// preflightContinue: false,
// optionsSuccessStatus: 204,
// };
// app.use(cors(corsOptions));
app.use(
cors({
origin: process.env.CLIENT_URL,
methods: "GET,POST,PUT,DELETE",
credentials: true,
})
);
// app.use((req, res, next) => {
// res.header("Access-Control-Allow-Origin", `${process.env.CLIENT_URL}`);
// res.header("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
// res.header("Access-Control-Allow-Credentials", true);
// res.header(
// "Access-Control-Allow-Headers",
// "Access-Control-Allow-Headers, Origin, X-Requested-With, Content-Type, Accept, Authorization, token, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Credentials, Access-Control-Allow-Origin"
// );
// next();
// });
app.use("/api/auth", authRoute);
app.use("/api/user", userRoute);
app.use("/api/admin", adminRoute);
app.use("/api/transaction", transactionRoute);
const port = process.env.PORT;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
And in my react frontend I'm fetching the request from the API like this:
const getUser = () => {
fetch(`${process.env.REACT_APP_API_URL}/auth/login/success`, {
method: "GET",
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
},
})
.then((response) => {
if (response.status === 200) return response.json();
throw new Error("authentication has been failed!");
})
.then((resObject) => {
authenticate(resObject, () => {
isAuth && navigate("/");
});
})
.catch((err) => {
console.log(err);
});
};
I also tried using axios to send the request,
axios
.get(`${process.env.REACT_APP_API_URL}/auth/login/success`, {
withCredentials: true,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
})
.then((res) => {
console.log(res);
authenticate(res.data, () => {
isAuth && navigate("/");
});
})
.catch((err) => {
console.log(err);
// setError(err.response.data.errors);
});
And it works fine and perfectly on my local host, all other routes are working and even the other authentication method works fine. Why is this particular route been blocked by CORS? When I open the API URL ${process.env.REACT_APP_API_URL}/auth/login/success directly in the browser I can see the json data is been sent fine. What is going on? Please what am I doing wrong?
#Kaneki21 Solution actually worked for me but he has deleted his answer so I'm reposting it. All credits go to him, so this cors configuration solved the error:
const corsOptions = {
origin: [`${process.env.CLIENT_URL}`],
methods: "GET,HEAD,PUT,OPTIONS,POST,DELETE",
allowedHeaders: [
"Access-Control-Allow-Headers",
"Origin",
"X-Requested-With",
"Content-Type",
"Accept",
"Authorization",
"token",
"Access-Control-Request-Method",
"Access-Control-Request-Headers",
"Access-Control-Allow-Credentials",
],
credentials: true,
preflightContinue: false,
optionsSuccessStatus: 204,
};
app.use(cors(corsOptions));
But I ran into another issue, Although the request was sent now, I noticed cookies weren't included in the header even after I added withCredentials: true. Inspecting the request in the chrome network tab I could see the cookies was been filtered out. I solved that by switching from cookie-session to express-session and using this config:
app.set("trust proxy", 1); // trust first proxy
app.use(
session({
secret: process.env.JWT_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
secure: true,
sameSite: "none",
},
})
);
Notice the sameSite: "none" in the cookie configuration, that enables cross-sites request and it requires secure to be true. Previously cookie-session wasn't setting the sameSite attribute even when I included it in the cookie config and browsers set Lax as a default so hence the switch to express-session. You can check out these sites to understand more site1 site2
I hope this helps someone out cause I know this gave me quite a bit of a headache

Can't make a successful Authorization request from Axios request to third-party API

I have been dealing with this issue where I am attempting to make a get request to a third-party API using Axios in my Node.js server. The endpoint requires a username and password which I am passing along as follows:
export const getStream = async(req, res) => {
let conn = createConnection(config);
let query = `SELECT * FROM cameras WHERE id = ${req.params.id}`
conn.connect();
conn.query(query, async (error, rows, _) => {
const camera = rows[0];
const {ip, user, pass} = camera;
if (error) {
return res.json({ "status": "failure", "error": error });
}
const tok = `${user}:${pass}`;
const userPass = Buffer.from(tok)
const base64data = userPass.toString('base64');
const basic = `Basic ${base64data}`;
const result = await axios({
method: 'get',
url: `<API URL>`,
headers: {
'Authorization': basic,
'Content-Type': 'multipart/x-mixed-replace; boundary=--myboundary'
},
auth: {username: user, password: pass}
})
res.json(result)
});
conn.end();
}
I am then calling this endpoint in my React front-end as such:
const getStream = async () => {
try {
const result = await publicRequest.get(`camera/getStream/${id}`)
console.log(result)
} catch (error) {
console.error(error)
}
}
Each time I make this request, my node server crashes and I get a 401 unauthorized error in my console. It appears that my Authorization header is not getting passed to the server even though everything else gets passed along as so.
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'multipart/x-mixed-replace; boundary=--myboundary',
'User-Agent': 'axios/0.26.1'
},
method: 'get',
url: '<url>',
auth: { username: '<username>', password: '<password>' },
data: undefined
For extra information, this is how my node server is setup
import express, { urlencoded, json } from 'express';
import userRoute from './routes/userRoute.js';
import cameraRoute from './routes/cameraRoute.js';
import cors from 'cors';
const app = express();
app.use(cors());
app.options('*', cors());
app.use(json())
app.use(urlencoded({ extended: true }));
app.use(express.static('public'));
app.use('/api/user', userRoute);
app.use('/api/camera', cameraRoute);
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
I have been working on this issue for several days and each time I try something new, I always get a 401 error, and the server crashes.
Any suggestions would be greatly appreciated.

express-sesison not saving on routes

I have a ExpressJS server and I would like to implement in Sessions however it doesn't seem to save the sessions.
The flow is to:
POST to /api/login
GET from /api/viewSession
However, the session['stuff'] returns undefined.
I suspected it might be because i'm trying to GET the session from a different URL. So I added a GET method to /api/login but it returned undefined too.
Could somebody point me in the right direction please? I'm a little lost after a few hours of Googling to no avail.
Here below is my code for index.js and my route api.js.
Also, I'm using
NPM - Version 8.3.1
Node - Version v16.14.0
npm i cors - Version 2.8.5
npm i express-session - Version 1.17.2
npm i express - Version 4.17.3
index.js
const express = require('express')
const formidable = require('express-formidable');
const cors = require('cors');
const session = require('express-session');
const api = require('./routes/api');
const app = express()
const port = 3000;
app.use(express.json());
app.use(formidable());
app.use(
cors({
origin: true,
optionsSuccessStatus: 200,
credentials: true,
})
);
app.options(
'*',
cors({
origin: true,
optionsSuccessStatus: 200,
credentials: true,
})
);
app.use(
session({
saveUninitialized: false,
secret: "anyrandomstring",
cookie: { maxAge: 36000000 }
})
);
//Routes
app.use('/api', api);
//Navigation
app.get('/', function (req, res) {
res.render('index');
res.send("Hi!");
})
//App Start
app.listen(port, () => {
console.log(`App Listening on port ${port}`);
})
api.js
"use strict";
const express = require("express");
let router = express.Router();
router
.route('/dump')
.post(async (req, res) => {
console.log(req.fields);
res.send({status: "ok"})
})
router
.route('/login')
.post(async (req, res) => {
//Saving in Session
req.session['stuff'] = "123456";
res.send("Ok");
})
router
.route('/viewSession')
.get((req, res) => {
console.log(req.session['stuff']);
res.send("ok");
})
module.exports = router;
Also, this is the way I send the POST/GET request
$.ajax({
url: "http://localhost:3000" + '/api/login',
type: "POST",
crossDomain: true,
dataType: "json",
data: {},
success: function (response) {
console.log(response);
}
})
If you're making cross-domain requests with XMLHttpRequest and you want to allow cookies to be set by the server handling the request, you need to set withCredentials : true.
Using jQuery:
$.ajax({
url: "http://localhost:3000" + '/api/login',
type: "POST",
crossDomain: true,
xhrFields: { withCredentials: true },
dataType: "json",
data: {},
success: function (response) {
console.log(response);
}
})

Get Express app's response header in Angular 7 client

I've been trying to access the http response header from a NodeJS app in the Angular 7 client code, but I'm unable to retrieve it. I've tried every solution I found on Stack Overflow. I still keep getting an empty header back in the browser. But it works fine on Postman.
I'm attaching the browser console log below:
headers: HttpHeaders
lazyInit: ƒ ()
lazyUpdate: null
normalizedNames: Map(0)
size: 0
__proto__: Map
[[Entries]]: Array(0)
length: 0
Server-side code:
app.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const route = require('./routes/route');
const { mongoose } = require('./db/mongoose');
const app = express();
app.use(bodyParser.json());
app.use(cors({
'allowedHeaders': ['sessionId', 'Content-Type', 'x-auth', 'Authorization'],
'exposedHeaders': ['sessionId', 'x-auth', 'Authorization'],
'origin': '*',
'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
'preflightContinue': false
}));
// app.options('*', cors());
app.use(route);
app.listen(3000);
controller.js
exports.signupUser = (req, res, next) => {
let user = new UserModel({
email: req.body.email,
password: req.body.password
});
user.save().then((user) => {
return user.generateAuthToken();
}).then((token) => {
res.header({
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization',
'Access-Control-Expose-Headers': 'Authorization',
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
'Access-Control-Allow-Credentials': 'true',
'x-auth': token
}).send(user);
}).catch((err) => {
res.status(400).send(err);
});
};
angular 7 code:
auth.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class AuthService {
token: string;
constructor(private http: HttpClient) { }
signup(email: string, password: string) {
this.http
.post('http://localhost:3000/signup', { email, password }, {observe: 'response'})
.subscribe((response) => {
console.log(response);
});
}
}
How are you attempting to access your header on the client side?
The response.headers from your example is an instance of HttpHeaders. The headers arent visible as properties on the headers object itself but you can access them like this:
this.http
.post('http://localhost:3000/signup', { email, password }, {observe: 'response'})
.subscribe((response) => {
let contentTypeHeader = response.headers.get("content-type");
console.log(contentTypeHeader);
});

Resources