Request Cookies have N/A Set for Cookie Attirbutes - node.js

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');
}

Related

How to create a cookie with node cookie-session

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();
});

Adding basic auth to node.js + express has no effect

I have added the following
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var fs = require("fs");
var index = require('./routes/index');
var users = require('./routes/users');
var app = express();
var basicAuth = require('express-basic-auth');
app.use(basicAuth({
users: { 'worker': '????????' }
}));
to my app.js. Depending on place, where I put this code, the site either stops work at all (displaying white space) or works without any auth.
Which is the correct place to put this code?
When white space, in Chrome Developer tools can be seen
Failed to load resource: the server responded with a status of 401 (Unauthorized)
but no login popup appears.
I am running my site on localhost under WebStorm. May be no popups is default browser behavior on localhost? Firefox does the same
With basic-auth, I use following code (in app.js or if you want this only on certain routes, then before those routes only):
var auth = require('basic-auth');
app.use(function(req, res, next){
var user = auth(req);
if (user === undefined || user['name'] !== 'mike' || user['pass'] !== 'mike123') {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="NodeJS"');
res.end('Unauthorized');
} else {
next();
}
});
By using express-basic-auth module, default configuration of the middleware does not add a WWW-Authenticate challenge header to responses of unauthorized requests. You must enable it by adding challenge: true to the options object as follows:
app.use(basicAuth({
users: { 'worker': '????????' },
challenge: true
}));
This piece of code will allow most browsers to show a popup to enter username/password on unauthorized responses.

Oauth access_token with node.js and jawbone-up NPM

UPDATED: following feedback from Remus below.
I can successfully authorize my web application and get back an access_token and refresh_token. I'm using the nice Grant NPM (or is that really grant-express?) to get authenticated (thanks to author Simeon Valichkov).
How do I pass in the access_token to my Jawbone API calls as a bearer token using NPMs like jawbone-up or Purest?
Question#1 - What's the simplest way to create this API call with a express-bearer-token and actually get back my Jawbone json data?
What I'm seeing on the page is the token (a looong string) rather than the Jawbone json results data.
var express = require('express')
, session = require('express-session')
, ejs = require('ejs')
, app = express()
, fs = require('fs')
, https = require('https')
, Grant = require('grant-express')
, grant = new Grant(require('./config'))
, bodyParser = require('body-parser')
, Purest = require('purest')
, jawbone = new Purest({provider: 'jawbone'})
, morgan = require('morgan')
, bearerToken = require('express-bearer-token');
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({extended:true}))
app.use(session({secret:'grant'}))
app.use(grant)
app.use(morgan('combined'))
app.use(bearerToken());
app.use(function (req, res) {
res.send('Token '+req.token);
});
var $today = new Date()
var $start = new Date($today); $start.setDate($today.getDate() -7)
var $end = new Date($today)
var $startDate = Math.floor(($start).getTime()/1000)
var $endDate = Math.floor(($end).getTime()/1000)
app.get('/sleeps', function (req, res) {
//res.send(JSON.stringify(req.query.raw, null, 2))
jawbone.query()
.select('sleeps')
.where ({start_date:$startDate, end_date:$endDate})
.auth(req.token)
.request(function(err, res, body) {
// expecting (hoping) to get sleep json here ...??
var result = JSON.parse(body);
res.json(result.data.items)
})
});
// HTTPS
var sslOptions = {
key : fs.readFileSync('./.server.key'),
cert : fs.readFileSync('./.server.crt')
};
var secureServer = https.createServer(sslOptions, app).listen(5000, function(){
console.log('Listening on 5000');
});
My Grant config file looks like this and would seem to be the obvious place to store my tokens.
module.exports = {
"server": {
"protocol" : "https",
"host" : "localhost:5000"
},
'jawbone' : {
'key' : '6f**********',
'secret' : '9b918*********************',
'callback' : '/sleeps',
'scope' : ['basic_read','extended_read','move_read','sleep_read']
}
};
Just to clarify - you're asking how to grab the token a user used when making a request to your server?
Personally I've done it several ways, notably using a regular expression to grab Authorization: Bearer <token> out of the headers. But in the end, I've found my go-to solution when using Express is to use the express-bearer-token middleware:
express = require('express');
bearerToken = require('express-bearer-token');
app = express();
app.use(bearerToken());
app.use(function (req, res) {
res.send('Token '+req.token);
});
So in your case, it would be as simple as:
app.get('/sleeps', function(req, res) {
jawbone.query()
.select('sleeps')
.where ({start_date:'', end_date:''})
.auth(req.token)
.request(function(err, res, body) {
res.json(req.query.raw);
})
});

How to access the raw body of the request before bodyparser?

