UNPKG

session-rememberme

Version:

Add remember-me support to your app with this express middleware.

133 lines (123 loc) 5.91 kB
// Generated by CoffeeScript 2.7.0 (function() { var _, bcrypt, crypto; _ = require("lodash"); bcrypt = require('bcrypt'); crypto = require('crypto'); module.exports = function(configs) { var defaults, exports, generateRememberMeToken; if ((configs != null ? configs.loadUser : void 0) == null) { throw "loadUser must be defined"; } if ((configs != null ? configs.deleteToken : void 0) == null) { throw "deleteToken must be defined"; } if ((configs != null ? configs.deleteAllTokens : void 0) == null) { throw "deleteAllTokens must be defined"; } if ((configs != null ? configs.saveNewToken : void 0) == null) { throw "saveNewToken must be defined"; } defaults = { // How log should the user be remembered. Defaults to 90 days. maxAge: 90 * 24 * 60 * 60 * 1000, checkAuthenticated: function(req) { var ref; // Return true if session is already authenticated return (req.session != null) && (((ref = req.session) != null ? ref.user : void 0) != null); }, // userFromData: The user data persisted in browser. Could be an id or a complex id that will be serialized with JSON.stringify. // return [userRememberMeTokens[], sessionUser] // userRememberMeTokens: The list of rememberme tokens associated with the user // sessionUser: The user (typically the user id) to save in session. Will be passed back to setUserInSession loadUser: function(userFromData) {}, // Will be called with sessionUser passed to loadUser's sessionUser return value when the rememember me token was validated. // req: the express Request object. // sessionUser: the user loaded by loadUser. Typically the user id. // Should load the session user (from DB) based on user info stored in browser. setUserInSession: function(req, sessionUser) { // Should store user information in session. This can then be used in checkAuthenticated. return req.session.user = sessionUser; }, // Called when a token is invalidated // sessionUser: the user loaded by loadUser. Typically the user id. // token: the token to delete // Return Promise deleteToken: function(sessionUser, token) {}, // Called when an attack is suspected // sessionUser: the user loaded by loadUser. Typically the user id. // Return Promise // Should remove the token from persistence store deleteAllTokens: function(sessionUser) {}, // sessionUser: the user loaded by loadUser. Typically the user id. // newToken: the newly generated token that should be added to persistence store // Return Promise // Should remove all tokens from persistence store saveNewToken: function(sessionUser, newToken) {} }; configs = _.merge(defaults, configs); // Will return the new token in http header 'X-Remember-Me' generateRememberMeToken = async function(sessionUser, currentToken, userFromData, res) { var newToken; if (currentToken != null) { // Delete current token await configs.deleteToken(sessionUser, currentToken); } // Generate a new token newToken = crypto.randomBytes(32).toString("hex"); await configs.saveNewToken(sessionUser, crypto.createHash('md5').update(newToken).digest('hex')); // Return the new token in 'X-Remember-Me' header return res.set('X-Remember-Me', JSON.stringify({ user: userFromData, token: newToken })); }; exports = {}; // Should be called after successfull login AND the remember me option was set. // Will add a header to the response so you must write response only in the callback. exports.login = async function(sessionUser, userFromData, res) { return (await generateRememberMeToken(sessionUser, null, userFromData, res)); }; // Should be called when the user logout. // Remove the remember me token from storage. exports.logout = async function(req, res, sessionUser) { var remembermeData; // Delete the received token from the list of "remember me" token for that user remembermeData = req.get('X-Remember-Me'); if (remembermeData != null) { remembermeData = JSON.parse(remembermeData); return (await configs.deleteToken(sessionUser, crypto.createHash('md5').update(remembermeData.token).digest('hex'))); } }; exports.middleware = async function(req, res, next) { var remembermeData, sessionUser, userFromData, userRememberMeTokens; if (configs.checkAuthenticated(req)) { return next(); } // Try getting it from the headers remembermeData = req.get('X-Remember-Me'); if (remembermeData != null) { remembermeData = JSON.parse(remembermeData); } if (!(((remembermeData != null ? remembermeData.user : void 0) != null) && ((remembermeData != null ? remembermeData.token : void 0) != null))) { return next(); } userFromData = remembermeData.user; [userRememberMeTokens, sessionUser] = (await configs.loadUser(userFromData)); if (!((sessionUser != null) && (userRememberMeTokens != null))) { return next(); } if (_.includes(userRememberMeTokens, crypto.createHash('md5').update(remembermeData.token).digest('hex'))) { // Set the user in session and handle the request configs.setUserInSession(req, sessionUser); // Generate a new remembre me token await generateRememberMeToken(sessionUser, crypto.createHash('md5').update(remembermeData.token).digest('hex'), userFromData, res); } else { // Wipe all user.rememberMeToken token in case this is an attack await configs.deleteAllTokens(sessionUser); } return next(); }; return exports; }; }).call(this);