Express session cookie not showing up after Heroku deployment (apollo, node) - express

I'm getting a cookie locally but not once I deploy to Heroku using the heroku-redis plugin. I know that Heroku is working alright because my herokup-posgres URL is working. I have been trying to figure this out for a couple days and I'm not sure what to do.
Apollo client:
const client = new ApolloClient({
uri:
process.env.NODE_ENV === "production"
? "https://podapi.herokuapp.com/graphql"
: "http://localhost:4000/graphql",
cache: new InMemoryCache(),
credentials: "include",
});
Cors:
const corsOptions = {
origin: process.env.NODE_ENV === "production"
? (process.env.VERCEL_APP as string)
: (process.env.LOCALHOST_FRONTEND as string),
credentials: true,
};
Add redis to express
const app = express();
app.set("trust proxy", 1);
const RedisStore = connectRedis(session);
const redis = new Redis();
app.use(cors(corsOptions));
app.use(
session({
name: "qid",
store: new RedisStore({
client: redis,
disableTTL: true,
url: process.env.NODE_ENV === "production" ? process.env.REDIS_URL : undefined,
disableTouch: true,
}),
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 * 10, // 10 years
httpOnly: true,
sameSite: "lax",
secure: process.env.NODE_ENV === "production" ,
domain: process.env.NODE_ENV === "production" ? "podapi.herokuapp.com" : "localhost",
},
saveUninitialized: false,
secret: "mySecret",
resave: false,
})
);
Things I've tried: Adding the domain in the cookie (it doesn't work with or without it); Hard coding process.env.NODE_ENV to "production" incase it wasn't actually running production; I've added many little things like credentials: true/"include"; I've since added app.set("trust proxy", 1);
Any ideas or advice would be incredibly helpful.

Related

express does not set cookies in Production but they are visible in respoonse tab

II have an exppress app witch works correctly in dev.
However, when i do try to set cookies in prod, cookies are visible in network tab, but do not present in the browser.
I did a research and i think i covered most common problems, still cookies are not set
You may see my express app
I do add express configuration file, which i post here as well
const app = require("express")();
require("./config/express")(app);
app.disable("x-powered-by");
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "example.com");
res.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "*");
res.setHeader("Access-Control-Allow-Credentials", true);
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
if (req.method === "OPTIONS") {
// return res.sendStatus(200);
}
next();
});
//express config.js
const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const jwt = require("express-jwt");
const jwks = require("jwks-rsa");
const cookieSecret =
process.env.COOKIESECRET ||
"aabbcc";
// const { errorHandler } = require('../utils')
const expressSession = require("express-session");
const config = require("../config/config");
const helmet = require("helmet");
const morgan = require("morgan");
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "50mb" }));
app.use(cookieParser()); // TRY to use it with secret as in the __express.js file
app.use(express.static("uploads"));
app.use(express.static(path.join(__dirname, "static")));
app.use(express.static(path.resolve(__basedir, "static")));
app.use("/static/uploads", express.static("static/uploads"));
app.use("/files", express.static("files"));
app.use(helmet());
app.use(morgan("combined"));
// app.use(errorHandler(err, req, res, next));
app.use(
expressSession({
secret:
"aabbcc",
resave: false,
saveUninitialized: true, cookies,
cookie: { secure: true, sameSite: "none", domain: 'example.com' },
})
);
app.set("trust proxy", 1);
};
const expiryDate = new Date(Date.now() + 60 * 60 * 1000);
res.cookie(authCookieName, token, {
expires: expiryDate,
httpOnly: true,
secure: true,
domain: "example.com",
});
res.cookie(secondCookieName, secondToken, {
expires: expiryDate,
httpOnly: true,
secure: true,
domain: "example.com",
});
res.status(200).send(user).end();
return;
After some research, it appeared, that this is the problem here
This Set-Cookie was blocked because its Domain attribute is invalid with regards to the current host URL
This is seen as message in the response-headers.
But i do set all domains correcty. I tried with https as well as without it
Does any ever had the same problems?
PS : Both Front end and back end run on subdomains of a main domain
backend.maindomain.com - my backend
frontend.maindomain.com - my frontend
maindomain.com - landing page from witch you are rediirected to the app front end if you want to use it
Solved!
It appears you need to set the main domain name as domain and cookies are being set on each subdomain

Axios get request not returning when express session uses connect-redis store

