2017-04-16 19:00:39 -06:00
'use strict' ;
const
mw = require ( '../middleware.js' ) ,
mail = require ( '../mail.js' ) ,
User = require ( '../models.js' ) . user ,
crypto = require ( 'crypto' ) ,
env = require ( '../env.js' ) ;
module . exports = ( app , passport ) => {
// Methods for success and failure
const
loginOutcome = {
failureRedirect : '/login' ,
failureFlash : true
2017-04-19 19:37:00 -06:00
} ,
2017-04-16 19:00:39 -06:00
loginCallback = ( req , res ) => {
2017-04-19 19:37:00 -06:00
// console.log(`Login callback called... redirecting to ${req.session.next}`);
2017-04-19 20:03:45 -06:00
req . flash ( req . session . flashType , req . session . flashMessage ) ;
req . session . flashType = undefined ;
req . session . flashMessage = undefined ;
2017-04-18 22:13:57 -06:00
res . redirect ( req . session . next || '/map' ) ;
2017-04-18 22:40:16 -06:00
} ,
2017-04-19 19:37:00 -06:00
appLoginCallback = ( req , res ) => {
2017-04-18 22:40:16 -06:00
if ( req . user ) { res . send ( req . user ) ; }
else { res . sendStatus ( 401 ) ; }
2017-04-16 19:00:39 -06:00
} ;
// Login/-out
app . route ( '/login' )
. get ( ( req , res ) => {
// Already logged in
if ( req . isAuthenticated ( ) ) { loginCallback ( req , res ) ; }
// Show login page
else { res . render ( 'login' ) ; }
} )
. post ( passport . authenticate ( 'local' , loginOutcome ) , loginCallback ) ;
app . get ( '/logout' , ( req , res ) => {
req . logout ( ) ;
req . flash ( 'success' , ` You have been logged out. ` ) ;
2017-04-18 22:13:57 -06:00
res . redirect ( req . session . next || '/' ) ;
2017-04-16 19:00:39 -06:00
} ) ;
// Signup
app . route ( '/signup' )
. get ( ( req , res ) => {
res . redirect ( '/login#signup' ) ;
} )
. post ( ( req , res , next ) => {
// Send token and alert user
function sendToken ( user ) {
// Create a password token
2017-04-18 01:08:57 -06:00
user . createPassToken ( ( err , token ) => {
2017-04-16 19:00:39 -06:00
if ( err ) { mw . throwErr ( err , req ) ; }
// Email the instructions to continue
mail . send ( {
2017-04-18 01:08:57 -06:00
from : mail . from ,
to : ` < ${ user . email } > ` ,
subject : 'Complete your Tracman registration' ,
text : mail . text ( ` Welcome to Tracman! \n \n To complete your registration, follow this link and set your password: \n ${ env . url } /settings/password/ ${ token } ` ) ,
html : mail . html ( ` <p>Welcome to Tracman! </p><p>To complete your registration, follow this link and set your password:<br><a href=" ${ env . url } /settings/password/ ${ token } "> ${ env . url } /settings/password/ ${ token } </a></p> ` )
} )
. then ( ( ) => {
2017-04-16 19:00:39 -06:00
req . flash ( 'success' , ` An email has been sent to <u> ${ user . email } </u>. Check your inbox to complete your registration. ` ) ;
res . redirect ( '/login' ) ;
2017-04-18 01:08:57 -06:00
} )
. catch ( ( err ) => {
2017-04-16 19:00:39 -06:00
mw . throwErr ( err , req ) ;
res . redirect ( '/login#signup' ) ;
} ) ;
2017-04-18 01:08:57 -06:00
2017-04-16 19:00:39 -06:00
} ) ;
}
// Validate email
req . checkBody ( 'email' , 'Please enter a valid email address.' ) . isEmail ( ) ;
req . sanitizeBody ( 'email' ) . normalizeEmail ( { remove _dots : false } ) ;
// Check if somebody already has that email
User . findOne ( { 'email' : req . body . email } )
2017-04-19 20:03:45 -06:00
. then ( ( user ) => {
// User already exists
if ( user && user . auth . password ) {
req . flash ( 'warning' , 'A user with that email already exists! If you forgot your password, you can <a href="/login/forgot">reset it here</a>.' ) ;
res . redirect ( '/login#login' ) ;
next ( ) ;
}
// User exists but hasn't created a password yet
else if ( user ) {
// Send another token (or the same one if it hasn't expired)
sendToken ( user ) ;
}
// Create user
else {
2017-04-16 19:00:39 -06:00
2017-04-19 20:03:45 -06:00
user = new User ( ) ;
user . created = Date . now ( ) ;
user . email = req . body . email ;
user . slug = slug ( user . email . substring ( 0 , user . email . indexOf ( '@' ) ) ) ;
2017-04-16 19:00:39 -06:00
2017-04-19 20:03:45 -06:00
// Generate unique slug
let slug = new Promise ( ( resolve , reject ) => {
( function checkSlug ( s , cb ) {
User . findOne ( { slug : s } )
. then ( ( existingUser ) => {
2017-04-16 19:00:39 -06:00
2017-04-19 20:03:45 -06:00
// Slug in use: generate a random one and retry
if ( existingUser ) {
crypto . randomBytes ( 6 , ( err , buf ) => {
if ( err ) { mw . throwErr ( err , req ) ; }
s = buf . toString ( 'hex' ) ;
checkSlug ( s , cb ) ;
} ) ;
}
2017-04-16 19:00:39 -06:00
2017-04-19 20:03:45 -06:00
// Unique slug: proceed
else { cb ( s ) ; }
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
2017-04-16 19:00:39 -06:00
} ) ;
2017-04-19 20:03:45 -06:00
} ) ( user . slug , ( newSlug ) => {
user . slug = newSlug ;
resolve ( ) ;
2017-04-16 19:00:39 -06:00
} ) ;
2017-04-19 20:03:45 -06:00
} ) ;
// Generate sk32
let sk32 = new Promise ( ( resolve , reject ) => {
crypto . randomBytes ( 32 , ( err , buf ) => {
if ( err ) { mw . throwErr ( err , req ) ; }
user . sk32 = buf . toString ( 'hex' ) ;
resolve ( ) ;
2017-04-16 19:00:39 -06:00
} ) ;
2017-04-19 20:03:45 -06:00
} ) ;
2017-04-16 19:00:39 -06:00
2017-04-19 20:03:45 -06:00
// Save user and send the token by email
Promise . all ( [ slug , sk32 ] )
. then ( ( ) => { user . save ( ) ; } )
. then ( ( ) => { sendToken ( user ) ; } )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( '/login#signup' ) ;
} ) ;
}
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( '/signup' ) ;
} ) ;
2017-04-16 19:00:39 -06:00
} ) ;
// Forgot password
app . route ( '/login/forgot' )
. all ( ( req , res , next ) => {
if ( req . isAuthenticated ( ) ) { loginCallback ( req , res ) ; }
else { next ( ) ; }
} )
. get ( ( req , res , next ) => {
res . render ( 'forgot' ) ;
} )
. post ( ( req , res , next ) => {
// Validate email
req . checkBody ( 'email' , 'Please enter a valid email address.' ) . isEmail ( ) ;
req . sanitizeBody ( 'email' ) . normalizeEmail ( { remove _dots : false } ) ;
User . findOne ( { 'email' : req . body . email } )
. then ( ( user ) => {
// No user with that email
if ( ! user ) {
// Don't let on that no such user exists, to prevent dictionary attacks
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' ) ;
}
// User with that email does exist
else {
// Create reset token
2017-04-18 01:08:57 -06:00
user . createPassToken ( ( err , token ) => {
2017-04-16 19:00:39 -06:00
if ( err ) { next ( err ) ; }
// Email reset link
mail . send ( {
from : mail . from ,
to : mail . to ( user ) ,
subject : 'Reset your Tracman password' ,
text : mail . text ( ` Hi, \n \n Did you request to reset your Tracman password? If so, follow this link to do so: \n ${ env . url } /settings/password/ ${ token } \n \n If you didn't initiate this request, just ignore this email. ` ) ,
html : mail . html ( ` <p>Hi, </p><p>Did you request to reset your Tracman password? If so, follow this link to do so:<br><a href=" ${ env . url } /settings/password/ ${ token } "> ${ env . url } /settings/password/ ${ token } </a></p><p>If you didn't initiate this request, just ignore this email. </p> ` )
} ) . then ( ( ) => {
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 ) => {
2017-04-19 19:37:00 -06:00
mw . throwErr ( err , req ) ;
res . redirect ( '/login' ) ;
2017-04-16 19:00:39 -06:00
} ) ;
} ) ;
}
} ) . catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( '/login/forgot' ) ;
} ) ;
} ) ;
2017-04-19 19:37:00 -06:00
// Android
app . get ( '/login/app/' , passport . authenticate ( 'local' ) , appLoginCallback ) ;
2017-04-19 20:03:45 -06:00
// Token-based (android social)
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 ) ;
2017-04-19 19:37:00 -06:00
2017-04-16 19:00:39 -06:00
// Social
app . get ( '/login/:service' , ( req , res , next ) => {
let service = req . params . service ,
2017-04-19 19:37:00 -06:00
sendParams = ( service === 'google' ) ? { scope : [ 'https://www.googleapis.com/auth/userinfo.profile' ] } : null ;
2017-04-16 19:00:39 -06:00
// Social login
if ( ! req . user ) {
2017-04-19 19:37:00 -06:00
// console.log(`Attempting to login with ${service} with params: ${JSON.stringify(sendParams)}...`);
2017-04-16 19:00:39 -06:00
passport . authenticate ( service , sendParams ) ( req , res , next ) ;
}
// Connect social account
else if ( ! req . user . auth [ service ] ) {
2017-04-19 19:37:00 -06:00
// console.log(`Attempting to connect ${service} account...`);
2017-04-16 19:00:39 -06:00
passport . authorize ( service , sendParams ) ( req , res , next ) ;
}
// Disconnect social account
else {
2017-04-19 19:37:00 -06:00
// console.log(`Attempting to disconnect ${service} account...`);
2017-04-16 19:00:39 -06:00
req . user . auth [ service ] = undefined ;
req . user . save ( )
2017-04-19 19:37:00 -06:00
. then ( ( ) => {
req . flash ( 'success' , ` ${ mw . capitalize ( service ) } account disconnected. ` ) ;
res . redirect ( '/settings' ) ;
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( '/settings' ) ;
} ) ;
2017-04-16 19:00:39 -06:00
}
2017-04-19 19:37:00 -06:00
2017-04-16 19:00:39 -06:00
} ) ;
2017-04-19 20:03:45 -06:00
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 ) ;
2017-04-16 19:00:39 -06:00
} ;