Skip to content
This repository has been archived by the owner on Aug 30, 2021. It is now read-only.

Commit

Permalink
Finalizing the User Accounts Module
Browse files Browse the repository at this point in the history
  • Loading branch information
amoshaviv committed Apr 15, 2014
1 parent 766e628 commit 279eb39
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 131 deletions.
169 changes: 94 additions & 75 deletions app/controllers/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ var mongoose = require('mongoose'),
User = mongoose.model('User'),
_ = require('lodash');

/**
* Get the error message from error object
*/
var getErrorMessage = function(err) {
var message = '';

Expand Down Expand Up @@ -153,7 +156,6 @@ exports.changePassword = function(req, res, next) {
});
}
});

} else {
res.send(400, {
message: 'Passwords do not match'
Expand Down Expand Up @@ -197,17 +199,16 @@ exports.me = function(req, res) {
*/
exports.oauthCallback = function(strategy) {
return function(req, res, next) {
passport.authenticate(strategy, function(err, user, email) {
passport.authenticate(strategy, function(err, user, redirectURL) {
if (err || !user) {
console.log(err);
return res.redirect('/#!/signin');
}
req.login(user, function(err) {
if (err) {
return res.redirect('/#!/signin');
}

return res.redirect('/');
return res.redirect(redirectURL || '/');
});
})(req, res, next);
};
Expand Down Expand Up @@ -250,94 +251,112 @@ exports.hasAuthorization = function(req, res, next) {
};

/**
* Helper function to save or update a user.
* When the user is logged in, it joins the user data to the existing one.
* Otherwise it creates a new user.
*
* @author Kentaro Wakayama
*
* @date 2014-04-09
*
* @param {Object} req This is the request object which contains the user when he is signed in.
* @param {String} token This is the accesstoken.
* @param {String} tokenSecret This is the refreshtoken.
* @param {Object} profile This is the user profile of the current provider.
* @param {Function} done Callback to supply Passport with the user that authenticated.
*
* @param {Object} providerData This Object contains all data which is specific for the provider
* @param {String} providerData.provider This is the passport provider name.
* @param {String} providerData.idKey This is the Key / Attribute name for saving / retrieving the provider id.
* @param {String} providerData.name This is the user's name.
* @param {String} [providerData.email] This is the user's email.
* @param {String} providerData.username This is the user's username.
*
* @return {[type]} [description]
* Helper function to save or update a OAuth user profile
*/
exports.saveOrUpdate = function(req, token, tokenSecret, profile, done, providerData) {
var provider = providerData.provider;
var idKey = providerData.idKey;
var searchProviderKey = provider + '.' + idKey;
var searchObject = {};
searchObject[searchProviderKey] = profile.id;

exports.saveOAuthUserProfile = function(req, providerUserProfile, done) {
if (!req.user) {
// no user active, this is a fresh login
User.findOne(searchObject, function(err, user) {
// Define a search query fields
var searchMainProviderIdentifierField = 'providerData.' + providerUserProfile.providerIdentifierField;
var searchAdditionalProviderIdentifierField = 'additionalProvidersData.' + providerUserProfile.provider + '.' + providerUserProfile.providerIdentifierField;

// Define main provider search query
var mainProviderSearchQuery = {};
mainProviderSearchQuery.provider = providerUserProfile.provider;
mainProviderSearchQuery[searchMainProviderIdentifierField] = providerUserProfile.providerData[providerUserProfile.providerIdentifierField];

// Define additional provider search query
var additionalProviderSearchQuery = {};
additionalProviderSearchQuery[searchAdditionalProviderIdentifierField] = providerUserProfile.providerData[providerUserProfile.providerIdentifierField];

// Define a search query to find existing user with current provider profile
var searchQuery = {
$or: [mainProviderSearchQuery, additionalProviderSearchQuery]
};

User.findOne(searchQuery, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
} else {
if (!user) {
var possibleUsername = providerUserProfile.username || ((providerUserProfile.email) ? providerUserProfile.email.split('@')[0] : '');

var possibleUsername = '';
if (providerData.email) {
possibleUsername = providerData.email.split('@')[0];
User.findUniqueUsername(possibleUsername, null, function(availableUsername) {
user = new User({
firstName: providerUserProfile.firstName,
lastName: providerUserProfile.lastName,
username: availableUsername,
displayName: providerUserProfile.displayName,
email: providerUserProfile.email,
provider: providerUserProfile.provider,
providerData: providerUserProfile.providerData
});

// And save the user
user.save(function(err) {
return done(err, user);
});
});
} else {
possibleUsername = profile.username;
return done(err, user);
}
}
});
} else {
// User is already logged in, join the provider data to the existing user
User.findById(req.user.id, function(err, user) {
if (err) {
return done(err);
} else {
// Check if user exists, is not signed in using this provider, and doesn't have that provider data already configured
if (user && user.provider !== providerUserProfile.provider && (!user.additionalProvidersData || !user.additionalProvidersData[providerUserProfile.provider])) {
// Add the provider data to the additional provider data field
if (!user.additionalProvidersData) user.additionalProvidersData = {};
user.additionalProvidersData[providerUserProfile.provider] = providerUserProfile.providerData;

User.findUniqueUsername(possibleUsername, null, function(availableUsername) {
user = new User({
firstName: providerData.firstName,
lastName: providerData.lastName,
username: availableUsername,
displayName: providerData.displayName,
email: providerData.email,
provider: provider,
});
// Then tell mongoose that we've updated the additionalProvidersData field
user.markModified('additionalProvidersData');

user[provider] = profile._json;
user[provider].token = token;
user[provider].tokenSecret = tokenSecret;
// And save the user
user.save(function(err) {
if (err) console.log(err);
return done(err, user);
return done(err, user, '/#!/settings/accounts');
});
});
} else {
user[provider].token = token;
user[provider].tokenSecret = tokenSecret;
user.save(function(err) {
if (err) console.log(err);
} else {
return done(err, user);
});
}
}
});
} else {
// a user is already logged in, join the provider data to the existing user.
User.findById( req.user._id, function(err, user) {
}
};

/**
* Remove OAuth provider
*/
exports.removeOAuthProvider = function(req, res, next) {
var user = req.user;
var provider = req.param('provider');

if (user && provider) {
// Delete the additional provider
if (user.additionalProvidersData[provider]) {
delete user.additionalProvidersData[provider];

// Then tell mongoose that we've updated the additionalProvidersData field
user.markModified('additionalProvidersData');
}

user.save(function(err) {
if (err) {
return done(err);
}
if (user) {
user[provider] = profile._json;
user[provider].token = token;
user[provider].tokenSecret = tokenSecret;
user.save(function(err) {
if (err) console.log(err);
return done(err, user);
return res.send(400, {
message: getErrorMessage(err)
});
} else {
return done(err, user);
req.login(user, function(err) {
if (err) {
res.send(400, err);
} else {
res.jsonp(user);
}
});
}
});
}
Expand Down
8 changes: 2 additions & 6 deletions app/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,14 @@ var UserSchema = new Schema({
required: 'Provider is required'
},
providerData: {},
additionalProvidersData: {},
updated: {
type: Date
},
created: {
type: Date,
default: Date.now
},
facebook: {},
twitter: {},
github: {},
google: {},
linkedin: {}
}
});

/**
Expand Down
1 change: 1 addition & 0 deletions app/routes/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = function(app) {
app.get('/users/me', users.me);
app.put('/users', users.update);
app.post('/users/password', users.changePassword);
app.del('/users/accounts', users.removeOAuthProvider);

// Setting up the users api
app.post('/auth/signup', users.signup);
Expand Down
18 changes: 9 additions & 9 deletions config/env/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ module.exports = {
title: 'MEAN.JS - Development Environment'
},
facebook: {
clientID: 'APP_ID',
clientSecret: 'APP_SECRET',
callbackURL: 'http://localhost:3000/auth/facebook/callback'
clientID: '588647347851720',
clientSecret: 'd2870185a0b41ab0ec32ac9d023be5b0',
callbackURL: 'http://local.meanjs.herokuapp.com:3000/auth/facebook/callback'
},
twitter: {
clientID: 'CONSUMER_KEY',
clientSecret: 'CONSUMER_SECRET',
clientID: 'f9JcCc0xSzEUkwF5E5ZKLQ',
clientSecret: 'E9zzKZhZlZuy5T1qMsu3c75EkGf9yVwp0uAIOwtI0oM',
callbackURL: 'http://localhost:3000/auth/twitter/callback'
},
google: {
clientID: 'APP_ID',
clientSecret: 'APP_SECRET',
clientID: '751147574067-kt9q7nnkvns3b8cg742nsddk9d77k0bt.apps.googleusercontent.com',
clientSecret: '-7acCDhnsbf22HoHB_8CkAHi',
callbackURL: 'http://localhost:3000/auth/google/callback'
},
linkedin: {
clientID: 'APP_ID',
clientSecret: 'APP_SECRET',
clientID: '77f1ywm1byjwpm',
clientSecret: 'K6D9cufcuNIjcqUr',
callbackURL: 'http://localhost:3000/auth/linkedin/callback'
}
};
16 changes: 11 additions & 5 deletions config/strategies/facebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

var passport = require('passport'),
FacebookStrategy = require('passport-facebook').Strategy,
User = require('mongoose').model('User'),
config = require('../config'),
users = require('../../app/controllers/users');

Expand All @@ -15,18 +14,25 @@ module.exports = function() {
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
// Set the provider data and include tokens
var providerData = profile._json;
providerData.accessToken = accessToken;
providerData.refreshToken = refreshToken;

var providerData = {
// Create the user OAuth profile
var providerUserProfile = {
firstName: profile.name.givenName,
lastName: profile.name.familyName,
displayName: profile.displayName,
provider: 'facebook',
idKey: 'id',
email: profile.emails[0].value,
username: profile.username,
provider: 'facebook',
providerIdentifierField: 'id',
providerData: providerData
};
users.saveOrUpdate(req, accessToken, refreshToken, profile, done, providerData);

// Save the user OAuth profile
users.saveOAuthUserProfile(req, providerUserProfile, done);
}
));
};
20 changes: 13 additions & 7 deletions config/strategies/google.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

var passport = require('passport'),
GoogleStrategy = require('passport-google-oauth').OAuth2Strategy,
User = require('mongoose').model('User'),
config = require('../config'),
users = require('../../app/controllers/users');

Expand All @@ -15,18 +14,25 @@ module.exports = function() {
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {

var providerData = {
// Set the provider data and include tokens
var providerData = profile._json;
providerData.accessToken = accessToken;
providerData.refreshToken = refreshToken;

// Create the user OAuth profile
var providerUserProfile = {
firstName: profile.name.givenName,
lastName: profile.name.familyName,
displayName: profile.displayName,
provider: 'google',
idKey: 'id',
email: profile.emails[0].value,
username: profile.username
username: profile.username,
provider: 'google',
providerIdentifierField: 'id',
providerData: providerData
};
users.saveOrUpdate(req, accessToken, refreshToken, profile, done, providerData);

// Save the user OAuth profile
users.saveOAuthUserProfile(req, providerUserProfile, done);
}
));
};
Loading

0 comments on commit 279eb39

Please sign in to comment.