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
Related
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
I know there are a lot of discussion about this topic already did. and i think i have followed all the instruction but still can't succeed.I think i'm missing something.
Here's my cookie :
const cookieOptions = {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000
),
httpOnly: true,
path: '/',
sameSite: 'lax',
maxAge: 1000 * 60 * 60 * 1,
};
if (process.env.NODE_ENV === 'production') {
cookieOptions.secure = true;
}
res.cookie('jwt', token, cookieOptions);
Cors :
app.use(
cors({
origin: ['http://localhost:3000'],
credentials: true,
})
);
Frontend :
const { data } = await axios.post(
"http://127.0.0.1:8000/api/v1/users/login",
{
email: enteredEmail,
password: enteredPassword,
},
{ withCredentials: true }
);
I have also used axios.defaults.withCredentials = true; . But still i can't find my jwt in Application > cookies.
Here's my Response header
and this is my request header
"res.cookie()" only set the HTTP Set-Cookie header with the options provided. Any option not specified defaults to the value stated in RFC 6265.If you take a look closely at the response header, see that jwt=... is present in the Set-Cookie header.
For your implementation, where you try to access the data from the axios response, you should look into res.json() or res.send(), as it directly sends the response back in the body, not the header.
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.
I recently built a simple real-time chat application with Nextjs on the frontend and Express on the backend. The frontend is deployed on vercel while the backend is deployed on heroku. When a user logs into the app, the backend generates a jwt token which is then sent via an HttpOnly cookie back to the frontend. Here is the code for said response:
const authenticate = async (req, res, next) => {
userService
.authenticate(req)
.then((user) => {
const { token, ...userInfo } = user;
res
.setHeader(
"Set-Cookie",
cookie.serialize("token", token, {
httpOnly: true,
secure: process.env.NODE_ENV !== "development",
maxAge: 60 * 60 * 24,
sameSite: "none",
path: "/",
})
)
.status(200)
.json(userInfo);
})
.catch(next);
};
After authentication, each subsequent request to the backend is supposed to send the token to ensure the user is logged in. For example, this is the request sent to the server to get a chat between the logged in user and another user.
const getChat = async (id) => {
const identification = id;
const response = await axios.get(
`<SERVER_URL>/chats/chat/${identification}`,
{ withCredentials: true }
);
return response;
};
In development when on localhost:3000 for the frontend and localhost:4000 for the backend, everything works fine. However, when I deployed the frontend to vercel and the backend to heroku, the browser simply refuses to save the cookie! The jwt token appears in the response header after sending the authentication request, but it isn't saved to the browser. I have tried absolutely everything I can think of, including changing the cookie parameters, but I can't get it to work. I am pretty sure I have cors properly configured on the backend as well, along with the cookie-parser module:
const cors = require("cors");
const cookieParser = require("cookie-parser");
app.use(
cors({
origin: "<CLIENT_URL>",
credentials: true,
})
app.use(cookieParser());
Thanks for taking the time to read this, any help would be greatly appreciated! And my apologies if I have not elaborated enough, this is my first post here and I'm still trying to learn the proper etiquette of the site!
HttpOnly can not read or write on client-side but when the first HttpOnly send through a request other request on the same origin can access the coockies in backend but you should request in Next.js like this.
Next.js using fetch :
const req = await fetch("http://localhost:7000/api/auth/login", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
},
body: JSON.stringify({
email: formData.get("email"),
password: formData.get("password"),
}),
});
const data = await req.json();
then in backend you can read the coockie through coockie-parser
server.js:
const cookieParser = require("cookie-parser");
app.use(coockieParser());
route.post('/login',(req,res) => {
if(user){
res
.cookie("access_token", newToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production" ? true : false,
})
.status(200)
.json({ ok: true, payload: data });
}
})
Now you can read this cookie in other routes but sure about the expiration time.
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,
}
);