Created
April 11, 2012 03:53
-
-
Save Aaronontheweb/2356731 to your computer and use it in GitHub Desktop.
everyauth password module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* | |
* Module dependencies | |
*/ | |
var User = require('../models/user') | |
, UserValidator = require('../validators/user-validator') | |
, Promise = require('everyauth').Promise; | |
//Everyauth configuration | |
module.exports = function(everyauth, repository){ | |
var validator = new UserValidator(repository); | |
everyauth.everymodule.logoutPath('/user/logout'); | |
everyauth.everymodule.logoutRedirectPath('/'); | |
everyauth.everymodule.findUserById( function (userId, callback) { | |
repository.get(userId, callback); | |
// callback has the signature, function (err, user) {...} | |
}); | |
everyauth.password | |
.getLoginPath('/user/login') //Uri of path to login page | |
.postLoginPath('/user/login') //Uri of path to login page form submission | |
.loginView('user/login.jade') | |
.loginLocals({ | |
title: 'Login' | |
}) | |
.authenticate(function(login, password){ | |
var promise = this.Promise(); | |
validator.validateLoginForm(login, password, function(error){ | |
if(error) promise.fulfill(error); | |
repository.get(login, function(error, user){ | |
//Fail the promise if we get an error or can't find the user | |
if(error) return promise.fail(error); | |
if(!user) return promise.fulfill(['user _'+ login + '_ does not exist.']); | |
User.verifyPassword(user.password, user.salt, password, function(error){ | |
//Fail if we get an error during password validation | |
if(error) return promise.fail(error); | |
//Otherwise, we can fulfill our promise | |
promise.fulfill(user); | |
}); | |
}); | |
}); | |
return promise; | |
}) | |
.loginSuccessRedirect('/') | |
.getRegisterPath('/user/register') //Uri of path to registration | |
.postRegisterPath('/user/register') //Uri of path for registration form submission | |
.registerView('user/register.jade') | |
.registerLocals({ | |
title: 'Register' | |
}) | |
.extractExtraRegistrationParams(function(req){ | |
return{ | |
email:req.body.email, | |
confirmPassword:req.body.confirmPassword | |
}; | |
}) | |
.validateRegistration(function(newUserAttributes){ | |
var promise = this.Promise(); | |
validator.validateRegistrationForm(convertNewUserAttributes(newUserAttributes), function(error){ | |
if(error) return promise.fulfill(error); | |
promise.fulfill(); | |
}); | |
return promise; | |
}) | |
.registerUser( function (newUserAttributes) { | |
var promise = this.Promise(); | |
repository.save(convertToUserObject(newUserAttributes), function(error, data){ | |
if(error) return promise.fail(error); | |
if(!data) return promise.fulfill(['Unable to save registration']); | |
promise.fulfill(data); | |
}); | |
return promise; | |
}) | |
.registerSuccessRedirect('/'); // Where to redirect to after a successful registration | |
}; | |
function convertNewUserAttributes(newUserAttributes){ | |
return {username:newUserAttributes.login, email:newUserAttributes.email, password:newUserAttributes.password, confirmPassword:newUserAttributes.confirmPassword}; | |
} | |
function convertToUserObject(newUserAttributes){ | |
var user = new User(newUserAttributes.login, newUserAttributes.email, newUserAttributes.password); | |
user.password = User.encryptPassword(user.password, user.salt); | |
return user; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* User model | |
*/ | |
/* | |
* Module dependencies | |
*/ | |
var crypto = require('crypto') | |
, timestamp = require('../helpers/timestamp'); | |
var User = exports = module.exports = function User(username, email, password, salt){ | |
this.id = username; | |
this.username = username; | |
this.email = email; | |
this.createdAt = new Date; | |
//If a salt isn't specified by default... | |
if(!salt){ | |
//Grab a unix timestamp and use that | |
this.salt = generateSalt(timestamp.currentTime()); | |
} else{ //Use the provided salt if it exists | |
this.salt = salt; | |
} | |
this.password = password; | |
}; | |
function generateSalt (salt){ | |
return crypto.createHash('md5').update(salt.toString()).digest('base64'); | |
} | |
function encryptPassword(password, salt){ | |
return crypto.createHmac('sha1', salt).update(password).digest('base64'); | |
} | |
//Want to have the option of a static method for this too since "this" scope can be an issue during chained callbacks | |
function verifyPassword(hashedPass, salt, plainPass, fn){ | |
if(hashedPass != encryptPassword(plainPass, salt)) return fn(new Error('invalid _password_')); | |
fn(); | |
} | |
User.prototype.verifyPassword = function(plainPassword, fn){ | |
console.log('Attempting to verify password for user %s', this.username); | |
verifyPassword(this.password, this.salt, plainPassword, fn); | |
} | |
//Have to break out the module exports at the end | |
exports.encryptPassword = encryptPassword; | |
exports.generateSalt = generateSalt; | |
exports.verifyPassword = verifyPassword; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Validator used for determining if a user | |
* is in a valid state and can be saved successfully. | |
*/ | |
//Accepts an implementation of the userRepository class in its constructor argument | |
var UserValidator = exports = module.exports = function UserValidator(userRepository){ | |
this.repository = userRepository; | |
}; | |
/* Validates the contents of a form submission for registration */ | |
UserValidator.prototype.validateRegistrationForm = function(registration, fn){ | |
var errors = []; | |
if(!registration.username) errors.push('_username_ required'); | |
if(!registration.email) errors.push('_email_ required'); | |
if(!registration.password) errors.push('_password_ required'); | |
if(!registration.confirmPassword) errors.push('_password confirm_ required'); | |
if(registration.password != registration.confirmPassword) errors.push('_password_ and _confirm_ must match.'); | |
if (errors.length) return fn(errors); | |
var validator = this; | |
this.userNameExists(registration.username, function(error, user){ | |
if(error) errors.push(error); | |
if(user) errors.push('_'+registration.username+'_ already exists.'); | |
validator.emailExists(registration.email, function(emailError, email){ | |
if(emailError) errors.push(emailError); | |
if(email) errors.push('_'+registration.email+'_ is already registered.'); | |
if(errors.length) return fn(errors); | |
fn(); | |
}); | |
}); | |
} | |
/* Validates the content of a login form submission */ | |
UserValidator.prototype.validateLoginForm = function(login, password, fn){ | |
var errors = []; | |
if(!login) errors.push('_username_ required'); | |
if(!password) errors.push('_password_ required'); | |
if (errors.length) return fn(errors); | |
fn(); | |
} | |
/* Checks to see if this username already exists in our database */ | |
UserValidator.prototype.userNameExists = function(username, fn){ | |
this.repository.get(username, function(error, user){ | |
//Return the error to the caller if we encountered some problem in the repository | |
if(error) return fn(error); | |
//If we do have a username that matches | |
if(user) return fn(null, true); | |
//If we don't have a username that matches | |
fn(null, false); | |
}); | |
} | |
/* Checks to see if this email address already exists in our database */ | |
UserValidator.prototype.emailExists = function(email, fn){ | |
this.repository.getByEmail(email, function(error, user){ | |
//Return the error to the caller if we encountered some problem in the repository | |
if(error) return fn(error); | |
//If we do have a username that matches | |
if(user) return fn(null, true); | |
//If we don't have a username that matches | |
fn(null, false); | |
}); | |
} |
@freddybown ask and ye shall receive. The user validator connects to any repository which implements the methods called throughout the validator ;)
I have one version of it that uses MongoDB in a replica set, another that uses a simple in-memory repository (primarily used for unit testing.)
Awesome. If possible add up the code of these files. TIA
Condense them into a single file?
Talking about '../models/user' and '../validators/user-validator' files.
This is awesome, still i'd like to see the whole set up. I'm a newbie with this and currently i'm working with nodejs, couchDB and nano to build an auth module. The last missing piece could rely here.
is it too much if I ask for the full codebase?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great resource. I'd love to see the contents of '../models/user' and '../validators/user-validator'