Skip to content

Commit

Permalink
Added Google, Facebook, and Email API endpoints for authentication, r…
Browse files Browse the repository at this point in the history
…efactored an extensive amount of code
  • Loading branch information
niftylettuce committed Oct 17, 2014
1 parent 8864955 commit 750b328
Show file tree
Hide file tree
Showing 32 changed files with 605 additions and 181 deletions.
6 changes: 5 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"it",
"console",
"describe",
"after",
"before",
"beforeEach",
"waits",
"waitsFor",
Expand Down Expand Up @@ -46,5 +48,7 @@
"sub": true,
"strict": false,
"white": false,
"asi": false
"asi": false,

"expr": true
}
27 changes: 27 additions & 0 deletions app/controllers/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

// # api

exports = module.exports = function() {

function login(req, res, next) {
res.json(req.user);
}

function updateUser(req, res, next) {
// we are simply re-using eskimo's default
// user update method in the users controller
// that ships with eskimo; just to save time
// we are manually setting the id param here
// and then calling `next()` to continue along
req.params.id = req.user.id;
next();
}

return {
login: login,
updateUser: updateUser
};

};

exports['@singleton'] = true;
59 changes: 17 additions & 42 deletions app/controllers/signup.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,19 @@

// # signup

var passport = require('passport');
var util = require('util');
var _ = require('underscore');
var _str = require('underscore.string');
_.mixin(_str.exports());

var validator = require('validator');
var randomstring = require('randomstring-extended');

