Skip to content

Instantly share code, notes, and snippets.

@Aaronontheweb
Created April 11, 2012 03:53
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Aaronontheweb/2356731 to your computer and use it in GitHub Desktop.
Save Aaronontheweb/2356731 to your computer and use it in GitHub Desktop.
everyauth password module
*
* 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;
}
/*
* 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;
/*
* 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);
});
}
@freddybowen
Copy link

Great resource. I'd love to see the contents of '../models/user' and '../validators/user-validator'

@Aaronontheweb
Copy link
Author

@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.)

@chaitu87
Copy link

Awesome. If possible add up the code of these files. TIA

@Aaronontheweb
Copy link
Author

Condense them into a single file?

@chaitu87
Copy link

Talking about '../models/user' and '../validators/user-validator' files.

@alterx
Copy link

alterx commented Mar 5, 2013

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