I am running a small node app. And I am trying to get it to create a cookie for each visitor, called 'session' that contains - for example - the session id. But I cannot seem to get node to create a cookie through cookie-session. My code so far:
const fs = require('fs');
const http = require('http');
const https = require('https');
const privateKey = fs.readFileSync('PATHTOKEY');
const certificate = fs.readFileSync('PATHTOKEY');
const credentials = {key: privateKey, cert: certificate};
const Keygrip = require("keygrip");
const express = require('express');
const app = express();
const port = APORTNUMBER;
const secureport = APORTNUMBER;
const helmet = require('helmet');
const options = {
dotfiles: 'deny',
etag: true,
extensions: ['html', 'htm'],
index: 'index.html',
lastModified: true,
maxAge: 0,
redirect: true,
setHeaders: function (res, path, stat) {
res.set('x-timestamp', Date.now())
}
};
app.use(express.static('public', options), helmet());
So far, no problems. But then comes the middleware cookie-session.
const session = require('cookie-session');
const expiryDate = new Date(Date.now() + 60 * 60 * 1000); // 1 hour
app.use(
session({
name: 'session',
keys: new Keygrip(["MYSECRET1", "MYSECRET2"]),
cookie: {
secure: true,
httpOnly: true,
expires: expiryDate
}
})
);
Above, I've specified the middleware to use these cookie-session parameters, but how do I proceed from here to actually get it to create this cookie?
const httpServer = http.createServer(app);
const httpsServer = https.createServer(credentials, app);
httpServer.listen(port);
httpsServer.listen(secureport);
console.log("Node server started");
Well, after trying this myself I manages to successfully use the cookie-session middleware. yay
I'm using the middleware like this:
app.use(cookieSession({
name: 'session', // replace this with your own name to suit your needs
keys: [ 'your-secret-key-goes-here', 'your-secret-key-goes-here' ]
})
About the duplicate values in keys option - the docs and related examples always use 2 different keys, despite the TypeScript #types lib declares that
The list of keys to use to sign & verify cookie values. Set cookies
are always signed with keys[0], while the other keys are valid for
verification, allowing for key rotation.
So.. I've used only one key.. twice... and it works as excepted
Note that I'm using this middleware before I'm registering the express app routes in order for this middleware to take effect before the router is executed (per request)
In each of my routes I can use the middleware using something like this
app.get('/test', (req, res) => {
req.session.test = { a: 5, b: 7} // yes - JSON payload are valid :)
})
To verify - ensure that your initial request got the following headers
Set-Cookie: session=eyJ0ZXN0Ijp7ImEiOjUsImIiOjd9fQ==; path=/; secure; httponly
Set-Cookie: session.sig=D4VVF4XSbBEWXI4b04ZvybAxppw; path=/; secure; httponly
This is only an example where the session is the name of the cookie as I've defined earlier. Cheers
Your current code looks right, based also on the documentation # http://expressjs.com/en/resources/middleware/cookie-session.html
I would suggest defining an app.get and testing everything with a tool like postman or fidler.
e.g.
app.get('/test', function (req, res, next) {
// Update views
req.session.views = (req.session.views || 0) + 1
// Write response
res.end(req.session.views + ' views')
})
I've not been able to figure out how to work with neither express-cookie nor cookie-session. However, I have been able to create cookies with cookie-parser middleware.
dependency:
const cookieParser = require('cookie-parser');
config:
const cookieConfig = {
httpOnly: true,
secure: true,
maxAge: 1800,
signed: true
};
Express:
app.use(cookieParser('MYSECRET'));
app.use(function (req, res, next) {
let cookie = req.cookies.cookieName;
if (cookie === undefined) {
let randomNumber=LOGICFORRANDOMNUMBER
res.cookie('COOKIENAME', randomNumber, cookieConfig);
};
next();
});
Related
Logger used in the code:
How to hide cookies information in headers in express calls for .sendredirect, .send and .sendFile functions? I have used pino logger.
LOGGER.ts
var pinoms = require('pino-multi-stream')
var prettyStream = pinoms.prettyStream()
var streams = [
{stream: fs.createWriteStream('my.log') },
{stream: prettyStream }
]
var logger = pinoms(pinoms.multistream(streams))
logger.info("HELLO %s!", "World")
abc.ts
const obj = require('../logger')
const logger = obj.logger
const expressLogger = expressPino({ logger });
const app = express()
.use(expressLogger)
.use('/',express.static ({
...,
setHeaders: function (res, path, stat) {
res.set('set-cookie', ''), // is correct and data it hides cookie
req.set('cookie', '') // ERROR: Cannot find name 'req'.
}})
Although I am able to hide cookies in response obj request still shows cookie. How can we hide this data in output?
Hiding header in req and response on express app which was generating secret info in loggers. Add a statement on express app object itself
app.use(function(req, res, next) {
delete req.headers['cookie']; // should be lowercase
delete req.headers['set-cookie'];
next();
});
.use(session({
secret: 'xxx',
resave: false,
saveUninitialized: false}))//the session cookie will not be set on the browser unless the session is modified
the above deletes the header altogether and single sign-on shall not work. Use
var redaction = noir([
'cookie', 'path.to.cookie'], '')
https://www.npmjs.com/package/pino-noir
What worked for me (I needed to only delete cookie from headers, not the whole headers):
export const httpLogger = pinoHttp({
logger,
serializers: {
req: pino.stdSerializers.wrapRequestSerializer(r => {
delete r.headers['cookie']
return r
}),
},
})
Keycloak is an open-source authentication and identity management solution written in Java. It provides a nodejs adapter using which I am able to successfully integrate with express. Here is the routes file which works:
'use strict';
module.exports = function(app) {
var Keycloak = require('keycloak-connect');
var session = require('express-session');
var memoryStore = new session.MemoryStore();
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
store: memoryStore
}));
var keycloak = new Keycloak({
store: memoryStore
});
app.use(keycloak.middleware({
logout: '/logout',
admin: '/'
}));
// var lRController = require('../controllers/LRController');
//
// app.route('/lrs').get(lRController.list_all_lrs).post(lRController.create_a_lr);
var DeliveryOrderController = require('../controllers/DeliveryOrderController');
app.route('/').get(keycloak.protect(), DeliveryOrderController.getAllDos)
app.route('/api/dos').get(keycloak.protect(), DeliveryOrderController.getAllDos).post(DeliveryOrderController.createDo);
app.route('/api/do').put(DeliveryOrderController.updateDo);
app.route('/api/do/:doNumber').get(DeliveryOrderController.getDoByDoNumber);
app.route('/api/do/location/:locationId').get(DeliveryOrderController.getDoByLocation);
app.route('/api/do/branch/:branchId').get(DeliveryOrderController.getDoByBranch);
app.route('/api/do').delete(DeliveryOrderController.deleteDo);
var TransportDeliveryOrderController = require('../controllers/TransportDeliveryOrderController');
app.route('/api/tdos').get(TransportDeliveryOrderController.getAllTdos).post(TransportDeliveryOrderController.createTdo);
app.route('/api/tdo').put(TransportDeliveryOrderController.updateTdo);
app.route('/api/tdo/:tdoNumber').get(TransportDeliveryOrderController.getTdoByTdoNumber);
app.route('/api/tdo/status/:status').get(TransportDeliveryOrderController.getTdoByStatus);
app.route('/api/tdo/status/:status/do/:doNumber').get(TransportDeliveryOrderController.getTdoByStatusAndDo);
};
As you can see in the Delivery order routes, I have two routes(copies of the same route) protected by keycloak.protect(). I am trying to do the same in sails. I have the following questions for doing that.
a. To integrate keycloak into express the following things are done to protect the routes
Require Keycloak and express session:
var Keycloak = require('keycloak-connect');
var session = require('express-session');
Define a memory store for the storing the sessions:
var memoryStore = new session.MemoryStore();
Include the session as middleware in express
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
store: memoryStore
}));
Initiate Keycloak:
var keycloak = new Keycloak({
store: memoryStore
});
Include keycloak Middleware into express middleware:
app.use(keycloak.middleware({
logout: '/logout',
admin: '/'
}));
Protect the route using keycloak.protect()
app.route('/api/dos').get(keycloak.protect(),DeliveryOrderController.getAllDos).post(DeliveryOrderController.createDo);
I need to establish similar steps in sails. How do I do these things in sails?
I am assuming http.js is where I add middleware. If I do, how do access keycloak in routes.js to use keycloak.protect().
For instance I can add the protect function in the following manner:
'/foo': [
keycloak.protect(),
{ controller: 'user', action: 'find' }
]
Here is the nodejs adapter for keycloak - https://github.com/keycloak/keycloak-nodejs-connect
I finally found an answer for this.
The problem is that keycloak.middleware() returns a list of functions and app.use() is happy with that.
Sails takes the http.middleware list and adds to it and calls app.use on the resulting list. If you just include the keycloak.middleware() you have a list of functions which includes an array of functions. Express ignores the array since it is not a function.
You need to expand the list into separate functions. Create a keycloak object at the top of http and initialize it.
Then put this at the bottom of the config/http.js file:
function expandList() {
var newOrder = [];
for (let i in module.exports.http.middleware.order)
{
var label = module.exports.http.middleware.order[i];
var functor = module.exports.http.middleware[label];
if (functor && functor.constructor === Array) {
for (let j in functor) {
let newlabel = label + "." + j;
newOrder.push(newlabel);
module.exports.http.middleware[newlabel] = functor[j];
}
} else {
newOrder.push(label);
}
};
module.exports.http.middleware.order = newOrder;
return "";
}
var result = init();
Inside the http.middleware object you need to use:
keycloakMiddleware: keycloak.middleware(),
and add it to order array.
Also add a policy to call protect and include this:
var kc = sails.config.http.keycloak.protect();
return kc(req, resp, next);
Hope this helps if you still need to solve this.
Maybe Sails should accept an array and expand it before calling Express
The above answer does not work for Sails 1.0. It now requires that the middleware be a function, not an array and keycloak returns an array from keycloak.middleware.
Here is what seems to work:
Create a service: KeycloakService
var session = require('express-session');
var Keycloak = require('keycloak-connect');
var memoryStore = new session.MemoryStore();
var KeycloakConfig = {
"realm": "somerealm,
"auth-server-url" : "https://server.com/auth",
"resource" : "someresource,
};
module.exports = {
config: KeycloakConfig,
setConfig: function (config) {
return new Keycloak({ store: memoryStore }, config);
}
}
Now in http.js put the following at the top
var KeycloakService = require('../api/services/KeycloakService');
var masterKeycloak = setupKeycloak();
var kcMiddleware = masterKeycloak.middleware();
function setupKeycloak() {
if (KeycloakService.keycloak == null) {
var config = Object.assign({}, KeycloakService.config);
config.bearerOnly = true;
KeycloakService.keycloak = KeycloakService.setConfig(config);
}
return KeycloakService.keycloak;
}
function recurseCallFunction(arr, i, req, res, next) {
if (arr.length > i) {
arr[i](req, res, () => recurseCallFunction(arr, i+1, req, res, next));
} else {
next();
}
}
Then in the middleware.order array include "keycloakMiddleware" and below the order array use
'keycloakMiddleware': function (req, res, next) {recurseCallFunction(kcMiddleware, 0, req, res, next);}
You will also need to define sails.config.http.keycloak: masterKeycloak
This will provide a function that recursively calls the middles functions in Keycloak.
You will need a policy defined. That policy can do:
var keycloak = sails.config.http.keycloak;
if (!req.hostname) {
req.hostname = req.host;
}
var authWithKeycloak = keycloak.protect();
if (authWithKeycloak) {
authWithKeycloak(req, res, next);
} else {
sails.log.warn('Keycloak authentication could not obtain a protection checker. Contact Developers');
}
This technique should help with Keycloak Policy Enforcer. You can add to the config to enable those and use them per the documentation with the keycloak.protect. I do not know anything about enforcers so I cannot help further on that.
I am working on fixing my Cloudfront (CF) Cookie Setup within my ExpressJS application and have had on and off success with the cookies successfully being set and recognized by my Cloudfront distribution, but I noticed that when I am unsuccessful (Bulk of the time) with accessing content from the distribution, it seems to potentially be due to the CF Request Cookies having an N/A value for Domain, Path and Expires. How do Request Cookies work? Within ExpressJS, there is the ability to Set-Cookie, which is what I do and is correctly displayed in the Response Cookies section, but I'm not sure what is causing the Request Cookies to appear in the start they do. I also noticed the CloudFront-Signature cookie is different between the request and response cookie. Could this cause this problem?
Screenshot of my HTTP request on page load
Code setting cookie:
var express = require('express');
var router = express.Router();
var session = require('express-session');
var passport = require('passport');
var crypto = require('crypto');
var moment = require('moment');
var path = require('path');
var fs = require('fs');
var cf = require('aws-cloudfront-sign');
var metaTags = require('./meta-routes');
router.use('/app', isLoggedIn, require('./app-routes'));
var cfPK = path.join(__dirname + process.env.CF_PRIVATE_KEY);
var cfOptions = {
keypairId: process.env.CF_KEY_ID,
expireTime: null,
privateKeyPath: cfPK
}
var signedCookies = cf.getSignedCookies(process.env.CF_CNAME, cfOptions);
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
for(var cookieId in signedCookies) {
res.cookie(cookieId, signedCookies[cookieId], { httpOnly: true, domain: process.env.DOMAIN_NAME || 'localhost', secure: true });
}
return next();
res.redirect('/login');
}
I have a simple express app that use session middleware together with passport-local middleware. Then I use share.js with browserchannel to stream data to server via share.listen(stream). All in align with documentation here.
My problem is that I cannot access session data (modified by passport-local and containing userID that was logged in) within stream. I need it to be able to restrict/grant access within client.on('message', function(data) {..}); based on some logic, but what of first importance is to check that the message came from logged in user. There, if I try to read ID it will be different from what potencialy is inside req.user._id. It seems that there share.js or browserchannel uses some different session, maybe?..
Here's the code:
var app = express();
var express = require('express');
...
// SETUP AND INIT
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true,
limit: 1024 * 1024 * 10
}));
app.use(methodOverride());
app.use(session({
secret: global.CONFIG.session.secret,
maxAge: new Date(Date.now() + 1000 * 60 * 60 * 24 * 2),
store: new MongoStore(global.CONFIG.mongo),
resave: true,
saveUninitialized: true
}));
app.use(express.static(__dirname + '/build'));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// Create the sharejs server instance.
var backend = livedb.client(livedbMongo(global.CONFIG.mongo.url, false));
var share = sharejs.server.createClient({
db: backend
});
app.use(browserChannel(function(client) {
var stream = new Duplex({objectMode: true});
stream._write = function(chunk, encoding, callback) {
if (client.state !== 'closed') {
client.send(chunk);
}
callback();
};
stream._read = function() {
};
stream.headers = client.headers;
stream.remoteAddress = stream.address;
client.on('message', function(data) {
console.log(client.id) // <- I wish it was the same as in req.user._id..
stream.push(data);
});
stream.on('error', function(msg) {
client.stop();
});
client.on('close', function(reason) {
stream.emit('close');
stream.emit('end');
stream.end();
});
// Actually pass the stream to ShareJS
share.listen(stream);
}));
It seems to me, from looking at the code, that there might be a solution that won't require hacking the module:
var browserChannel = require('browserchannel').server;
var middleware = browserChannel(options, function(session, req) {
if (req.user) {
session.user = req.user;
}
});
app.use(middleware);
See here.
I have the same problem and I solved it by wrapping the browserchannel middleware constructor in a custom constructor:
function myMiddlewareConstructor () {
var request;
var bcMiddleware = browserChannel(function (client) {
//here you see the request
});
return function (req,res,next) {
request = req;
bcMiddleware(req,res,next);
}
}
app.use(myMiddlewareConstructor());
It avoids having to change the browserchannel code.
After several days of inspecting the code I have found a solution. If we look at this line in browserchannel/dist/server.js we can see that the session is being created using some information from initial request. We can modify this part of code by adding
session = createSession(req.connection.remoteAddress, query, req.headers);
// ----------- we add this ------------
session.user = {};
if( req.user )
session.user = req.user;
// ------------------------------------
This will add user session details from initial request to the session variable.
In my application, I need to set a cookie using the express framework. I have tried the following code but it's not setting the cookie.
var express = require('express'), http = require('http');
var app = express();
app.configure(function(){
app.use(express.cookieParser());
app.use(express.static(__dirname + '/public'));
app.use(function (req, res) {
var randomNumber=Math.random().toString();
randomNumber=randomNumber.substring(2,randomNumber.length);
res.cookie('cokkieName',randomNumber, { maxAge: 900000, httpOnly: true })
console.log('cookie have created successfully');
});
});
var server = http.createServer(app);
var io = require('socket.io').listen(server);
server.listen(5555);
The order in which you use middleware in Express matters: middleware declared earlier will get called first, and if it can handle a request, any middleware declared later will not get called.
If express.static is handling the request, you need to move your middleware up:
// need cookieParser middleware before we can do anything with cookies
app.use(express.cookieParser());
// set a cookie
app.use(function (req, res, next) {
// check if client sent cookie
var cookie = req.cookies.cookieName;
if (cookie === undefined) {
// no: set a new cookie
var randomNumber=Math.random().toString();
randomNumber=randomNumber.substring(2,randomNumber.length);
res.cookie('cookieName',randomNumber, { maxAge: 900000, httpOnly: true });
console.log('cookie created successfully');
} else {
// yes, cookie was already present
console.log('cookie exists', cookie);
}
next(); // <-- important!
});
// let static middleware do its job
app.use(express.static(__dirname + '/public'));
Also, middleware needs to either end a request (by sending back a response), or pass the request to the next middleware. In this case, I've done the latter by calling next() when the cookie has been set.
Update
As of now the cookie parser is a seperate npm package, so instead of using
app.use(express.cookieParser());
you need to install it separately using npm i cookie-parser and then use it as:
const cookieParser = require('cookie-parser');
app.use(cookieParser());
Set Cookie?
res.cookie('cookieName', 'cookieValue')
Read Cookie?
req.cookies
Demo
const express('express')
, cookieParser = require('cookie-parser'); // in order to read cookie sent from client
app.get('/', (req,res)=>{
// read cookies
console.log(req.cookies)
let options = {
maxAge: 1000 * 60 * 15, // would expire after 15 minutes
httpOnly: true, // The cookie only accessible by the web server
signed: true // Indicates if the cookie should be signed
}
// Set cookie
res.cookie('cookieName', 'cookieValue', options) // options is optional
res.send('')
})
Not exactly answering your question, but I came across your question, while looking for an answer to an issue that I had. Maybe it will help somebody else.
My issue was that cookies were set in server response, but were not saved by the browser.
The server response came back with cookies set:
Set-Cookie:my_cookie=HelloWorld; Path=/; Expires=Wed, 15 Mar 2017 15:59:59 GMT
This is how I solved it.
I used fetch in the client-side code. If you do not specify credentials: 'include' in the fetch options, cookies are neither sent to server nor saved by the browser, even though the server response sets cookies.
Example:
var headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
return fetch('/your/server_endpoint', {
method: 'POST',
mode: 'same-origin',
redirect: 'follow',
credentials: 'include', // Don't forget to specify this if you need cookies
headers: headers,
body: JSON.stringify({
first_name: 'John',
last_name: 'Doe'
})
})
Set a cookie:
res.cookie('cookie', 'monster')
https://expressjs.com/en/4x/api.html#res.cookie
Read a cookie:
(using cookie-parser middleware)
req.cookies['cookie']
https://expressjs.com/en/4x/api.html#req.cookies
Setting cookie in the express is easy
first install cookie-parser
npm install cookie-parser
using middleware
const cookieParser = require('cookie-parser');
app.use(cookieParser());
Set cookie know more
res.cookie('cookieName', '1', { expires: new Date(Date.now() + 900000), httpOnly: true })
Accessing that cookie know more
console.dir(req.cookies.cookieName)
Done!
setting a cookie can be done as such:
res.cookie('cookie name', 'cookie value', [options])
where cookie_name is the name(String) of the cookie you wish to set, for example - "token", and the cookie value is the value(String) you wish to store in the said cookie.
as far as options go, you can read more about them here:
https://expressjs.com/en/api.html
one example of an option is 'maxAge' which indicates how long a cookie is valid, this is used for example when assigning an authentication token and you wish to limit the time a user can stay logged in before having to re-login.
Reading a cookie can be done as such:
req.cookies['cookie name']
which will return the value of the cookie.
Isomorphic Read cookie helper:
function getCookieValue(cookieName = '', cookie = '') {
const matches = cookie.match(`(^|[^;]+)\\s*${cookieName}\\s*=\\s*([^;]+)`)
return matches ? matches.pop() : ''
}
// Node with express:
getCookieValue('cookieName', req.headers.cookie)
// Browser:
getCookieValue('cookieName', document.cookie)
Write in Node with express:
res.cookie('cookieName', 'cookieValue')
Write in the browser:
function setCookie(
cname,
cvalue,
exdays = 100 * 365 /* 100 days */
) {
const now = new Date()
const expireMs = exdays * 24 * 60 * 60 * 1000
now.setTime(now.getTime() + expireMs)
document.cookie = `${cname}=${cvalue};expires=${now.toUTCString()};path=/`
}
// Example of usage
setCookie('cookieName', 'cookieValue')
If you have a problem with setting multiple cookies for one request
Try this way:
res.setHeader('Set-Cookie', [
`accessToken=${accessToken}; HttpOnly; Path=/; Max-Age=${60 * 60}; Secure=True;`,
`refreshToken=${refreshToken}; HttpOnly; Path=/; Max-Age=${60 * 60 * 24 * 7 * 2}; Secure=True;`
]);