So I have this weird thing going on where the axios requests I make from the front end do not get the response I'm sending from the server, but it only happens when I set the store in express session.
I am making a post request to the login route like in this function
const logIn = async (formData) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const response = await axios.post("/login", formData, config);
// do something with the response
};
When I make the request the server responds with status 200 and a user object. When I check the network tab in the browser devtools, I see that it is getting the correct response and the payload is showing the right user object as well
However axios never returns and is left hanging forever.
Now here's the kicker. This only happens if I set the store on the express session in my app.js
import express from "express";
import router from "./routes/router.js";
import session from "express-session";
import "dotenv/config";
import redis from "redis";
import connectRedis from "connect-redis";
const PORT = process.env.PORT || 5000;
const app = express();
const redisClient = redis.createClient();
redisClient.connect();
redisClient.on("connect", () => {
console.log("redis client connected");
});
const RedisStore = connectRedis(session);
app.use(
session({
name: "qid",
store: new RedisStore({ client: redisClient }), // RIGHT HERE
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365,
httpOnly: true,
sameSite: "lax",
secure: false,
},
saveUninitialized: false,
secret: process.env.SESSION_SECRET,
resave: false,
})
);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/", router);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
As soon as I comment out the line where I set the store, Axios gets the response and everything works perfectly. So I have to assume that the session store is somehow blocking the server response from getting to axios.
Do you have any idea why that might be?
I ran into the exact same issue, but I could finally solve this for my setup by using ioredis instead of redis.
I updated your app.js accordingly
import express from "express";
import router from "./routes/router.js";
import session from "express-session";
import "dotenv/config";
import Redis from 'ioredis';
import connectRedis from "connect-redis";
const PORT = process.env.PORT || 5000;
const app = express();
const redisClient = new Redis();
redisClient.on("connect", () => {
console.log("redis client connected");
});
const RedisStore = connectRedis(session);
app.use(
session({
name: "qid",
store: new RedisStore({ client: redisClient }), // RIGHT HERE
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365,
httpOnly: true,
sameSite: "lax",
secure: false,
},
saveUninitialized: false,
secret: process.env.SESSION_SECRET,
resave: false,
})
);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/", router);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Vue + axios does not save session cookies

In dev mode (express on localhost:4000, vue on localhost:8080) works fine, all cookies saving correct. In production mode i change cors settings and axios.defaults.baseURL, and... client dont save cookies...
but response request contains set-cookie header
backend cors settings:
app.use(
cors({
credentials: true,
origin: process.env.CLIENT_URL
})
)
backend session settings:
app.use(
session({
store: new redisStore({
host: 'localhost', port: 6379, client: redisClient, ttl: 86400
}),
name: '_TS',
secret: process.env.SESSION_SECRET,
resave: false,
cookie: { secure: false, maxAge: 600000 },
saveUninitialized: true
})
);
frontend:
axios.defaults.withCredentials = true
axios.defaults.baseURL = 'http://my-domain.com';
(contain session cookie!)
(session cookie is missing)
P.S. Postman works correctly, and saving cookies fine.

Safety of setting browser cookies from Express

Recently deployed a site of mine, and I am wondering if this solution to allowing the Express server on Heroku to set browser cookies for my Netlify React app is safe. I found it on an ill-explained SO answer elsewhere.
User.create(req.body)
.then(userNew => {
res
.cookie(
"usertoken",
jwt.sign({ _id: userNew._id }, process.env.JWT_KEY),
{
secure: true,
sameSite: "none",
httpOnly: false,
}
)
.json({
msg: "User registration success!",
user: {
_id: userNew._id,
userName: userNew.userName,
email: userNew.email,
favs: userNew.favs,
}
});
})
.catch(err => res.status(400).json(err));
The httpOnly, secure, and sameSite options are my concern. I used to only have httpOnly set to 'true' in development with no issue, but this solution worked for me in production. Thanks!
Set httpOnly to true to prevent client-side access to the cookie
Make sure to set expiry for JWT with expiresIn option.
Set maxAge in cookie option same at that of JWT expiry.
You can track if you are in production or not with NODE_ENV environmental variable. You can set up your code in a way that you don't keep changing it during production and development.
Here is how I commonly use the cookie along with JWT
const isProd = process.env.NODE_ENV === 'production';
res.cookie(
'usertoken',
jwt.sign({ _id: userNew._id }, process.env.JWT_KEY, { expiresIn: '1d' }),
{
secure: isProd,
sameSite: isProd ? 'none' : 'lax',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000,
}
);

NestJS API cookies arent working on Heroku

My NEST api works on localhost but cookies are not working on heroku.
Here is my config
app.enableCors({ origin: process.env.FRONT_END_URL, credentials: true }); // FE_URL == http://localhost:3000 (a react app)
app.set('trust proxy', 1); // I've seen people using express using this, but isn't working here
app.use((req, res, next) => {
req.connection.proxySecure = true; // If i don't do this, it'll throw an error if i'm using secure == true and sameSite == 'none'
next();
});
app.use(
sessions({
cookieName: 'FEATSession',
secret: 'ThisIsMyTopSecretWord',
duration: 24 * 60 * 60 * 1000,
activeDuration: 1000 * 60 * 5,
cookie: {
path: '/', // Tried '/' and not setting this prop too
// domain: 'feat-be.herokuapp.com', // I tried using and not using it too
sameSite: 'none',
secure: true,
httpOnly: true, // Tried true and false too
},
}),
);
Everything else is working fine, only cookies doesn't.
I've just solved myself a very similar problem today, try different syntax of setting cookie:
SameSite property invalid Cookies HTTPONLY MERN