Set Cookie with Nextjs and Vercel - node.js

I have a NextJs app that is deployed with Vercel that is not setting the cookie. I went into the console > Network and can see the request has a 200 status and the set-cookie value is present with no warning. I check the console > Applications > Cookies and the cookie is not found. I found a few similar questions on StackOverflow and Github, but very limited answers that didn't seem to push me to a solution.
My domain structure is like this:
domain.com
api.domain.com
subdomain.domain.com
nextapp.domain.com -> deployed through vercel
The domain and subdomain apps are standard React apps deployed through AWS and the API is a nodejs (express) app. I'm having no issues with the cookies being set for the domain and subdomain apps so I'm led to believe this is an issue caused by Vercel. My API responses look like this:
res.status(200).cookie('token', token, { httpOnly: true }).json(...)
with these cors options set
app.use(cors({
origin: true,
credentials: true
})
I tried updating the cookie options in the response to the following, but neither had an effect.
{ httpOnly: true, sameSite: false, secure: true }
{ httpOnly: true, sameSite: 'none', secure: true }
Is there something within Vercel that I'm missing that I should be aware of?
EDIT
It looks like this could be an issue with Next.JS actually. I'm not using the API layer within Next.js. My request looks something like this:
try {
let response = await axios.post('https://api.domain.com/login', params, { withCredentials: true } )
} catch(error) {
...
}

The issue wasn't related to NextJS or Vercel. When I set my cookie on the api I needed to add the domain like this:
res.status(200).cookie('token', token, { httpOnly: true, domain: process.env.NODE_ENV === 'development' ? '.localhost' : '.domain.com' }).json(...)
and it set as expected. I'm not sure why the cookie was still being set regardless on my domain and subdomain requests, but nonetheless with the added domain it sets on my nextjs.domain

Related

Cookie is not set on Production when I add domain option when setting cookie

I host my application on vercel.com. At first the cookie is not working at all, I tried to add the options in the cookie by many solutions like
{
path: '/',
secure: true,
sameSite: 'none'
}
and it works but unfortunately, it appeared as the cookie is not shown in the browser, but on the api the cookie is set which made me confused. So I thought maybe I forgot to add domain option when setting cookie, then I added domain in the cookie option like:
{
domain: 'mydomain.me'
path: '/',
secure: true,
sameSite: 'none'
}
Then the cookie is not working again and I when I checked in Network it said, this attempt to set a cookie via set-cookie header was blocked because its domain attribute was invalid with regards to the current host url.
What really went wrong here, why the cookie is not set when I add domain option?
And when I did not add domain why the cookie is set but not showing on the browser?

Is it impossible to set a domain of localhost cookie remotely from a backend?

I've already checked out these two SO questions:
Can I use localhost as the domain when setting an HTTP cookie?
Setting a cookie from a remote domain for local development
But I don't want to edit my HOSTS file and setting a wildcard domain doesn't help me.
I've used node.js, but it should be programming language agnostic...
So my problem is the following:
I wanna work on my Angular frontend on https://localhost:4200 (and possibly http://localhost:4200) and reach my backend by getting access to it. Obviously I have to implement CORS rules for that, hence I've implemented the following CORS rules in the Node.js backend:
const allowedOrigins = environment.header;
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
where allowedOrigins is an array that contains the following:
environment.header = ['https://localhost:4200', 'http://localhost:4200', 'http://test.example.org', 'https://test.example.org'];
The problem at hand is that when I go to work on my Angular frontend locally it does not send the cookie to the backend for some reason (maybe this kind of connection is simply not allowed by some RFC???), hence my JWT checking mechanism throws 403 Forbidden after logging in instantly.
My JWT check function looks like this:
if (req.headers.origin === 'https://localhost:4200' || 'http://localhost:4200')
orig = 'localhost';
else
orig = req.headers.origin;
res.cookie(
'access_token', 'Bearer ' + token, {
//domain: 'localhost',
domain: orig,
path: '/',
expires: new Date(Date.now() + 900000), // cookie will be removed after 15 mins
httpOnly: true // in production also add secure: true
})
I need to do this to work on my Angular frontend locally and the backend has a connection to another server, which works only locally for now...
withCredentials is of course true (so the JWT cookie is being sent with), so that's not the problem in my codebase.
UPDATE
Ok so I've figured out that req.headers.origin is usually undefined..
UPDATE 2
Changed req.headers.origin to req.headers.host, but still it doesn't work
I needed to add the following properties to the res.cookie for it to work:
sameSite: 'none', secure: true
Then I enabled third-party cookies in Incognito mode and it worked for me.
Case closed.

express cookie-session failing on mobile

I am trying to use cookie-session in express / nodejs, my setup works on PCs, but not on mobile:
on Android/Firefox, I can setup the session cookie, but I cannot modify it once set, I can't event erase it by setting req.session=null. Android/chrome is fine.
on iOS (Safari, chrome, firefox), the cookie doesn't seem to ever be set (iOS 15.3).
The setup is as follows:
my website is hosted at "mywebsite.com" (names modified for this forum)
my API setting/using the cookie is at another URL "mywebsite.io" (different extension)
cookie-session options are:
name: 'session',
keys: ['my secret'],
sameSite: 'none',
secure: true,
httpOnly: true,
signed: true,
overwrite: true,
Both website and API implement https. The API is behind a nginx proxy. Express includes this:
app.set('trust proxy', 1); // trust first proxy
to make sure the secure option works behind the proxy.
The web client is created in React, I run API calls using superagent with the .withCredentials() option.
I have tried:
setting the maxAge option
setting the domain option to 'mywebsite.io' or 'mywebsite.com'
setting the 'path' option to '/'
using the should-send-same-site-none module
Whatever I do, the iOS cookie is always empty (req.session = undefined on subsequent calls), and the Android/Firefox combo wont't let me update it.

HTTP cookies are not working for localhost subdomains

I have a node/express API that will create an HTTP cookie and pass it down to my React app for authentication. The setup was based on Ben Awad's JWT HTTP Cookie tutorial on Youtube if you're familiar with it. Everything works great when I am running the website on my localhost(localhost:4444). The issue I am now running into is that my app now uses subdomains for handling workspaces(similar to how JIRA or Monday.com uses a subdomain to specify a workspace/team). Whenever I run my app on a subdomain, the HTTP cookies stop working.
I've looked at a lot of threads regarding this issue and can't find a solution, no matter what I try, the cookie will not save to my browser. Here are the current things I have tried so far with no luck:
I've tried specifying the domain on the cookie. Both with a . and without
I've updated my host file to use a domain as a mask for localhost. Something like myapp.com:4444 which points to localhost:4444
I tried some fancy configuration I found where I was able to hide the port as well, so myapp.com pointed to localhost:4444.
I've tried Chrome, Safari, and Firefox
I've made sure there were no CORS issues
I've played around with the security settings of the cookie.
I also set up a ngrok server so there was a published domain to run in the browser
None of these attempts have made a difference so I am a bit lost at what to do at this point. The only other thing I could do is deploy my app to a proper server and just run my development off that but I really really don't want to do that, I should be able to develop from my local machine I would think.
My cookie knowledge is a bit bare so maybe there is something obvious I am missing?
This is what my setup looks like right now:
On the API I have a route(/refresh_token) that will create a new express cookie like so:
export const sendRefreshToken = (res: Response, token: string): void => {
res.cookie('jid', token, {
httpOnly: true,
path: '/refresh_token',
});
};
Then on the frontend it will essentially run this call on load:
fetch('http://localhost:3000/refresh_token', {
credentials: 'include',
method: 'POST'
}).then(async res => {
const { accessToken } = await res.json()
setState({ accessToken, workspaceId })
setLoading(false)
})
It seems super simple to do but everything just stops working when on a subdomain. I am completely lost at this point. If you any ideas, that would be great!
if httpOnly is true, it won't be parsable through client side js. for working with cookies on subdomains, set domain as the main domain (xyz.com)
an eg in BE:
res.cookie('refreshToken', refreshToken, {
domain: authCookieDomain,
path: '/',
sameSite: 'None',
secure: true,
httpOnly: false,
maxAge: cookieRefreshTokenMaxAgeMS
});
and on FE add withCredentials: true as axios options or credentials: include with fetch, and that should work

Express session-cookie not being sent on openshift with https and secure flag

Got a strange issue, I am using Express and in development we use http and have secure: false for the session cookie, however now we are moving to openshift we have turned https on thinking it would be a simple endeavour but our cookies are not being sent back with the responses. If however we turn off https and revert back to http on openshift it works fine and cookies are sent.
So here is an example of what the cookie config looks like:
var setupSession = function() {
var sessionConfig = {
secret: environmentVars.cookie.secret,
name: environmentVars.cookie.name,
maxAge: environmentVars.cookie.expiry,
domain: environmentVars.cookie.domain,
httpOnly: true,
secure: environmentVars.cookie.secure, // true when using https
secureProxy: environmentVars.cookie.secure, // true when using https
signed: true
};
app.set('trust proxy', 1); // Just added this, still no luck
app.use(session(sessionConfig));
};
So the above is run when the app starts up and as noted in the comments when we are using a secure connection the environment vars are set for us, and when the above is used in conjunction with HTTPS no cookie is sent back from express, however openshift cookies are sent back, like the gears one etc. Again with http and disabling the secure stuff it works fine we all get cookies and rejoice. All responses work and data is sent back its just the set-cookie header is missing for the apps cookies (but as mentioned not openshift ones).
So the actual certificate is not setup within nodejs it is setup on openshift as an alias with a certificate applied. So express really has no idea it is being run in https other than the environmental vars it is passed and the port it is provided by the gear that is running it.
So has anyone else had anything similar or has any ideas on what we can try to solve the problem or diagnose it? I did some reading and people suggested trying the trust proxy and secureProxy, which has been done but still no luck.
So it turns out I was just being an idiot, it should look like:
var setupSession = function() {
var sessionConfig = {
secret: environmentVars.cookie.secret,
name: environmentVars.cookie.name,
maxAge: environmentVars.cookie.expiry,
domain: environmentVars.cookie.domain,
httpOnly: true,
secureProxy: environmentVars.cookie.secure, // true when using https
signed: true,
cookie: {
secure: environmentVars.cookie.secure, // true when using https
}
};
app.set('trust proxy', 1); // Just added this, still no luck
app.use(session(sessionConfig));
};
All works now :)
I had similar problem with express-session and after many trials the culprit for me was setting cookie.domain. Browsers wouldn't save the cookie.
This is how I was setting the value:
cookie: {
...
domain: process.env.OPENSHIFT_CLOUD_DOMAIN,
...
}
Hope it helps anyone going through the same, since at the time this is the best suited stackoverflow question to share this.

Resources