exports = module.exports = function(settings, logger, email, User) {
exports = module.exports = function(settings, User) {

function create(req, res, next) {

// email
if (_.isBlank(req.body.email)) {
return next({
message: 'Email was blank',
param: 'email'
});
}

// name
if (_.isBlank(req.body.name)) {
return next({
message: 'First name was blank',
param: 'name'
});
}

// surname
if (_.isBlank(req.body.surname)) {
return next({
message: 'Last name was blank',
param: 'surname'
});
}

// password validation
User.validatePassword(req.body.password, function(errorMessage) {

Expand All @@ -60,23 +38,20 @@ exports = module.exports = function(settings, logger, email, User) {
return next(err);
}

if (!user) {
return next(new Error('An error has occured while registering, please try later'));
}

req.flash('success', 'Successfully signed up, check your inbox soon for a welcome email');
next();

// Send welcome email here
email('welcome', {
user: user,
url: settings.url
}, {
to: user.full_email,
subject: 'Eskimo - Welcome to Eskimo'
}, function(err, responseStatus) {
if (err) {
return logger.error(err);
}

logger.info('Sent welcome email to %s', user.email);
});

passport.authenticate('local', {
successReturnToOrRedirect: '/',
successFlash: true,
failureFlash: true,
failureRedirect: true
})(req, res, next);

user.sendWelcomeEmail();

}

Expand All @@ -88,4 +63,4 @@ exports = module.exports = function(settings, logger, email, User) {
};

exports['@singleton'] = true;
exports['@require'] = [ 'igloo/settings', 'igloo/logger', 'igloo/email', 'models/user' ];
exports['@require'] = [ 'igloo/settings', 'models/user' ];
47 changes: 44 additions & 3 deletions app/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ var strength = require('strength');
var passportLocalMongoose = require('passport-local-mongoose');
var validator = require('validator');
var mongoosePaginate = require('mongoose-paginate');
var randomstring = require('randomstring-extended');

exports = module.exports = function(settings, mongoose, iglooMongoosePlugin) {
exports = module.exports = function(settings, mongoose, iglooMongoosePlugin, email, logger) {

var nameType = {
type: String,
Expand All @@ -33,7 +34,24 @@ exports = module.exports = function(settings, mongoose, iglooMongoosePlugin) {
name: nameType,
surname: nameType,
reset_token: String,
reset_at: Date
reset_at: Date,
api_token: String,
facebook_id: String,
facebook_access_token: String,
facebook_refresh_token: String,
google_id: String,
google_access_token: String,
google_refresh_token: String
});

// pre save
User.pre('save', function(next) {
var user = this;
// set an API token for the user
if (!user.api_token) {
user.api_token = randomstring.token(32);
}
next();
});

// virtuals
Expand All @@ -52,6 +70,29 @@ exports = module.exports = function(settings, mongoose, iglooMongoosePlugin) {
return util.format('%s %s <%s>', user.name, user.surname, user.email);
});

// methods
User.methods.sendWelcomeEmail = function sendWelcomeEmail(callback) {

var user = this;

email('welcome', {
user: user,
url: settings.url
}, {
to: user.full_email,
subject: util.format('Eskimo - Welcome %s!', user.name)
}, function(err, responseStatus) {
if (_.isFunction(callback)) {
return callback(err, responseStatus);
}
if (err) {
return logger.error(err);
}
logger.info('Sent welcome email to %s', user.email);
});

};

// statics
User.static('validatePassword', function(password, callback) {

Expand Down Expand Up @@ -100,4 +141,4 @@ exports = module.exports = function(settings, mongoose, iglooMongoosePlugin) {
};

exports['@singleton'] = true;
exports['@require'] = [ 'igloo/settings', 'igloo/mongo', 'igloo/mongoose-plugin' ];
exports['@require'] = [ 'igloo/settings', 'igloo/mongo', 'igloo/mongoose-plugin', 'igloo/email', 'igloo/logger' ];
2 changes: 2 additions & 0 deletions app/views/layout.jade
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ html.no-js
script(src='/bower/bootbox/bootbox.js')
script(src='/js/plugins.js')
script(src='/js/main.js')
if settings.facebook.enabled
script(src='/js/fb-appended-hash-bug-fix.js')
<!-- endbuild -->
12 changes: 11 additions & 1 deletion app/views/login.jade
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ block content
.container
.row
.col-md-4.col-md-offset-4
h1 Log in
if settings.facebook.enabled
a(href='/auth/facebook').btn.btn-lg.btn-primary.btn-block
i.fa.fa-facebook-square
| Log in with Facebook
hr
if settings.google.enabled
a(href='/auth/google').btn.btn-lg.btn-primary.btn-block
i.fa.fa-google-plus
| Log in with Google
hr
h1.text-center Log in
form(action='/login', method='POST')
input(type='hidden', name='_csrf', value=csrf)
.form-group
Expand Down
1 change: 1 addition & 0 deletions app/views/my-account.jade
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ block content
h1 My Account
h3 Email: #{req.user.email}
h3 Name: #{req.user.full_name}
h3 API Token: #{req.user.api_token}
12 changes: 11 additions & 1 deletion app/views/signup.jade
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ block content
.container
.row
.col-md-4.col-md-offset-4
h1 Sign up
if settings.facebook.enabled
a(href='/auth/facebook').btn.btn-lg.btn-primary.btn-block
i.fa.fa-facebook-square
| Sign up with Facebook
hr
if settings.google.enabled
a(href='/auth/google').btn.btn-lg.btn-primary.btn-block
i.fa.fa-google-plus
| Sign up with Google
hr
h1.text-center Sign up
form(action='/signup', method='POST', autocomplete='off')
input(type='hidden', name='_csrf', value=csrf)
.form-group
Expand Down
22 changes: 22 additions & 0 deletions assets/public/js/fb-appended-hash-bug-fix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

(function(window) {

// Remove the ugly Facebook appended hash
// <https://github.com/jaredhanson/passport-facebook/issues/12>
(function removeFacebookAppendedHash() {
if (!window.location.hash || window.location.hash !== '#_=_')
return;
if (window.history && window.history.replaceState)
return window.history.replaceState("", document.title, window.location.pathname);
// Prevent scrolling by storing the page's current scroll offset
var scroll = {
top: document.body.scrollTop,
left: document.body.scrollLeft
};
window.location.hash = "";
// Restore the scroll offset, should be flicker free
document.body.scrollTop = scroll.top;
document.body.scrollLeft = scroll.left;
}());

}(window));
5 changes: 3 additions & 2 deletions assets/public/js/main.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

(function(window, $) {
'use strict';

$(function() {
});
//$(function() {
//});

}(window, jQuery));
1 change: 1 addition & 0 deletions assets/public/js/plugins.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

// Avoid `console` errors in browsers that lack a console.
(function(window) {
'use strict';
Expand Down
15 changes: 15 additions & 0 deletions boot/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ exports = module.exports = function() {
return {

defaults: {
facebook: {
enabled: false,
appID: '',
appSecret: '',
scope: [ 'email' ]
},
google: {
enabled: false,
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'
],
clientID: '',
clientSecret: ''
},
pkg: pkg,
cache: false,
showStack: true,
Expand Down
39 changes: 35 additions & 4 deletions boot/policies.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,54 @@
// app - policies

var connectEnsureLogin = require('connect-ensure-login');
var auth = require('basic-auth');
var _ = require('underscore');

exports = module.exports = function(IoC) {
exports = module.exports = function(IoC, User) {

// policy/middleware helpers
var ensureLoggedIn = connectEnsureLogin.ensureLoggedIn;
var ensureLoggedOut = connectEnsureLogin.ensureLoggedOut;

// Here is where you'd have things like isAdmin or isMember, for example
// since there are issues with `passport-http` right now
// this is implemented as a temporary solution
function ensureApiToken(req, res, next) {

var creds = auth(req);

if (!creds || !_.isString(creds.name)) {
res.statusCode = 401;
return next({
message: 'API token missing',
param: 'username'
});
}

User.findOne({
api_token: creds.name
}, function(err, user) {
if (err) return next(err);
if (!user) {
return next({
message: 'Invalid API token provided',
param: 'username'
});
}
req.user = user;
next();
});

}

var policies = {
ensureLoggedIn: ensureLoggedIn,
ensureLoggedOut: ensureLoggedOut
ensureLoggedOut: ensureLoggedOut,
ensureApiToken: ensureApiToken
};

return policies;

};

exports['@singleton'] = true;
exports['@require'] = [ '$container' ];
exports['@require'] = [ '$container', 'models/user' ];
Loading

0 comments on commit 750b328

Please sign in to comment.