Introduction
I have a problem with sending cross domain session cookie and after searching I got even more confused.
Originally I had a client (Next app) on foo.vercel.com and the api (Express) on bar.heroku.com.
Sending and saving cookies was working correctly on localhost, but after deployment I got a problem with the sameSite set to lex by default. So I changed it to none. But I found that the sameSite none requires secure flag on the cookie set, so I also changed that.
The problem
After setting secure to true the cookie is no longer being sent.
If secure is set, and you access your site over HTTP, the cookie will not be set.
Both my server and client are hosted on HTTPS. I checked the logs on the heroku and the request protocol is HTTPS... however when I console.log a request.protocol from a GraphQL resolver the protocol is HTTP:
After seeing this
There's no such thing as cross domain cookies. You could share a cookie between foo.example.com and bar.example.com but never between example.com and example2.com
I moved the client to baz.heroku.com but the problem remains.
In order to check if everything still works I disabled the Cookies without SameSite must be secure in chrome://flags and it works correctly.
Questions
Is it even possible to set cookie cross domain?
Why does the POST method have different protocol than request I get in express server.
(I get the request from express and pass it through context to GraphQL resolvers)
And of course how can I send cookie to the client on different domain.
I would appreciate any help.
The problem was Heroku's proxy. I had to add the following to the express server:
app.set("trust proxy", 1);
Related
I have recently Pushed by Server and Webapp to the cloud. It is still in the testing phase and we're working with the default links given for both the server and Website. These links are normal HTTP and not Secure. I'm setting some cookies when the user logs in, but the client is not saving the cookies now. It's throwing an error stating Set-cookie was blocked because sameSite is empty so it has been set to Lax.
Questions :
I have tried setting the sameSite to None. But this also requires me to set secure as true. Which is not possible as the links are still HTTP only.
If I do not set the SameSite, It moves it by default to Lax. So it blocks my cross site cookie.
Is there anyway I can get this to work by someway completely avoiding SameSite or making it to none and still bypass the secure parameter?
Note : [ There server and Website have different URLs and these links are of EC2 (server) and S3 static host (website) ]
I am sending cookies from subdomain(1) express server to subdomain(2) react frontend with cookie domain set as actual domain.
cookies domain is set to my actual domain.
I can see cookies in the network panel, cookies are coming with the response.
I tested in postman, everything is working as expected but not in browser. Tried in firefox and chrome as well.
So nothing worked. We could not figure out why cookies were not getting set.
We decided to set cookies manually using js-cookie lib.
I noticed that when Prevent cross-site tracking is checked in Safari, I am unable to set the secure cookies. I described this issue in great detail in this question.
Then how do you set the secure cookies in Express with that setting enabled?
From MDN:
Values
The SameSite attribute accepts three values:
Lax
Cookies are allowed to be sent with top-level navigations and will be sent along with GET request initiated by third party website. This is the default value in modern browsers.
Strict
Cookies will only be sent in a first-party context and not be sent along with requests initiated by third party websites.
None
Cookies will be sent in all contexts, i.e sending cross-origin is allowed.
None used to be the default value, but recent browser versions made Lax the default value to have reasonably robust defense against some classes of cross-site request forgery (CSRF) attacks.
None requires the Secure attribute in latest browser versions. See below for more information.
It says in this article that Apple is phasing out third party cookies with Safari. I'm reading online that third party cookies are generated by a different domain than the one user is visiting, for cross-site tracking, retargeting, and ad-serving.
I am working on a project where the frontend is served on Netlify and the backend is from Heroku. Since the backend has a different domain than the front-end, the cookies generated from the node express backend are considered Third-party cookies?
Does that mean that I should have both frontend and backend on the same server going forward following this security practice?
Netlify allows you to proxy requests to your backend on a different hostname.
https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service
I'm not sure if it will let you set the cookies that way, Netlify might strip all headers, you should try.
If for some reason that does not work for you then you should either serve frontend and backend from the same hostname or set the cookie with JS on the client-side (which I don't recommend), also you can't set HttpOnly cookie from JS side.
I've got a node/express/passport backend listening on port 3001 which I'm hitting directly with the browser to authenticate. It authenticates and controls access to protected urls just fine. The front-end is polymer and served on a different port(8080) for dev. After authentication express redirects the browser to the polymer app. The problem is when the polymer app then makes an iron-ajax call back to the express backend, the auth cookie/session handle doesn't get passed, so the backend denies the request.
In production I'm thinking the backend will be on a different sub-domain (backend.foo.com) from the frontend (users.foo.com). Curious if anyone knows how to get express/passport to set a session cookie that will be shared across all the sub domains?
Cross-site sessions and subdomain cookies are quite different things.
To make your sessions cookies available for subdomains, you should be able to do something like this:
app.use(express.session({
cookie: {
path: '/',
domain: '.example.com',
}
}));
To have the sessions available across different, unrelated domains, you would have to use a more complicated mechanism to share data between domains but even though the title of your question suggests that you need the latter, the content of your question asks about the former so a subdomain cookie is all you need.
Also read about CORS - Cross-origin resource sharing - and make sure that nothing is blocked due to misconfigured CORS because that can be sometimes a real pain to configure correctly for situations like this.
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
I'm using express / node js for a simple server.
There's no need for secure https everywhere, but I do want to secure some upload form posts and responses that come back on that to phones.
So far I've setup a standard nodejs server on http with express.js.
I have an app.post('/upload'...)
I'm deployed on heroku, so I changed the app I'm testing to post the form data to https://myapp.herokuapp.com/upload
Is it now posting over https? And will the response be over https?
Or do I need to reconfigure the express server in some way to specifically handle that?
These uploads/responses are the only secure part, and non-visible to users (done by the phone app) - so there's no need to do full http ssl endpoint config for the whole domain/sub domain if the above piggyback solution is ok.
On Heroku, SSL is terminated at the routing layer and a X-Forwarded-Proto: https header is added to the request so your app can know that the request came in over SSL. In other words, from your app's perspective, the request is plain HTTP and doesn't need to do anything special, but you can always check for the X-Forwarded-Proto: https header if you want to make sure the request was made securely. If the request was made over SSL, the response will also be over SSL since it they are both part of the same connection.