I am writing a custom middleware that generates a cryptographic signature of every request (it is very similiar to the authentication mechanism used by AWS API v4). In order for this signature to be correctly generated, I must fetch the entire raw body of the HTTP request.
I am also using BodyParser, which is registered after my custom middleware.
My custom middleware can be represented like this:
// libs/simplifiedSignatureCheckerMiddleware.js
module.exports = function (req, res, next){
// simple and fast hashing stuff
var payload = '';
req.on('data', function(chunk) { payload += chunk }, null);
req.on('end', function(){
// hmac stuff
console.log(payload);
var ok = true; // ...
if(ok)
next();
else
next("Bad")
});
}
This is how I use it on the server.
// simpleServer.js
// BASE SETUP
// =============================================================================
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var jsonStream = require('express-jsonstream');
var nconf = require('nconf');
var https = require('https');
var fs = require('fs');
// load configurations
nconf.argv().env();
nconf.file({file: 'config.json'});
app.use(require('./libs/simplifiedSignatureCheckerMiddleware'));
// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(jsonStream());
// ROUTES FOR API
// =============================================================================
var router = express.Router();
router.post('/api/', function (req, res) {
var param1 = req.body.param1 || "";
var param2 = req.body.param2 || "";
res.json({message: 'welcome', one: param1, two: param2 });
});
// REGISTER ROUTES
app.use(router);
// START THE SERVER
// =============================================================================
https.createServer({
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
}, app).listen(nconf.get('http:port'));
console.log("APIs listening on port " + nconf.get('http:port'));
As you can verify, the raw body is written successfully to the console by the middleware, BUT the request will never be processed by the registered route and the connection hangs forever.
Do you have any clue on how to solve this problem?
Thanks in advance.
Ok, since the only feasible way to solve this problem seems to be by modifying the original source code of bodyParser, I have forked it.
https://github.com/emanuelecasadio/body-parser-rawbody
This fork exposes the raw body of the request as a field named rawBody. As you can see, there is only ONE extra line of code.
You can install it by using npm install body-parser-rawbody.
EDIT
Another option is to use the bodyParser like this, as noted by dougwilson here: https://github.com/expressjs/body-parser/issues/83#issuecomment-80784100
app.use(bodyParser.json({verify:function(req,res,buf){req.rawBody=buf}}))
I haven't personally tried this option and I do not know if it works.

Why is socket.io creating a second 'sid' cookie with a different path?

I'm pulling up a project for which I swear this was not a problem before, but apparently is not right now -- I'm probably doing something stupid. I'm seeing express & socket.io create two different "sid" cookies, one with a path of "/" and the other with a path of "/socket.io". The behavior I'm expecting is to share the same cookie/session between my express app & socket.io.
"sid" cookie for "/":
"sid" cookie for "/socket.io":
I'm setting up express via:
var config = require('config');
var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var sessionStore = require('./session-store');
var sessionConfig = {
store : sessionStore,
secret : config.server.sessionSecret,
key : config.server.sessionKey,
saveUninitialized : true,
resave : true,
cookie : { secure: config.server.useHTTPS }
};
module.exports = function (app) {
app.use(cookieParser(config.server.sessionSecret));
app.use(session(sessionConfig));
};
I'm setting up socket.io via:
var config = require('config');
var redis = require('socket.io-redis')(config.redis.socket);
var cookieParser = require('socket.io-cookie-parser');
var sessionStore = require('./session-store');
module.exports = function (io) {
io.adapter(redis);
io.use(cookieParser(config.server.sessionSecret));
io.use(authorization);
};
function authorization (socket, next) {
var unauthorized = new Error('Unauthorized');
if (!socket.request.headers.cookie) {
return next(unauthorized);
}
var sessionKey = socket.server.engine.cookie;
var sessionId = socket.request.signedCookies[sessionKey] || socket.request.cookies[sessionKey];
if (!sessionId) {
return next(unauthorized);
}
sessionStore.get(sessionId, function (err, session) {
// use session's userId to fetch user & attach to socket
});
}
These two files are tied together from my main server file:
var http = require('http');
var express = require('express');
var socketio = require('socket.io');
var config = require('config');
var app = express();
var server = http.Server(app);
var io = socketio(server, {
cookie: config.server.sessionKey
});
// initialize aspects of the app
require('./config/initializers/io')(io);
require('./config/initializers/express')(app);
module.exports = server;
Okay, I think I solved it. It looks like supplying the cookie option when mounting socket.io atop my server ends up causing engine.io to set a cookie with the same name based upon these lines of code:
if (false !== this.cookie) {
transport.on('headers', function(headers){
headers['Set-Cookie'] = self.cookie + '=' + id;
});
}
According to RFC-2109 HTTP State Management Mechanism, the default path is the current URL path:
Path Defaults to the path of the request URL that generated the
Set-Cookie response, up to, but not including, the
right-most /.
That would explain the new cookie being created since socket.io's endpoint is /socket.io by default. Since I'm using custom authorization that reads a cookie anyway, I figure it's safe to disable cookies in engine.io by changing my socket instantiation to the following:
var io = socketio(server, {
cookie: false
});
This now breaks my authorization function included in the original question, specifically this line:
var sessionKey = socket.server.engine.cookie;
Since I'm no longer passing the cookie key through to socket.io/engine.io, I instead need to read straight from my config:
var sessionKey = config.server.sessionKey;

Resources