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)
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 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
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'm trying to send data from client's inputs based on React.js to server written in Node.js which put it to DB. I have no errors and after submit, new records show in database but they are empty. I have two inputs and I'm joining them in one string and trying send it to DB (so DB has one property). Can you check my code and see what is wrong? Maybe something with headers...
This is function in React component:
addCompetitor = event => {
event.preventDefault();
const name = this.state.draftCompetitorName;
const lastname = this.state.draftCompetitorLastname;
fetch(`http://localhost:5000/competitors`, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: `${name}${lastname}` })
})
.then(response => response.json())
};
This is server POST response:
app.post("/competitors/", urlencodedParser, function (req, res) {
const newCompetitor = new Competitor({ name: req.body.name });
newCompetitor.save().then(competitor => res.json(competitor));
});
And it's app configuration:
app.use(function (req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, OPTIONS, PUT, PATCH, DELETE"
);
res.setHeader(
"Access-Control-Allow-Headers",
"Content-Type",
"X-Requested-With"
);
res.setHeader("Access-Control-Allow-Credentials", true);
next();
});
If not first install bodyparser. This parses incoming request bodies in a middleware before your handlers, which will be available under the req.body property.
app.use(bodyParser.json({
limit: '50mb',
parameterLimit: 100000
}))
Alternatively what is the express version you are using ? Is it greater than 4.16? Then you can also use
app.use(express.json());
See notes here
https://expressjs.com/en/api.html#express.json
Modify your code
let databody = {
"name": `${name}${lastname}`,
"otherprop": this.state.otherprop
}
From frontend use
body: JSON.stringify(databody),
In express end remove urlencodedParser , should be like below:
app.post("/competitors", function (req, res) {
console.log(req.body);
});
You are using urlencodedParser as a middleware so I guess you used bodyParser.urlencoded({}) but your request is sending a json format. Try adjusting your request by adding the following header:
'Content-Type': 'application/x-www-form-urlencoded'
EDIT:
Also body should be in the following format:
body: `name=${name}${lastname}`
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
});