I was writing a local-signup strategy and noticed that it doesn't work so I stepped back and tried to authenticate against my empty collection. Every time I submit the form it takes ~30-40s until it results in a timeout. I ensured passport.authenticate() is called but it seems ike it's not doing any redirects and hence it is timing out because I am not rendering something either.
Questions:
I expected that it would do a redirect to the failureUrl (which is '/signup'), but instead nothing is happening. What am I doing wrong here?
Why there is no single log message coming from passport? This is driving me crazy because I have absolutely no idea what is going wrong there.
I am new to node.js and as far as I got I don't need to pass the configured passport object to the router but instead I can just do const passport = require('passport') is that correct?
This is my function handler for the /signup route:
function processSignup (req, res) {
logger.info("POST request received")
logger.info(req.body)
passport.authenticate('local', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
})
}
Winston prints:
7:32:04 PM - info: POST request received 7:32:04 PM - info:
username=dassd#dass.de, password=dasdsa, submit=Register
My passport.js file looks like this:
const LocalStrategy = require('passport-local').Strategy
const User = require('./user-model')
const passport = require('passport')
// expose this function to our app using module.exports
function config() {
passport.serializeUser(function(user, done) {
done(null, user.id)
})
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user)
})
})
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
}
module.exports = {
config: config
}
The relevant snipped of my app.js:
// required for passport
require('./authentication/passport').config();
app.use(cookieParser())
app.use(bodyParser())
app.use(session({
secret: 'secretToBeChanged',
saveUninitialized: false,
resave: false
}))
app.use(passport.initialize())
app.use(passport.session()) // persistent login sessions
app.use(flash()) // use connect-flash for flash messages stored in session
After a quick look at the documentation for passportjs, I think you need to do something like this:
function processSignup (req, res, next) {
logger.info("POST request received")
logger.info(req.body)
const handler = passport.authenticate('local', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
});
handler(req, res, next);
}
passport.authenticate() returns a function that is meant to be used as the route handler function.
Normally, you would type something like:
app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}));
But since you have abstracted with your own route handler function, you need to invoke the one returned from passport.authenticate().
In the end Mikael Lennholm was right and he pointed me into the right direction. I couldn't find that in any passport.js tutorials. However the passport.js documentation contains this code snippet which represents the same but I prefer it's code style:
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
Related
I am using passport and trying to do my code organisation like routes(just one line route), controller with one method to handle route, model to do some action which coming from controller method. Following code is working fine returning me token if mobile number and password is matched. But how can I reorgnize code so it break and become properly, I dont' want to put anything in routes.js file but just one single line, and nothing in controller but one method where I want to put all passport stuff in passport.js file either config/passport.js or root/passport.js
const express = require('express'),
cors = require('cors'),
compression = require('compression'),
morgan = require('morgan'),
passport = require('passport')
const app = express()
app.use(passport.initialize())
app.use(express.json())
app.use(express.urlencoded({extended: false}))
app.use('/', Router)
Now in my config/routes.js file
const passport = require('passport')
const userController = require('../app/controllers/users_controller')
const jwt = require('jsonwebtoken')
LocalStrategy = require('passport-local').Strategy
passport.use(
new LocalStrategy(function (username, password, done) {
User.findOne({mobile: username}, function (err, user) {
if (err) {
return done(err)
}
if (!user) {
return done(null, false, {message: 'Incorrect Username'})
}
console.log(` Password: ${password} `)
if (!user.comparePassword(password)) {
return done(null, false, {message: 'Incorrect password'})
}
return done(null, user)
})
})
)
passport.serializeUser(function (user, cb) {
cb(null, user.id)
})
passport.deserializeUser(function (id, cb) {
User.findById(id, function (err, user) {
if (err) {
return cb(err)
}
cb(null, user)
})
})
router.post('/api/v1/login', function (req, res, next) {
passport.authenticate('local', {session: false}, (err, user, info) => {
if (err || !user) {
return res.status(400).json({
message: 'Something is not right',
user: user,
})
}
req.login(user, {session: false}, (err) => {
if (err) {
res.send(` Error: ${err}`)
}
// generate a signed son web token wtih contents of user
const token = jwt.sign(user.id, 'your_jwt_secret')
return res.json({user, token})
})
})(req, res)
})
I want to break it, it should not be in routes, my other routes are like following
router.get('/logout', users_controller.update)
router.post('/forgotPassword', users_controller.update)
I want to that it should be like following route
router.post('/login', users_controller.login)
I tried many times but still can't work out, even used this code, but in my another website I used same method and it worked in Controller, so all passport related stuff I put in controller but that one is also not correct, it should be one line in route file and method in controller rest in separate passport.js files.
Kindly can you assist me how can I do it?
I have a server set up to authenticate user login, so far I was able to successfully authenticate user and redirect them to the success page (as well as redirect them back to the login page if failed). However, I can't use my own express middleware to restrict user if they are not logged in, for some reason my req.isAuthenticate() check in the middleware is always false. I feel like my session info is not stored when logging in, but I'm not sure.
Here's my setup (note that I use express-flash-2 instead of connect-flash therefore the req.flash())
In server.js
// ...
var flash = require('express-flash-2');
var session = require('express-session');
var passport = require('passport');
// ...
server.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: true
}));
server.use(flash());
server.use(passport.initialize());
server.use(passport.session());
passport.use(auth_controller.authStrategy);
passport.serializeUser(auth_controller.authSerializer);
passport.deserializeUser(auth_controller.authDeserializer);
server.get('/maintenance_login', (req, res) => {
res.render('login');
});
server.post('/maintenance_login', passport.authenticate('local', {
successRedirect: '/maintenance',
failureRedirect: '/maintenance_login',
failureFlash: true
}));
// This does basically the same as above
// server.post('/maintenance_login', (req, res, next) => {
// passport.authenticate('local', function(err, user, info) {
// if (err) { return next(err); }
// if (!user) { return res.redirect('/maintenance_login'); }
//
// req.logIn(user, function(err) {
// if (err) { return next(err); }
// console.log('is authenticated?: ' + req.user); // this returns true
// return res.redirect('/maintenance');
// });
// })(req, res, next);
// });
server.get('/maintenance:query?', auth_controller.restrict, maintenance_controller.maintenance_list);
In auth_controller.js
var passport = require('passport');
var LocalStrategy = require('passport-local');
var User = require('../models/user');
exports.authStrategy = new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
}, function (email, password, done) {
User.authenticate(email, password, function(err, user) {
// success message
// error message
done(err, user, err ? { message: err.message } : null);
});
});
exports.authSerializer = function(user, done) {
done(null, user.id);
};
exports.authDeserializer = function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
};
exports.restrict = function (req, res, next) {
console.log('is authenticated?: ' + req.isAuthenticated()); // This always returns false
if (req.isUnauthenticated()) {
res.flash('error', 'Access denied, please log in.');
return res.redirect('/maintenance_login');
}
return next();
}
Again, my issue is that whenever I was successfully logged in, I got kicked back to /maintenance_login and the flash showing "Access denied, please log in.". Which means that req.isUnauthenticated() in the restrict middleware returns true.
Any ideas?
Well, it turns out that missing the .Strategy is crucial. Changing var LocalStrategy = require('passport-local'); to var LocalStrategy = require('passport-local').Strategy; fixes this issue. Incase anyone else is having the same problem.
I'm using angular to deal with some routes,on the server side I'm using passport so basically I can acess to the user session variable req.user in my views , but when it comes to a route renderred by ui-router my req.user is undefined. Any idea to access to the req.user even it's not an express route
app.js code :
// Express Session
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true
}));
// Passport init
app.use(passport.initialize());
app.use(passport.session());
// Global Vars
app.use(function (req, res, next) {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
res.locals.user = req.user || null;
next();
});
my passport code is as follows :
passport.use('employee',new LocalStrategy(
function(email, password, done) {
Employee.getUserByEmail(email, function(errEmp, emp){
if(errEmp ) throw errEmp;
if(!emp){
return done(null, false, {message: 'Unknown User'});
}
if(emp) {
Employee.comparePassword(password, emp.encryptedpass, function (err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, emp);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
}
});
}
));
router.get('/',ensureAuthenticated ,function(req, res, next) {
res.render('index', { title: 'Nubes' });
});
function ensureAuthenticated(req, res, next){
if(req.isAuthenticated()){
Employee.findById(req.user.id,function (err,emp) {
if(emp) {
res.render('employee/index');
}
})
}
}
router.post('/login', function(req, res, next) {
Employee.findOne({email:req.body.username},function (err,emp) {
if(emp){
passport.authenticate('employee', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
})
});
In my rendered page 'employee/index' I can display my user information but routes that rendered by ui-router don't have a user variable
here is an example code of angular route :
$stateProvider
.state('home',{
url:'/',
templateUrl:'/views/employee/home.html'
})
in home.html user is not defined which is normal because it's not express server who rendred it . what I want is to get this user variable in my ui-router rendered pages
I suggest to implement token based authentication using something like JWT-tokens, you have also passport plugins for those.
If you still want to use sessions based info and pass it to the client, you can store them in some global JS variable, which i highly dont recommend.
here some info about jwt: https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
for the locals if using handlebars it would look something like this:
<script>
var user = {{user}};
</script>
just note that you might need to implement some JSON stringify and decoding in order to get the info as JS object.
My passport middleware is not working. When I call passport.authenticate() on a route to confirm the user is authenticated, I am redirected to the failure page. The login route however works as expected, and I am redirected successfully to the correct page, where the failure middleware redirect is called and I am sent back to the login page.
I have a Passport strategy like so:
module.exports = function(){
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy
var User = require('../models/user');
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: "email",
passwordField: "password"
}, function(email, password, done){
User.getUserByEmail(email, function(err, user){
if(err) throw err;
if(!user){
console.log('unknown user');
return done(null, false, {message: 'Email not recognised, please try again'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
} else {
console.log('Invalid password');
return done(null, false, { message: 'Password not recognised, please try again' });
}
});
});
}));
};
Logging in is fine using:
router.post('/user/login',
passport.authenticate('local', {
successRedirect: '/clients',
failureRedirect: '/user/login',
failureFlash: true
}
));
My '/clients' route is like so, where the authentication fails and redirects incorrectly back to the login page:
router.get('/clients',
passport.authenticate("local", {
failureRedirect: "/user/login",
failureFlash: "not verified"
}), function(req, res, next) {
res.locals.client = null;
Client.find({})
.select("name")
.select("_id")
.exec(function(err, clients){
res.render('client/views/clients', {
title: 'Clients',
clients: clients
});
});
});
Server passport initialization like so:
//passport
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({ extended: false }));
require("./user/services/passport")(passport);
What am I doing wrong?
As you have used failureRedirect: '/user/login' . It will redirect you to the login page. If you want to redirect it to someother page change the failureRedirect value to that corresponding route.
I am using passportjs for user authentication. In the official guide, the only shown user case is a redirection operation after authentication:
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login',
failureFlash: true })
);
However, in my application, I don't want passport to redirect immediately. Instead, I hope passport could send me back some json object indicating whether the authentication is success or not. How I can do that?
You can send custom responses -
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.send(400, 'Incorrect username');
}
req.logIn(user, function(err) {
if (err) {
return next(err);
}
res.send({'message': 'User authenticated'});
});
})(req, res, next);
});