UNPKG

session-rememberme

Version:

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

117 lines (91 loc) 4.77 kB
_ = require("lodash") bcrypt = require( 'bcrypt' ) crypto = require( 'crypto' ) module.exports = ( configs ) -> throw "loadUser must be defined" unless configs?.loadUser? throw "deleteToken must be defined" unless configs?.deleteToken? throw "deleteAllTokens must be defined" unless configs?.deleteAllTokens? throw "saveNewToken must be defined" unless configs?.saveNewToken? defaults = # How log should the user be remembered. Defaults to 90 days. maxAge: 90 * 24 * 60 * 60 * 1000 checkAuthenticated: ( req ) -> # Return true if session is already authenticated return req.session? && req.session?.user? # 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: ( userFromData ) -> # Should load the session user (from DB) based on user info stored in browser. return # 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. setUserInSession: ( req, sessionUser ) -> # Should store user information in session. This can then be used in checkAuthenticated. 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: ( sessionUser, token ) -> # Should remove the token from persistence store return # Called when an attack is suspected # sessionUser: the user loaded by loadUser. Typically the user id. # Return Promise deleteAllTokens: ( sessionUser ) -> # Should remove all tokens from persistence store return # sessionUser: the user loaded by loadUser. Typically the user id. # newToken: the newly generated token that should be added to persistence store # Return Promise saveNewToken: ( sessionUser, newToken ) -> return configs = _.merge defaults, configs # Will return the new token in http header 'X-Remember-Me' generateRememberMeToken = (sessionUser, currentToken, userFromData, res) -> # Delete current token await configs.deleteToken sessionUser, currentToken if 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 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 = ( sessionUser, userFromData, res ) -> await generateRememberMeToken sessionUser, null, userFromData, res # # Should be called when the user logout. # Remove the remember me token from storage. # exports.logout = ( req, res, sessionUser ) -> # Delete the received token from the list of "remember me" token for that user remembermeData = req.get('X-Remember-Me') if remembermeData? remembermeData = JSON.parse(remembermeData) await configs.deleteToken sessionUser, crypto.createHash('md5').update(remembermeData.token).digest('hex') exports.middleware = (req, res, next) -> return next() if configs.checkAuthenticated( req ) # Try getting it from the headers remembermeData = req.get('X-Remember-Me') remembermeData = JSON.parse(remembermeData) if remembermeData? return next() unless remembermeData?.user? && remembermeData?.token? userFromData = remembermeData.user [userRememberMeTokens, sessionUser] = await configs.loadUser userFromData return next() unless sessionUser? && userRememberMeTokens? 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 next() return exports