#32 Fixed social logins

master
Keith Irwin 2017-04-19 21:37:00 -04:00
parent 8069dcd1d1
commit c71c1fd1c1
No known key found for this signature in database
GPG Key ID: 378933C743E2BBC0
6 changed files with 148 additions and 68 deletions

View File

@ -5,6 +5,9 @@ const
GoogleStrategy = require('passport-google-oauth20').Strategy,
FacebookStrategy = require('passport-facebook').Strategy,
TwitterStrategy = require('passport-twitter').Strategy,
GoogleTokenStrategy = require('passport-google-id-token'),
FacebookTokenStrategy = require('passport-facebook-token'),
TwitterTokenStrategy = require('passport-twitter-token'),
env = require('./env.js'),
mw = require('./middleware.js'),
User = require('./models.js').user;
@ -34,7 +37,7 @@ module.exports = (passport)=>{
// No user with that email
if (!user) {
req.session.next = undefined;
return done( null, false, req.flash('danger','Incorrect email or password.') );
return done( null, false, req.flash('warning','Incorrect email or password.') );
}
// User exists
@ -47,7 +50,7 @@ module.exports = (passport)=>{
// Password incorrect
if (!res) {
req.session.next = undefined;
return done( null, false, req.flash('danger','Incorrect email or password.') );
return done( null, false, req.flash('warning','Incorrect email or password.') );
}
// Successful login
@ -65,41 +68,55 @@ module.exports = (passport)=>{
// Social login
function socialLogin(req, service, profileId, done) {
// console.log(`socialLogin() called`);
let query = {};
query['auth.'+service] = profileId;
// Log in
// Intent to log in
if (!req.user) {
// console.log(`Logging in with ${service}.`);
var query = {};
query['auth.'+service] = profileId;
User.findOne(query, (err,user)=>{
if (err){ return done(err); }
// console.log(`Logging in with ${service}...`);
User.findOne(query)
.then( (user)=>{
// Can't find user
else if (!user){
if (!user){
// Lazy update from old googleId field
if (service==='google') {
User.findOne( {'googleID':parseInt(profileId)}, (err,user)=>{
if (err){ return done(err); }
User.findOne({ 'googleID': parseInt(profileId) })
.then( (user)=>{
// User exists with old schema
if (user) {
user.auth.google = profileId;
user.googleId = null;
user.save( (err)=>{
if (err){ mw.throwErr(err,req); }
else { console.info(`🗂️ Lazily updated schema for ${user.name}.`); }
user.googleId = undefined;
user.save()
.then( ()=>{
console.info(`🗂️ Lazily updated schema for ${user.name}.`);
return done(null, user);
} );
} else {
req.flash('danger',`There's no user for that ${service} account. `);
})
.catch( (err)=>{
mw.throwErr(err,req);
return done(err);
});
}
// No such user
else {
req.flash('warning',`There's no user for that ${service} account. `);
return done();
}
} );
})
.catch ( (err)=>{
mw.throwErr(err,req);
return done(err);
});
}
// No googleId either
else {
req.flash('danger',`There's no user for that ${service} account. `);
req.flash('warning',`There's no user for that ${service} account. `);
return done();
}
}
@ -109,17 +126,49 @@ module.exports = (passport)=>{
// console.log(`Found user: ${user}`);
return done(null, user);
}
})
.catch( (err)=>{
mw.throwErr(err,req);
return done(err);
});
}
// Connect account
// Intent to connect account
else {
// console.log(`Connecting ${service} account.`);
req.user.auth[service] = profileId;
req.user.save( (err)=>{
if (err){ return done(err); }
else { return done(null, req.user); }
} );
// console.log(`Connecting ${service} account...`);
// Check for unique profileId
User.findOne(query)
.then( (existingUser)=>{
// Social account already in use
if (existingUser) {
req.flash('warning',`Another user is already connected to that ${service} account. `);
return done();
}
// Connect to account
else {
// console.log(`Connecting ${service} account.`);
req.user.auth[service] = profileId;
req.user.save()
.then( ()=>{
req.flash('success', `${mw.capitalize(service)} account connected. `);
return done(null,req.user);
} )
.catch( (err)=>{
mw.throwErr(err);
return done(err);
} );
}
})
.catch( (err)=>{
mw.throwErr(err,req);
return done(err);
})
}
}
@ -133,6 +182,12 @@ module.exports = (passport)=>{
}, (req, accessToken, refreshToken, profile, done)=>{
socialLogin(req, 'google', profile.id, done);
}
)).use('google-token', new GoogleTokenStrategy({
clientID: env.googleClientId,
passReqToCallback: true
}, (req, parsedToken, googleId, done)=>{
socialLogin(req,'google', googleId, done);
}
));
// Facebook
@ -144,6 +199,13 @@ module.exports = (passport)=>{
}, (req, accessToken, refreshToken, profile, done)=>{
socialLogin(req, 'facebook', profile.id, done);
}
)).use('facebook-token', new FacebookTokenStrategy({
clientID: env.facebookAppId,
clientSecret: env.facebookAppSecret,
passReqToCallback: true
}, (req, accessToken, refreshToken, profile, done)=>{
socialLogin(req,'facebook', profile.id, done);
}
));
// Twitter
@ -155,6 +217,13 @@ module.exports = (passport)=>{
}, (req, token, tokenSecret, profile, done)=>{
socialLogin(req, 'twitter', profile.id, done);
}
)).use('twitter-token', new TwitterTokenStrategy({
consumerKey: env.twitterConsumerKey,
consumerSecret: env.twitterConsumerSecret,
passReqToCallback: true
}, (req, token, tokenSecret, profile, done)=>{
socialLogin(req,'twitter', profile.id, done);
}
));
return passport;

