The following warning is being shown in the console, even though I have the following settings on my express application. Has anyone seen this error before? My search brought me to https://github.com/expressjs/express/issues/3095
I am also using express : 4.17.1
let COOKIE_OPTIONS = { httpOnly: true, sameSite: 'None', secure: true };
A cookie associated with a cross-site resource at http://MYURL.URL was set
without the `SameSite` attribute. A future release of Chrome will only deliver
cookies with cross-site requests if they are set with `SameSite=None` and
`Secure`. You can review cookies in developer tools under
Application>Storage>Cookies and see more details at
https://www.chromestatus.com/feature/5088147346030592 and
https://www.chromestatus.com/feature/5633521622188032.
When doing a request using Insomia (Postman) I see the following
access_token=someToken;
Path=/;
HttpOnly;
Secure;
SameSite=None
Documentation Link: https://www.npmjs.com/package/express-session#cookiesamesite
The below code will solve your issue. This is also recommended going forward.
const express = require('express');
const session = require('express-session');
const app = express();
const sessionConfig = {
secret: 'MYSECRET',
name: 'appName',
resave: false,
saveUninitialized: false,
store: store,
cookie : {
sameSite: 'strict', // THIS is the config you are looing for.
}
};
if (process.env.NODE_ENV === 'production') {
app.set('trust proxy', 1); // trust first proxy
sessionConfig.cookie.secure = true; // serve secure cookies
}
app.use(session(sessionConfig));
In your case, set sameSite to 'none'
In case you are wondering what is store? I am using my database as storage for all the cookies. It's not relevant to the question asked by OP. Just added as pointed by #klevis in the comment. Here's the code:
const KnexSessionStore = require('connect-session-knex')(session);
const store = new KnexSessionStore({
tablename: 'session',
knex: kx,
createtable: false
});
Edit 1: Fixed issue pointed out by CaptainAdmin
Edit 2: Added store definition.
You can set these options without using any node package.. With Express Only
Like this:
app.get('/', (req,res)=>{
//.....Other Code
res.cookie('cookieName', 'cookieValue', { sameSite: 'none', secure: true})
//.....Other Code
})
As far I kwon, this is a warning about new implementation for chrome in the future
samesite option on cookies: Starting in Chrome 80, cookies that do not specify a SameSite attribute will be treated as if they were SameSite=Lax with the additional behavior that they will still be included in POST requests to ease the transition for existing sites.
Any further information: https://www.chromium.org/updates/same-site
If you desire to test your web page, this article explains how to set Chrome flags for testing. If your page stops working you have to check all request and see for "http://" to "https://" updates or check third-party cookies
Related
I have a server-side rendered web app running on localhost:3000 and the API on localhost:3010. How do I set the same cookie on both domains after a request to the API?
When I log in, I'm sending a POST request to localhost:3010 and it's setting a cookie like this:
const token = jwt.sign({ id, email }, secret, { expiresIn });
res.cookie('authorization', token, { signed: true, httpOnly: true, maxAge: 10000000 });
My problem is I can't figure out how to set that cookie on the app at localhost:3000. I was just using localStorage before, but it doesn't work for server-side rendering when I have my API and app on different domains.
Here's how my server-side rendering middleware on localhost:3000 looks like, trying to access said cookie:
import Cookies from 'universal-cookie';
export function serverSideRendering(req, res, next) {
const cookies = new Cookies(req.headers.cookie);
const token = cookies.get('authorization');
// ...
}
Try to change property name "authorization" to "JWT-token";
res.cookie('JWT-token', token, { signed: true, httpOnly: true, maxAge: 10000000 });
or you can try native node js method;
res.writeHead(200, {
'Set-Cookie': 'authorization='+token,
});
And my advice, don't use cookie, use headers fields. It's helps you in future to increase your app to ios and android apps.
And you can keep your token inside store of your app, it's more secure.
i followed the sample of authorized-https-endpoint and only added console.log to print the req.cookies, the problem is the cookies are always empty {} I set the cookies using client JS calls and they do save but from some reason, I can't get them on the server side.
here is the full code of index.js, it's exactly the same as the sample:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const express = require('express');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
const app = express();
const validateFirebaseIdToken = (req, res, next) => {
console.log(req.cookies); //// <----- issue this is empty {} why??
next();
};
app.use(cors);
app.use(cookieParser);
app.use(validateFirebaseIdToken);
app.get('/hello', (req, res) => {
res.send(`Hello!!`);
});
exports.app = functions.https.onRequest(app);
store cookie:
curl http://FUNCTION_URL/hello --cookie "__session=bar" // req.cookies =
{__session: bar}
doesn't store:
curl http://FUNCTION_URL/hello --cookie "foo=bar" // req.cookies =
{}
If you are using Firebase Hosting + Cloud Functions, __session is the only cookie you can store, by design. This is necessary for us to be able to efficiently cache content on the CDN -- we strip all cookies from the request other than __session. This should be documented but doesn't appear to be (oops!). We'll update documentation to reflect this limitation.
Also, you need to set Cache-Control Header as private
res.setHeader('Cache-Control', 'private');
Wow this cost me 2 days of debugging. It is documented (under Hosting > Serve dynamic content and host microservices > Manage cache behavior, but not in a place that I found to be useful -- it is at the very bottom "Using Cookies"). The sample code on Manage Session Cookies they provide uses the cookie name session instead of __session which, in my case, is what caused this problem for me.
Not sure if this is specific to Express.js served via cloud functions only, but that was my use case. The most frustrating part was that when testing locally using firebase serve caching doesn't factor in so it worked just fine.
Instead of trying req.cookies, use req.headers.cookie. You will have to handle the cookie string manually, but at least you don't need to implement express cookie parser, if that's a problem to you.
Is the above answer and naming convention still valid? I can't seem to pass any cookie, to include a session cookie named "__session", to a cloud function.
I setup a simple test function, with the proper firebase rewrite rules:
export const test = functions.https.onRequest((request, response) => {
if (request.cookies) {
response.status(200).send(`cookies: ${request.cookies}`);
} else {
response.status(200).send('no cookies');
}
});
The function gets called every time I access https://www.xxxcustomdomainxxx.com/test, but request.cookies is always undefined and thus 'no cookies' is returned.
For example, the following always returns 'no cookies':
curl https://www.xxxcustomdomainxxx.com/test --cookie "__session=testing"
I get the same behavior using the browser, even after verifying a session cookie named __session was properly set via my authentication endpoint. Further, the link cited above (https://firebase.google.com/docs/hosting/functions#using_cookies) no longer specifies anything about cookies or naming conventions.
I'm trying to get sessions variables working in my Express Node.js project, using the express-session module. I'm not getting any errors, and setting session variables seems to work, however they don't persist across requests. Here's a simplified bit of my code:
server.js
var express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var app = express();
app.use(bodyParser.json());
// Sessions
app.use(session({
secret: config.secret
}));
app.use('/api/projects', require('./controllers/api/projects'));
var server = app.listen(3000, function() {
console.log('Server listening on', 3000)
});
api/projects.js router
var router = require('express').Router()
router.get('/set', function(req, res, next) {
req.session.test = "test";
res.status(200).json({"test":req.session.test});
});
router.get('/current', function(req, res, next) {
res.status(200).json({"test":req.session.test});
})
Setting the variable seems to works...
The /set API call should set a session variable called test. This API call appears to work when I try it with curl:
curl -X GET localhost:3000/api/projects/set --cookie "connect.sid=s%3AyP3a8siRuA-5jDxWH4T03UxNpFd6lfBq.Ha8b8eJxbtW8fAJlbgR9jumfmBpJIXNE6444fOb2Jro"
{"test":"test"}
This is also confirmed in the console log:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
test: 'test' }
...however next time I check it it's not set
When I try to check the variable with my second API call, it appears to be undeclared:
curl -X GET localhost:3000/api/projects/current --cookie "connect.sid=s%3AyP3a8siRuA-5jDxWH4T03UxNpFd6lfBq.Ha8b8eJxbtW8fAJlbgR9jumfmBpJIXNE6444fOb2Jro"
{}
This is confirmed in the console log, the test variable is no longer set:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
How can I ensure my sessions variables persist across requests?
(PS: I've been stuck on this for a while and any small comment on hints or resolution is very welcome, even if you're not sure you've got the answer)
I can see two possible reasons for this.
First, you could've messed up with cookies in curl.
You could ensure that you've set your cookies correctly by checking http response. If it contains Set-Cookie header with a new connect.sid then you did something wrong.
Alternatively, you could use a web browser with native cookies support to guard yourself against such mistakes.
Second, you may've restarted your sever between two requests.
Since you didn't specify any persistent store for your sessions, no data will persist between node.js server restarts.
If you want session data to persist after node.js server stops or restarts, you should consider using some persistent session store (i.e. redis-store).
I'm trying to set up a basic session system in node. Here's what I've got so far:
app.js:
app.use(express.cookieParser('stackoverflow'));
app.use(express.session());
I'm setting the session data in ajax.js:
addClassToCart: function(req, res) {
req.session.cart = req.body.classId;
console.log(req.session.cart);
}
This logs the correct information. However, when I try to retrieve that information elsewhere (same file, different function):
console.log(req.session.cart);
I get undefined. I feel like I'm missing something incredibly basic. Various tutorials for this are either awful or require me to add in even more packages (something I'm trying to avoid).
More data from my debugging:
This works with non-AJAX requests
The session is set before it's logged.
As it turns out, the issue wasn't with Express' session (as the other answers seem to think). Rather, it was a misunderstanding on my part. I changed addClassToCart to the following:
addClassToCart: function(req, res) {
req.session.cart = req.body.classId;
console.log(req.session.cart);
res.send('class added');
}
Adding res.send() fixed the problem.
As noted in the answer to a related SO question, this can also occur if you're using fetch to get data from your server but you don't pass in the credentials option:
fetch('/addclasstocart', {
method: 'POST',
credentials: 'same-origin' // <- this is mandatory to deal with cookies
})
Cookies won't be passed to the server unless you include this option which means the request's session object will be reset with each new call.
I don't know about basic session store, but redis only took me a few minutes to setup:
app.use(express.session({
store: new RedisStore({
host: cfg.redis.host,
db: cfg.redis.db
}),
secret: 'poopy pants'
}));
On a mac:
brew install redis
app.use(express.session({
secret: "my secret",
store: new RedisStore,
cookie: { secure: false, maxAge:86400000 }
}));
Not sure the problem is in session age, but it just to be safe, I'd suggest you to specify maxAge.
NodeJS is a fantastic tool and blazing fast.
I'm wondering if HTTPClient supports cookies and if can be used in order to simulate very basic browser behaviour!
Help would be very much appreciated! =)
EDIT:
Found this: node-httpclient (seems useful!) not working!
Short answer: no. And it's not so great.
I implemented this as part of npm so that I could download tarballs from github. Here's the code that does that: https://github.com/isaacs/npm/blob/master/lib/utils/fetch.js#L96-100
var cookie = get(response.headers, "Set-Cookie")
if (cookie) {
cookie = (cookie + "").split(";").shift()
set(opts.headers, "Cookie", cookie)
}
The file's got a lot of npm-specific stuff (log, set, etc.) but it should show you the general idea. Basically, I'm collecting the cookies so that I can send them back on the next request when I get redirected.
I've talked with Mikeal Rogers about adding this kind of functionality to his "request" util, complete with supporting a filesystem-backed cookiejar, but it's really pretty tricky. You have to keep track of which domains to send the cookies to, and so on.
This will likely never be included in node directly, for that reason. But watch for developments in userspace.
EDIT: This is now supported by default in Request.
If you are looking to do cookies client side you can use https://github.com/mikeal/request
M.
Zombie.js is another choice if you want browser-like behaviour. It "maintains state across requests: history, cookies, HTML5 local and session stroage, etc.". More info on zombie's cookie api: http://zombie.labnotes.org/API
There is also PhantomJS and PhantomJS-based frameworks, like CasperJS.
A feature-complete solution for cookies
The self-made solutions proposed in the other answers here don't cover a lot of special cases, can easily break and lack a lot of standard features, such as persistence.
As mentioned by isaacs, the request module now has true cookie support. They provide examples with cookies on their Github page. The examples explain how to enable cookie support by adding a "tough-cookie" cookie jar to your request.
NOTE: A cookie jar contains and helps you manage your cookies.
To quote their Readme (as of April 2015):
Cookies are disabled by default (else, they would be used in
subsequent requests). To enable cookies, set jar to true (either in
defaults or options) and install tough-cookie.
The cookie management is provided through the tough-cookie module. It is a stable, rather feature-complete cookie management tool that implements the "HTTP State Management Mechanism" - RFC 6265. It even offers a variety of options to persist (store) cookies, using a cookie store.
The code below demonstrates using cookie from server side, here's a demo API server that parse cookies from a http client and check the cookie hash:
var express = require("express"),
app = express(),
hbs = require('hbs'),
mongoose = require('mongoose'),
port = parseInt(process.env.PORT, 10) || 4568;
app.configure(function () {
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(__dirname + '/public_api'));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.get('/api', function (req, res) {
var cookies = {};
req.headers.cookie && req.headers.cookie.split(';').forEach(function( cookie ) {
var parts = cookie.split('=');
cookies[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim();
});
if (!cookies['testcookie']) {
console.log('First request');
res.cookie('testcookie','testvaluecookie',{ maxAge: 900000, httpOnly: true });
res.end('FirstRequest');
} else {
console.log(cookies['testcookie']);
res.end(cookies['testcookie']);
}
});
app.listen(port);
On the client side, just make a normal request to the server api above, i'm using request module, it by default transfers cookie for each request.
request(options, function(err, response, body) {
console.log(util.inspect(response.headers));
res.render("index.html", {layout: false,user: {
username: req.session.user + body
}});
});
Just get cookies from Set-Cookie param in response headers and send them back with future requests. Should not be hard.