double-submit-cookies
Version:
Double Submit Cookies Authentication
81 lines (69 loc) • 2.51 kB
JavaScript
var jwt = require('jsonwebtoken'),
nonce = require('nonce')(),
cookieParser = require('cookie-parser'),
ejwt = require('./express-jwt'),
crypto = require('./crypto');
module.exports = {
addDSCookies: _addDSCookies,
clearDSCookies: _clearDSCookies,
jwtTokenVerifier: _jwtTokenVerifier
};
// Exposed methods
function _addDSCookies(options) {
var xsrfToken = generateRandomCsrfToken(),
claims = getClaimFromData(options.data, xsrfToken),
token = jwt.sign(claims, options.jwtSecret, {
expiresIn: options.jwtTokenExpirationInMinutes * 60
});
if (options.header) { // Use headers instead of cookies
options.res.setHeader("x-token", token);
options.res.setHeader("x-xsrf-token", xsrfToken);
} else {
// Set JWT token in secure/http only cookie
options.res.cookie('token', token,
{
maxAge: (options.jwtTokenExpirationInMinutes * 60 * 1000),
secure: (typeof options.secure === 'undefined') ? true : options.secure,
httpOnly: true
});
// Generate additional cookie to implement double-submit cookie protection
// https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
options.res.cookie('xsrf-token', xsrfToken, {
secure: (typeof options.secure === 'undefined') ? true : options.secure
});
}
}
function _clearDSCookies(options) {
options.res.clearCookie('token');
options.res.clearCookie('xsrf-token');
}
function _jwtTokenVerifier(app, options) {
app.use(cookieParser());
app.use(ejwt(
{
secret: options.jwtSecret,
algorithms: [options.jwtAlgorithm], // Allow only a single algorithm we use
getToken: function fromCookie (req) {
// For now accept only tokens stored in secure cookies
if (req.cookies && req.cookies.token) {
return req.cookies.token;
}
// Return token if it is stored in header
if (req.headers && req.headers['x-token']) {
return req.headers['x-token'];
}
return null;
}
}).unless({
path: options.path
}));
}
// Private methods
function getClaimFromData(data, xsrfToken) {
data.xsrfToken = xsrfToken;
return data;
}
function generateRandomCsrfToken() {
return encodeURIComponent(crypto.hashSha1Sync(nonce()));
}
;