View File

@ -14,15 +14,13 @@ module.exports = (app, passport) => {
loginOutcome = {
failureRedirect: '/login',
failureFlash: true
},
connectOutcome = {
failureRedirect: '/settings',
failureFlash: true
},
},
loginCallback = (req,res)=>{
// console.log(`Login callback called... redirecting to ${req.session.next}`);
req.flash('success',"You have been logged in.");
res.redirect( req.session.next || '/map' );
},
androidLoginCallback = (req,res)=>{
appLoginCallback = (req,res)=>{
if (req.user){ res.send(req.user); }
else { res.sendStatus(401); }
};
@ -212,7 +210,8 @@ module.exports = (app, passport) => {
req.flash('success', `If an account exists with the email <u>${req.body.email}</u>, an email has been sent there with a password reset link. `);
res.redirect('/login');
}).catch((err)=>{
mw.throwErr(err);
mw.throwErr(err,req);
res.redirect('/login');
});
});
@ -225,50 +224,59 @@ module.exports = (app, passport) => {
});
} );
// Android
app.get('/login/app/', passport.authenticate('local'), appLoginCallback);
// Token-based
app.get(['/login/app/google','/auth/google/idtoken'], passport.authenticate('google-token'), appLoginCallback);
app.get('/login/app/facebook', passport.authenticate('facebook-token'), appLoginCallback);
app.get('/login/app/twitter', passport.authenticate('twitter-token'), appLoginCallback);
// Social
app.get('/login/:service', (req,res,next)=>{
let service = req.params.service,
sendParams = (service==='google')? {scope:['profile']} : null;
sendParams = (service==='google')?{scope:['https://www.googleapis.com/auth/userinfo.profile']}:null;
// Social login
if (!req.user) {
// console.log(`Attempting to login with ${service} with params: ${JSON.stringify(sendParams)}...`);
passport.authenticate(service, sendParams)(req,res,next);
}
// Connect social account
else if (!req.user.auth[service]) {
// console.log(`Attempting to connect ${service} account...`);
passport.authorize(service, sendParams)(req,res,next);
}
// Disconnect social account
else {
// console.log(`Attempting to disconnect ${service} account...`);
req.user.auth[service] = undefined;
req.user.save()
.catch((err)=>{
mw.throwErr(err,req);
res.redirect('/settings');
}).then(()=>{
req.flash('success', `${mw.capitalize(service)} account disconnected. `);
res.redirect('/settings');
});
.then(()=>{
req.flash('success', `${mw.capitalize(service)} account disconnected. `);
res.redirect('/settings');
})
.catch((err)=>{
mw.throwErr(err,req);
res.redirect('/settings');
});
}
});
app.get('/login/:service/cb', (req,res,next)=>{
var service = req.params.service;
if (!req.user) {
passport.authenticate(service, loginOutcome)(req,res,next);
} else {
req.flash('success', `${mw.capitalize(service)} account connected. `);
passport.authenticate(service, connectOutcome)(req,res,next);
}
}, loginCallback);
// Android
app.get('/login/android/', passport.authenticate('local'), androidLoginCallback);
app.get('/login/android/google', passport.authenticate('google-id-token'), androidLoginCallback);
//TODO: Add android facebook login
//TODO: Add android twitter login
app.get('/login/google/cb',
passport.authenticate('google',loginOutcome),
loginCallback
);
app.get('/login/facebook/cb',
passport.authenticate('facebook',loginOutcome),
loginCallback
);
app.get('/login/twitter/cb',
passport.authenticate('twitter',loginOutcome),
loginCallback
);
};

