2017-03-15 02:01:55 -06:00
'use strict' ;
2017-04-09 21:40:08 -06:00
const
mw = require ( './middleware.js' ) ,
2017-04-01 11:03:05 -06:00
mail = require ( './mail.js' ) ,
User = require ( './models.js' ) . user ,
2017-04-13 16:53:18 -06:00
slug = require ( 'slug' ) ,
crypto = require ( 'crypto' ) ,
2017-04-01 11:03:05 -06:00
env = require ( './env.js' ) ;
2016-03-31 17:06:21 -06:00
2017-04-13 16:53:18 -06:00
module . exports = ( app , passport ) => {
2017-04-01 11:03:05 -06:00
// Methods for success and failure
2017-04-09 21:40:08 -06:00
const
loginOutcome = {
2017-04-12 17:39:39 -06:00
failureRedirect : '/login' ,
failureFlash : true
2017-04-13 22:45:25 -06:00
} ,
2017-04-09 21:40:08 -06:00
connectOutcome = {
2017-04-12 17:39:39 -06:00
failureRedirect : '/settings' ,
failureFlash : true
} ,
2017-04-13 16:53:18 -06:00
loginCallback = ( req , res ) => {
2017-04-13 22:45:25 -06:00
res . redirect ( req . session . next || '/map' ) ;
2017-04-09 21:40:08 -06:00
} ;
2017-04-13 22:45:25 -06:00
2017-04-01 11:03:05 -06:00
// Login/-out
app . route ( '/login' )
2017-04-13 16:53:18 -06:00
. get ( ( req , res ) => {
2017-04-13 22:45:25 -06:00
if ( req . isAuthenticated ( ) ) { loginCallback ( ) ; }
2017-04-01 11:03:05 -06:00
else { res . render ( 'login' ) ; }
} )
. post ( passport . authenticate ( 'local' , loginOutcome ) , loginCallback ) ;
2017-04-13 16:53:18 -06:00
app . get ( '/logout' , ( req , res ) => {
2017-04-01 11:03:05 -06:00
req . logout ( ) ;
2017-04-12 17:39:39 -06:00
req . flash ( 'success' , ` You have been logged out. ` ) ;
2017-04-13 22:45:25 -06:00
res . redirect ( req . session . next || '/' ) ;
2017-04-01 11:03:05 -06:00
} ) ;
2017-04-13 22:45:25 -06:00
2017-04-01 11:03:05 -06:00
// Signup
2017-04-13 16:53:18 -06:00
app . get ( '/signup' , ( req , res ) => {
res . redirect ( '/login#signup' ) ;
2017-04-13 23:00:18 -06:00
} )
. post ( '/signup' , ( req , res , next ) => {
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Send token and alert user
function sendToken ( user ) {
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Create a password token
2017-04-13 22:45:25 -06:00
user . createToken ( ( err , token ) => {
2017-04-13 16:53:18 -06:00
if ( err ) { mw . throwErr ( err , req ) ; }
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Email the instructions to continue
mail . send ( {
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> ` )
2017-04-13 22:45:25 -06:00
} ) . catch ( ( err ) => {
2017-04-13 16:53:18 -06:00
mw . throwErr ( err , req ) ;
res . redirect ( '/login#signup' ) ;
2017-04-13 22:45:25 -06:00
} ) . then ( ( ) => {
2017-04-13 16:53:18 -06:00
req . flash ( 'success' , ` An email has been sent to <u> ${ user . email } </u>. Check your inbox to complete your registration. ` ) ;
2017-04-13 22:45:25 -06:00
res . redirect ( '/login' ) ;
2017-04-13 16:53:18 -06:00
} ) ;
} ) ;
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
}
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Check if somebody already has that email
User . findOne ( { 'email' : req . body . email } , ( err , user ) => {
if ( err ) { mw . throwErr ( err , req ) ; }
2017-04-13 22:45:25 -06:00
2017-04-01 11:03:05 -06:00
// User already exists
2017-04-13 16:53:18 -06:00
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 ( ) ;
}
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// 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 ) ;
}
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Create user
else {
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -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-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Generate unique slug
var generateSlug = new Promise ( ( resolve , reject ) => {
( function checkSlug ( s , cb ) {
2017-04-13 22:45:25 -06:00
User . findOne ( { slug : s } )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
} )
. then ( ( existingUser ) => {
2017-04-13 16:53:18 -06:00
// Slug in use: generate a random one and retry
if ( existingUser ) {
s = '' ;
while ( s . length < 6 ) {
s += 'abcdefghijkmnpqrtuvwxy346789' . charAt ( Math . floor ( Math . random ( ) * 28 ) ) ;
}
checkSlug ( s , cb ) ;
2017-04-13 22:45:25 -06:00
}
2017-04-13 16:53:18 -06:00
// Unique slug: proceed
2017-04-13 22:45:25 -06:00
else { cb ( s ) ; }
2017-04-13 16:53:18 -06:00
} ) ;
2017-04-13 22:45:25 -06:00
} ) ( user . slug , ( newSlug ) => {
2017-04-13 16:53:18 -06:00
user . slug = newSlug ;
resolve ( ) ;
} ) ;
} ) ;
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Generate sk32
var generateSk32 = new Promise ( ( resolve , reject ) => {
crypto . randomBytes ( 32 , ( err , buf ) => {
if ( err ) { mw . throwErr ( err , req ) ; }
user . sk32 = buf . toString ( 'hex' ) ;
resolve ( ) ;
} ) ;
} ) ;
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Save user and send the token by email
Promise . all ( [ generateSlug , generateSk32 ] )
2017-04-13 22:45:25 -06:00
. catch ( err => {
mw . throwErr ( err , req ) ;
} ) . then ( ( ) => {
user . save ( ( err ) => {
if ( err ) { mw . throwErr ( err , req ) ; }
sendToken ( user ) ;
} ) ;
2017-04-01 11:03:05 -06:00
} ) ;
2017-04-13 22:45:25 -06:00
2017-04-01 11:03:05 -06:00
}
2017-04-13 22:45:25 -06:00
2017-04-01 11:03:05 -06:00
} ) ;
} ) ;
2017-04-13 16:53:18 -06:00
2017-04-01 11:03:05 -06:00
// Forgot password
2017-04-13 23:00:18 -06:00
app . route ( '/login/forgot' )
. all ( ( req , res , next ) => {
if ( req . isAuthenticated ( ) ) { loginCallback ( ) ; }
else { next ( ) ; }
} )
. get ( ( req , res , next ) => {
res . render ( 'forgot' ) ;
} )
. post ( ( req , res , next ) => {
//TODO: Validate and sanitize email
// req.assert('email', 'Please enter a valid email address.').isEmail();
// req.sanitize('email').normalizeEmail({ remove_dots: false });
User . findOne ( { 'email' : req . body . email } , ( err , user ) => {
if ( err ) { mw . throwErr ( err ) ; }
// 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 exists
else {
// Create reset token
user . createToken ( ( err , token ) => {
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 ) => {
mw . throwErr ( err ) ;
} ) ;
2017-04-13 16:53:18 -06:00
2017-04-13 23:00:18 -06:00
} ) ;
}
} ) ;
2017-04-13 16:53:18 -06:00
2017-04-13 23:00:18 -06:00
} ) ;
2016-03-31 15:01:27 -06:00
2017-04-01 11:03:05 -06:00
// Social
2017-04-13 16:53:18 -06:00
app . get ( '/login/:service' , ( req , res , next ) => {
2017-04-13 22:45:25 -06:00
let service = req . params . service ,
sendParams = ( service === 'google' ) ? { scope : [ 'profile' ] } : null ;
2017-04-13 16:53:18 -06:00
// Social login
if ( ! req . user ) {
2017-04-01 11:03:05 -06:00
passport . authenticate ( service , sendParams ) ( req , res , next ) ;
2017-04-13 16:53:18 -06:00
}
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Connect social account
else if ( ! req . user . auth [ service ] ) {
2017-04-01 11:03:05 -06:00
passport . authorize ( service , sendParams ) ( req , res , next ) ;
2017-04-13 16:53:18 -06:00
}
2017-04-13 22:45:25 -06:00
2017-04-13 16:53:18 -06:00
// Disconnect social account
else {
2017-04-01 11:03:05 -06:00
req . user . auth [ service ] = undefined ;
2017-04-13 22:45:25 -06:00
req . user . save ( )
. catch ( ( err ) => {
2017-04-13 16:53:18 -06:00
mw . throwErr ( err , req ) ;
res . redirect ( '/settings' ) ;
2017-04-13 22:45:25 -06:00
} ) . then ( ( ) => {
2017-04-01 11:03:05 -06:00
req . flash ( 'success' , ` ${ mw . capitalize ( service ) } account disconnected. ` ) ;
2017-04-13 16:53:18 -06:00
res . redirect ( '/settings' ) ;
2017-04-13 22:45:25 -06:00
} ) ;
2016-03-31 17:06:21 -06:00
}
} ) ;
2017-04-13 16:53:18 -06:00
app . get ( '/login/:service/cb' , ( req , res , next ) => {
2017-04-01 11:03:05 -06:00
var service = req . params . service ;
if ( ! req . user ) {
passport . authenticate ( service , loginOutcome ) ( req , res , next ) ;
} else {
req . flash ( 'success' , ` ${ mw . capitalize ( service ) } account connected. ` ) ;
2017-04-12 11:41:27 -06:00
req . session . next = '/settings' ;
2017-04-01 11:03:05 -06:00
passport . authenticate ( service , connectOutcome ) ( req , res , next ) ;
}
} , loginCallback ) ;
2017-04-13 16:53:18 -06:00
2017-04-01 11:03:05 -06:00
// Android auth
//TODO: See if there's a better method
2017-04-13 16:53:18 -06:00
app . get ( '/auth/google/idtoken' , passport . authenticate ( 'google-id-token' ) , ( req , res ) => {
2017-04-13 22:45:25 -06:00
if ( ! req . user ) { res . sendStatus ( 401 ) ; }
2017-04-01 11:03:05 -06:00
else { res . send ( req . user ) ; }
} ) ;
2017-04-13 16:53:18 -06:00
} ;