Fixed login flash and redirects
parent
4de5964e70
commit
cf667dc35a
|
@ -33,6 +33,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.') );
|
||||
}
|
||||
|
||||
|
@ -45,6 +46,7 @@ module.exports = (passport)=>{
|
|||
|
||||
// Password incorrect
|
||||
if (!res) {
|
||||
req.session.next = undefined;
|
||||
return done( null, false, req.flash('danger','Incorrect email or password.') );
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
'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
|
||||
},
|
||||
connectOutcome = {
|
||||
failureRedirect: '/settings',
|
||||
failureFlash: true
|
||||
},
|
||||
loginCallback = (req,res)=>{
|
||||
// Prevent redirect loop
|
||||
if (req.session.next.substring(0,7)==='/login#'){
|
||||
req.session.next = '#';
|
||||
res.redirect('/map');
|
||||
}
|
||||
else {
|
||||
res.redirect( req.session.next || '/map' );
|
||||
}
|
||||
};
|
||||
|
||||
// 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.`);
|
||||
// Prevent redirect loop
|
||||
if (req.session.next.substring(0,8)==='/logout#') {
|
||||
req.session.next = '#';
|
||||
res.redirect('/');
|
||||
} else {
|
||||
res.redirect(req.session.next || '/');
|
||||
}
|
||||
});
|
||||
|
||||
// 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
|
||||
user.createToken((err,token)=>{
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
|
||||
// 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\nTo 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(()=>{
|
||||
req.flash('success', `An email has been sent to <u>${user.email}</u>. Check your inbox to complete your registration. `);
|
||||
res.redirect('/login');
|
||||
}).catch((err)=>{
|
||||
mw.throwErr(err,req);
|
||||
res.redirect('/login#signup');
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 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})
|
||||
.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 {
|
||||
|
||||
user = new User();
|
||||
user.created = Date.now();
|
||||
user.email = req.body.email;
|
||||
user.slug = slug(user.email.substring(0, user.email.indexOf('@')));
|
||||
|
||||
// Generate unique slug
|
||||
let slug = new Promise((resolve,reject) => {
|
||||
(function checkSlug(s,cb){
|
||||
|
||||
User.findOne({slug:s})
|
||||
.catch((err)=>{
|
||||
mw.throwErr(err,req);
|
||||
})
|
||||
.then((existingUser)=>{
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
// Unique slug: proceed
|
||||
else { cb(s); }
|
||||
|
||||
});
|
||||
|
||||
})(user.slug, (newSlug)=>{
|
||||
user.slug = newSlug;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
|
||||
// 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');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// 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
|
||||
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\nDid you request to reset your Tracman password? If so, follow this link to do so:\n${env.url}/settings/password/${token}\n\nIf 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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}).catch( (err)=>{
|
||||
mw.throwErr(err,req);
|
||||
res.redirect('/login/forgot');
|
||||
});
|
||||
|
||||
} );
|
||||
|
||||
// Social
|
||||
app.get('/login/:service', (req,res,next)=>{
|
||||
let service = req.params.service,
|
||||
sendParams = (service==='google')? {scope:['profile']} : null;
|
||||
|
||||
// Social login
|
||||
if (!req.user) {
|
||||
passport.authenticate(service, sendParams)(req,res,next);
|
||||
}
|
||||
|
||||
// Connect social account
|
||||
else if (!req.user.auth[service]) {
|
||||
passport.authorize(service, sendParams)(req,res,next);
|
||||
}
|
||||
|
||||
// Disconnect social account
|
||||
else {
|
||||
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');
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
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 auth
|
||||
//TODO: See if there's a better method
|
||||
app.get('/auth/google/idtoken', passport.authenticate('google-id-token'), (req,res)=>{
|
||||
if (!req.user){ res.sendStatus(401); }
|
||||
else { res.send(req.user); }
|
||||
} );
|
||||
|
||||
};
|
|
@ -7,6 +7,7 @@
|
|||
"bcrypt-nodejs": "0.0.3",
|
||||
"body-parser": "^1.17.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"connect-flash-plus": "^0.2.1",
|
||||
"cookie-parser": "^1.4.1",
|
||||
"cookie-session": "^2.0.0-alpha.1",
|
||||
"express": "^4.15.2",
|
||||
|
|
16
server.js
16
server.js
|
@ -10,7 +10,7 @@ const
|
|||
mongoose = require('mongoose'),
|
||||
nunjucks = require('nunjucks'),
|
||||
passport = require('passport'),
|
||||
flash = require('connect-flash'),
|
||||
flash = require('connect-flash-plus'),
|
||||
env = require('./config/env.js'),
|
||||
mw = require('./config/middleware.js'),
|
||||
User = require('./config/models.js').user,
|
||||
|
@ -64,21 +64,20 @@ const
|
|||
app.use(expressValidator());
|
||||
app.use(flash());
|
||||
}
|
||||
|
||||
|
||||
/* Auth */ {
|
||||
require('./config/passport.js')(passport);
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
require('./config/routes/auth.js')(app, passport);
|
||||
}
|
||||
|
||||
|
||||
/* Routes */ {
|
||||
|
||||
|
||||
// Static files (keep this before setting default locals)
|
||||
app.use('/static', express.static( __dirname+'/static', {dotfiles:'allow'} ));
|
||||
|
||||
|
||||
// Set default locals available to all views (keep this after static files)
|
||||
app.get( '/*', (req,res,next)=>{
|
||||
app.get( '*', (req,res,next)=>{
|
||||
|
||||
// Path for redirects
|
||||
req.session.next = ( req.path.substring(0, req.path.indexOf('#')) || req.path )+'#';
|
||||
|
@ -94,6 +93,9 @@ const
|
|||
next();
|
||||
} );
|
||||
|
||||
// Auth routes
|
||||
require('./config/routes/auth.js')(app, passport);
|
||||
|
||||
// Main routes
|
||||
app.use( '/', require('./config/routes/index.js') );
|
||||
|
||||
|
|
Loading…
Reference in New Issue