View File

@ -31,10 +31,10 @@ router.route('/')
function setSettings(){
// Set values
req.user.name = xss(req.body.name);
req.user.slug = slug(xss(req.body.slug));
req.user.settings = {
// Set values
req.user.name = xss(req.body.name);
req.user.slug = slug(xss(req.body.slug));
req.user.settings = {
units: req.body.units,
defaultMap: req.body.map,
defaultZoom: req.body.zoom,

View File

@ -24,11 +24,12 @@
"nunjucks": "^2.3.0",
"passport": "^0.3.2",
"passport-facebook": "^2.1.1",
"passport-facebook-token": "^3.3.0",
"passport-google-id-token": "^0.4.0",
"passport-google-oauth2": "^0.1.6",
"passport-google-oauth20": "^1.0.0",
"passport-local": "^1.0.0",
"passport-twitter": "^1.0.4",
"passport-twitter-token": "^1.3.0",
"slug": "^0.9.1",
"socket.io": "^1.4.4",
"xss": "^0.3.3"

View File

@ -81,7 +81,8 @@ const
// Path for redirects
let nextPath = ( req.path.substring(0, req.path.indexOf('#')) || req.path );
if ( nextPath!=='/login' && nextPath!=='/logout' ){
if ( nextPath.substring(0,6)!=='/login' && nextPath.substring(0,7)!=='/logout' ){
console.log(`Setting redirect path to "${nextPath}#"`);
req.session.next = nextPath+'#';
}

View File

@ -12,12 +12,13 @@
<h3>Display your realtime GPS location on a map</h3>
{% if user %}
<a class='btn' href="/map">Map<i class='fa fa-angle-right'></i></a>
<a class='btn' href="/settings">Settings<i class='fa fa-angle-right'></i></a>
{% if user.isAdmin %}
<a class='btn' href="/admin">Admin<i class='fa fa-angle-right'></i></a>
{% endif %}
{% else %}
<a class='btn' href="/map/keith">View example<i class='fa fa-angle-right'></i></a>
<a class='btn' href="/login#signup">Join<i class='fa fa-angle-down'></i></a>
<a class='btn' href="/login#signup">Join<i class='fa fa-angle-right'></i></a>
<a class='btn' href="/login#login">Login<i class='fa fa-angle-right'></i></a>
{% endif %}
</div>