I have created 2 herokuapps, both sharing the herokuapp.com as the main domain, however when I want to set cookie from one to another it does not allow me, I also tested this with ngrok and the result is the same.
It returns "This Set-Cookie was blocked because its Domain attribute was invalid with regards to the current host url"
here is my backend code:
const express = require("express");
const app = express();
const cors = require("cors");
const cookieParser = require("cookie-parser");
app.use(cookieParser());
app.use(
cors({
origin: [process.env.FRONT_URL], // {my-frontend}.herokuapp.com
methods: ["GET", "PUT", "POST"],
allowedHeaders: ["Content-Type", "Authorization", "x-csrf-token"],
credentials: true,
maxAge: 600,
exposedHeaders: ["*", "Authorization"],
})
);
app.get(
"/protect-me",
function (req, res, next) {
if (req.cookies["access_token"] == "accesstoken") next();
else return res.status(401).send("Unauthorized");
},
function (req, res, next) {
res.json({ msg: "user get" });
}
);
app.post("/login", function (req, res, next) {
res.cookie("access_token", "accesstoken", {
expires: new Date(Date.now() + 3600 * 1000 * 24 * 180 * 1), //second min hour days year
secure: true, // set to true if your using https or samesite is none
httpOnly: true, // backend only
sameSite: "none", // set to none for cross-request
domain: process.env.COOKIE_DOMAIN, // tested both with .herokuapp.com & herokuapp.com
path: "/"
});
res.json({ msg: "Login Successfully" });
});
app.listen(process.env.PORT, function () {
console.log("CORS-enabled web server listening on port 80");
});
then on frontend I first try to login with codes below from {my-frontend}.herokuapp.com:
fetch('https://{my-backend}.herokuapp.com/login', {
method: 'POST', credentials: 'include'
});
and then making the second request from {my-frontend}.herokuapp.com:
fetch('https://{my-backend}.herokuapp.com/protect-me', {
credentials: 'include'
});
Thank you in advance for your attention :)
Additional Note
Just as a side note, this works perfectly fine when we have a root domain and subdomain communication, what I mean is, if for example your auth server is on yourdomain.com, then your dashboard is on dashboard.yourdomain.com, then you can easily set a .yourdomain.com cookie and all works fine
but it is not possible for me to make a cookie with auth.yourdomain.com for .yourdomain.com so that the dashboard.yourdomain.com can access it as well
I think the cookie domain should be same as that of frontend url thats what the error is also saying
Related
I am running a node.js app on http://localhost:3002 and a client side app on http://localhost:5173 (hosted with vite tooling)
I am trying to send cookies from the server to the client to authenticate users using express-session, but every request that comes in keeps generating as a new session. The cookies are not sending properly.
Node.js code
this.#app.use(cookieParser());
this.#app.use(cors({
origin: WEB_CLIENT_URL,
credentials: true,
methods:'GET, POST',
allowedHeaders:'Origin, X-Requested-With, Content-Type, Accept, authorization'
}));
var sess = {
secret: 'keyboard cat',
saveUninitialized: false,
resave: false,
cookie: {
secret: 'yourSecret',
secure: process.env.NODE_ENV === 'production',
httpOnly: process.env.NODE_ENV === 'production',
sameSite: "none" as "none",
maxAge: 24 * 60 * 60 * 1000, // 24 hours
domain: undefined//'localhost:3002'
},
}
if (process.env.NODE_ENV === 'production') {
this.#app.set('trust proxy', 1) // trust first proxy
}
this.#app.use(session(sess));
this.#app.use(async (req, res, next)=> {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', req.headers.origin ?? "");
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization');
// 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');
const session = req.session as Session;
console.log(req.session.id, req.method, req.originalUrl);
if (!session.user) {
const access = await TokenCollection.CreateAccessToken();
session.user = access.token;
req.session.save((err) => {
if (err) {
next(Utils.GetError('Error creating session', 500));
} else {
next();
}
});
} else {
console.log("FOUND USER");
next();
}
});
and client side
response = await fetch("http://localhost:3002/api/user", {
method: "POST",
body: JSON.stringify(profile_form_data),
headers: {
'authorization': `Bearer ${access.token}`,
'content-type': 'application/json'
},
credentials: 'include'
});
BTW im running in dev mode so process.env.NODE_ENV === 'production' will be false.
Does anyone know what's wrong?
I have build a React Redux app. It works fine in localhost in every browser(chrome,edge,firefox). But the Heroku deployed app doesn't works in Edge, Firefox , although it worked in chrome perfectly.
My app doesn't seems to send request and receive response ,because i noticed that the login button spinner keeps on spinning because it waits for response. So i think that no response is received from backend.
Following are the conclusion i made after test in different browser:
it works perfectly in every browser in localhost development mode.
After Deploying , herokuapp works perfectly fine in chrome only.
Herokuapp doesn't work in edge , Firefox.
Same is the issue with other system(Friends PC).
It doesn't work in any browser except chrome in Mobile device
After console logging and banging my head for hours i got the following error in microsoft edge:
Access to XMLHttpRequest at 'https://ecrypt.herokuapp.com/user/login' from origin 'http://ecrypt.herokuapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'https://ecrypt.herokuapp.com' that is not equal to the supplied origin.
2.2c59d01c.chunk.js:2 POST https://ecrypt.herokuapp.com/user/login net::ERR_FAILED
Okay, so i figured out that there is some problem with CORS.
Following is my code
Frontend:
import axios from "axios";
const API = axios.create({
baseURL: "https://ecrypt.herokuapp.com",
// withCredentials: false,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,PUT,POST,DELETE,PATCH,OPTIONS",
"Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token",
},
});
// const API = axios.create({ baseURL: "http://localhost:9000" });
const cloudinaryAPI = axios.create({
baseURL: "https://api.cloudinary.com/v1_1/ecryptimgdb",
});
//register new user
export const registerNewUser = (formData) =>
API.post("/user/register", formData);
//Account Activation through Email
export const activation = (activation_token) =>
API.post("/user/activation", {
data: {
activation_token,
},
});
//Login
export const login = (formData) =>
API.post("/user/login", formData, { withCredentials: true });
//get Token
export const getToken = () =>
API.post("/user/refresh_token", null, { withCredentials: true });
//Logout
export const logout = () => API.get("/user/logout", { withCredentials: true });
//get User
export const getUser = (token) =>
API.get("/user/info", {
headers: { Authorization: `${token}` },
});
//PROFILE SETTINGS__________________________________________________________________________________________
export const editProfile = (token, profileData) =>
API.post(
"/user/updateProfile",
{ profileData },
{
headers: { Authorization: `${token}` },
}
);
//forgot password____
export const forgotPass = (email) =>
API.post("/user/forgotPassword", { email });
//reset password_____
export const resetPass = (token, password) =>
API.post(
"/user/resetPassword",
{ password },
{
headers: { Authorization: `${token}` },
}
);
//change password____
export const changePass = (oldPassword, newPassword, token) =>
API.post(
"/user/changePassword",
{ oldPassword, newPassword },
{
headers: { Authorization: `${token}` },
}
);
BACKEND:
//IMPORTS
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const cors = require("cors");
const cookiesParser = require("cookie-parser");
const path = require("path");
const app = express();
// app.use(cors({ credentials: true, origin: "http://localhost:3000" }));
app.use(cors({ credentials: true, origin: "https://ecrypt.herokuapp.com" }));
app.use(cookiesParser());
// app.use(bodyParser.json({ limit: "30mb", extended: true }));
// app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const CONNECTION_URL = process.env.MONGODB_URL;
// const CONNECTION_URL = process.env.MONGODB_LOCAL_URL;
const PORT = process.env.PORT || 9000;
//MONGODB CLOUD DATABASE CONNECTION________________________
mongoose
.connect(CONNECTION_URL, {
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
})
.then(() => console.log("Connected to Database :: MongoDB Cloud"))
.catch((err) => console.log(err.message));
// app.use("/", routesIndex);
app.use("/", require("./routes/index"));
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}
//SERVER LISTENING
app.listen(PORT, (err) => {
if (err) {
console.log(err.message);
} else {
console.log(`Listening on localhost:${PORT}`);
}
});
NOTE
I am using cookies to store token so i needed withCredentials and Authorization headers.
The following headers should be sent by the server (instead of the front)
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,PUT,POST,DELETE,PATCH,OPTIONS",
"Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token",
},
Try removing headers from your axios request. I don't think those headers are allowed.
Ok, so i figured it out ,in my case i was setting headers as
{
headers: { Authorization: `${token}` },
}
Instead of setting it like above a slight change just worked for me:
{
headers: { Authorization: `Bearer ${token}` },
}
and at backend side in index.js or server.js whatever your file name is ,use cors middle ware like this:
const cors = require("cors");
app.use(
cors({
origin: ["https://blahblah.herokuapp.com", "http://localhost:****"],
credentials: true,
})
);
Note: credentials true if you want to pass cookies and access them at server side.
In my case i wanted to access the HttpOnly cookies at server side.
I've already read all the req.user undefined Post on SO, but after 3 days I have yet to find a solution, so please don't be mad for yet another req.user undefined question.
I've setup a normal Passport.js Local User Authentication. EVERYTHING works from Registration, to Login and to Logout perfectly,except when I try to fetch the User / User Session from the frontend. BUT fetching (or requestion) the user / user session through Postman works just fine, I get everything displayed perfectly and even the server console.logs me the user info instead of undefined, unlike when I try to fetch it from the Frontend.
I've modified my Cors & Fetch Code countless time in the last 3 days and I'm truly at my wits end.
<-- Backend (Node/Express) Code -->
// App
const app: Application = express();
app.use(express.urlencoded({ extended: true }))
app.use(express.json());
app.use(cors({ origin: 'http://localhost:3000', credentials: true }));
// I've even tried to include methods: "GET,HEAD,PUT,PATCH,POST,DELETE" with NO success.
// I've tried with and without origin or credentials.
// Express Session
app.use(session({
// secret: process.env.SESSION_SECRET
secret: 'whatever the secret is',
resave: false,
store: new pgSessionStore({
pool: pool,
tableName: 'session'
}),
saveUninitialized: true,
cookie: { maxAge: 365 * 24 * 60 * 60 * 1000 } //One year
}))
// Initialize passport & session
app.use(passport.initialize());
app.use(passport.session());
// Auth
initializePassport(passport);
// Routes
app.use(mountRoutes);
<-- Frontend (React.js) Code -->
I've tried Axios & Fetch in different 'Config' Variants that Fetch, Axios & Cors could offer.
const requestUser = () => {
fetch("http://localhost:3001/users", {
method: "GET",
mode: 'cors',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
}).then((res) => console.log(res)).catch(err => console.log(err))
const config = {
withCredentials: true,
headers: {
'Content-Type': 'application/json',
},
};
const url = 'http://localhost:3001/users'
const getUser = () => {
axios.get(url, config).then((res) => {
console.log(res.data);});
}
I'm truly at my wits and and I hope someone could help me. Maybe Iam just unable to see the forest infront of the tree after 3 days. Thanks in advance.
I am trying to make a third party application meaning it will run across multiple domains.
I want to handle a session per user that uses the app, therefore, I used the express-session module to make it but every time I make a request it starts up a new session for the current request...
const express = require('express'),
router = express.Router();
const session = require('express-session')
router.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
next();
});
router.use(session({
secret: 'keyboard cat',
resave: true,
maxAge: 2 * 60 * 60 * 1000, // 2 hours
saveUninitialized: false,
cookie: {
maxAge: 2 * 60 * 60 * 1000 ,
secure: false,
sameSite : false,
httpOnly: false}
}))
router.get( '/',function (req, res, next) {
// let payload = req.query;
let isDevClient = req.session.isDevClient || false;
console.log('isNew? ', isDevClient );
res.status(201).send({
success: true,
isDevClient,
message: 'msg..'
});
}).post( '/',function (req, res, next) {
let payload = req.body;
console.log('isNew? ', req.session.isDevClient )
req.session.isDevClient = true;
res.status(200).send({
success: true,
message: 'ok'
});
});
module.exports = router;
Request example
// javascript
fetch('https://127.0.0.1:8443/',{
method : "POST",
credentials: 'include',
})
//Jquery
$.ajax({
'type': 'post',
'url': 'https://127.0.0.1:8443',
'xhrFields': {
'withCredential's: true
}
'success': function (response) {},
})
``
Use credentials: 'include' in your fetch call, otherwise fetch won't send cookies during cross-domain request. Example:
fetch(..., {
...,
credentials: 'include'
}
Update: seems like recent Chrome version will not send cookies during cross-domain requests if SameSite attribute is not set.
Setting sameSite : 'none' should fix it. Note that chrome also requires these cookies to be Secure. https://www.chromestatus.com/feature/5633521622188032
By the way, you can easily provide examples with repl.it (like this)
I am receiving an http response with a set-cookie header that I can see in Chrome Devloper tools. I can even see the cookie listed under the network header, however the cookie is not being set in the browser. The browser will set same origin cookies but not ones from CORS requests.
I have tried exposing the header, to find out it is forbidden
I have tried setting withCredentials to true and setting the Access-Control-Allow-Credentials header to true, I am not using a wildcard for my Access-Control-Allow-Origin header and I have the cookie path set to '/'
I tried making this POST request with jQuery, but I could still not console log the set cookie header visible in the Chrome Developer Tools:
How do I make the browser set this cookie?
Here are some relevant parts of my project (Node/Express backend w/ AngularJS frontend):
var cors = require('cors');
app.use(cors({credentials: true, origin: 'http://localhost:8000'})
app.use(session({
secret: 'A_SECRET',
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
secure: false,
path: '/',
domain: 'http://localhost:8000',
maxAge: 1000 * 60 * 24
}
}))
app.use((req,res,next) => {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', 'http://localhost:8000');
next();
})
And in the front end (port 8000) -
angular.module('signIn', [
'core.company',
'signUp'
])
.config(function($httpProvider){
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
$httpProvider.defaults.withCredentials = true;
});
Sign-in component:
$http({
method: 'POST',
url: 'http://xxx.xx.xx.xxx:3000/login',
data: self.user
}).then(function successCallback(response, status, headers, config) {
console.log(response.headers('Set-Cookie')) // logs null
console.log(response.headers('set-cookie')) // logs null
}, function errorCallback(response) {
return response
});