I'm building an application using Node that uses Passport.js to handle user login using a local database.
So I have the following code that gets called when a user goes to /profile. After successfully logging in the user gets redirected to /profile. Which does happen according to morgan.
app.get('/profile', passport.authenticate('local-login', { session : false, failureRedirect : '/login' }), function(req, res) {
console.log("testnow");
res.render('profile.ejs', {
user : req.user // get the user out of session and pass to template
});
});
My local-login code is the following.
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
console.log("testdone");
return done(null, user);
});
}));
When testing the code I login and get redirected to profile for a split second. The console prints "testdone" which is in my local-login code BUT doesn't print "testnow" as it is expected to. Meaning the second function in my /profile get method never seems to get called even tho local-login is calling the next function.
So from the end users standpoint you login (behind the scenes you get redirected to /profile for a split section) and /profile redirects you back to /login.
Any ideas on how to fix this so the second function in my /profile get method actually gets called?
Thanks so much in advance. I would also be more then happy to provide any additional information to help figure this out.
passport.authenticate() is meant to handle the actual authentication; in other words, to take the login credentials and pass them to the strategy. It's not meant to pass along requests if they are already authenticated, which is what you're trying to use it for.
Instead, you want to use something like connect-ensure-login to guard routes for which a user has to be logged in.
See also this Passport example project.
Related
I've written code with Passport.js for authentication purpose. While user logged into chrome and using same credentials user logged into another browser 'FF'.
As we all know that Passport.js store all details into req.users and req.session.passport.users. If from one browser user update some details how can we update into another browsers req object without logout?
Same kind of, If admin update user1 details and he already logged in than how that will affect?
Any clue?
As we all know that Passport.js store all details into req.users and
Not necessarily. passport.js does not store user details in req.user, but your passport.js integration code loads the user details from some backend storage and then puts it in the request object on every request.
So it is up to you to update the user in the backend and decide when to retrieve a new version ( instead of just deserializing jwt, for example ) on every request just as well.
Sample code from http://www.passportjs.org/docs/basic-digest/
passport.use(new BasicStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.validPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
This code is executed on every single request which means that on every request to the server your user is loaded from your database.
Even if you're working with multiple sessions in multiple browsers the result is the same. So it is up to you to handle when and how you want to update your user in your database.
Otherwise if you don't load your user from an external datasource but e.g. deserialize the whole user object from jwt ( which is not recommended unless you really understand what you're doing ) then you need to think of a synchronisation strategy e.g. check some updated flag in db or some cache on deserialization
I can register just fine, however when I get directed to my game route I get a default Error page that's just white with [object Object] on the screen. Then I get the same in my console, [object Object] and it repeats every once in a while.
At first I thought it was something to do with socket.io, but it isn't even getting to that point. I think it might be something with passport and how I have it configured, not being setup good with the promise route I'm going, but I am at a complete loss. I don't know exactly where this error is occurring.
Here is the passport file:
/*jshint esversion: 6 */
const LocalStrategy = require('passport-local').Strategy;
const db = require('../config/db');
const bcrypt = require('bcryptjs');
let io = require('./io');
module.exports = (passport) => {
// Local Strategy login
passport.use('local-login',
new LocalStrategy((username, password, done) => {
console.log('username');
// Match Username
let sql = 'SELECT * FROM users WHERE username = ?';
db.query(sql, [username]).then(results => {
if (!results.length) {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
console.log('password');
// Match Password
bcrypt.compare(password, results[0].password, (err, isMatch) => {
if (isMatch) {
console.log('Password is correct');
return done(null, results[0]);
} else {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
});
});
}));
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// used to serialize the user for the session
passport.serializeUser((user, done) => {
console.log(user.username + ' has been Serialized');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
db.query('SELECT * FROM users WHERE id = ?', [id]).then(results => {
console.log(results[0].username + ' has been deserialized');
done(results[0]);
});
});
};
This seems to go off without a hitch, now here is my login redirect:
// Login Process
router.post('/login',
passport.authenticate('local-login', {
successRedirect: '/game',
failureRedirect: '/',
failureFlash: true,
}), (req, res) => {
console.log('login route test');
});
Again this seems to be doing well, it does in fact redirect me as intended. Now, here is some extra stuff I think might be causing it:
// Passport config
require('./config/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
app.get('*', function (req, res, next) {
res.locals.user = req.user || null;
next();
});
Then the game route:
//Route to game app
app.get('/game', function (req, res) {
console.log('Log before it checks req.user');
if (req.user) {
console.log('req.user is working');
res.render('game');
} else {
req.flash('error', 'You need to be signed in!');
res.redirect('/');
}
});
So here is the thing here: When I am not logged in and go to the /game route it will kick me back into my main route with the correct flash error. However, when I login, I can;t for the life of me get it to fire off a console.log() function. So I am thinking it is getting stuck with the req.user on login, but I am not sure why nor how. If more information is needed, I can give more... but this is mostly what all handles the login process (except socket.io, but it doesn't even get to that point yet, and all my socket.io file does is send the data client side for easy updates).
I will keep trying my luck, but since I am new to promises, this may be the reason why, and if it is, I may not be so lucky.
EDIT: Well, I've changed everything back to a normal callback for my DB (which is what I had it before). Weirdly though, I am getting the same result, and I've no idea why. This project had been put on hold for months, but I hadn't touched anything until I changed all the DB stuff. So something must have broken before I even touched anything when I left this project it was working just fine. But I did change it back to the promise method, because I'd rather stick to this message.
EDIT: Also, I am getting a 500 internal server error on the browser console.
EDIT: Updated code and added console.logs in more places to see where this is hanging up, and I'm still not sure. So here is the logging sequence when I click the login button:
username
password
Password is correct
Bloodmorphed has been Serialized
Bloodmorphed has been deserialized
[object Object]
Bloodmorphed has been deserialized
[object Object]
EDIT: So it seems like the login process is not working correctly. I am not sure why and I can't find a problem with anything I am doing. I have looked at multiple sources of how to set-up passport for MySQL and while some of them differ a tiny bit, they all seem to be the same where it matters. I do not know why this is happening and according to multiple sources of working logins, I am doing this right.
I am, well... simply an idiot. I forgot when I changed to a promise system, I handle errors inside the query itself, so there was no reason for me to pass it through.
so where I had done done(results[0]) in the passport.deserializeUser... I just had to add null before it like so: done(null, results[0]) so much wasted time on a simple matter. I feel like a dumby.
I am using Passport JS to handle authentication and Express JS to handle my routing.
Currently, I have a login route that on success go to the /teacher URI. (As shown below).
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/teacher', // redirect to the teacher profile section
failureRedirect: '/login', // redirect back to the login page if there is an error
failureFlash: true // allow flash messages
}));
I have three main parts of my application, Teacher, Parent, Student.
I have built the Teacher side of the application but now looking to build the Student and Parent,
In my Passport JS session I have req.user.group which tells me if the user is a Teacher, Parent or Student.
Is there any way of redirecting to different locations dependent on what type of user logs onto the application.
E.G If a parent was to log on it would go to /parent and a student /student
Thank you.
app.post('/login', passport.authenticate('local-login', function(err, user, info){
var error = err || info;
if (error) return res.status(401).json(error);
if (!user) return res.status(404).json({message: 'Something went wrong, please try again.'});
else {
if(req.user.group==='Teacher'){
res.redirect('/teacher');
}
else if(req.user.group==='Parent'){
res.redirect('/parent');
}
else{
res.redirect('/student');
}
}
}));
I Have used Passport-Google-OAuth in Node.js web service project. I am using OAuth2Strategy.
The process i have used is i call the web service method to authenticate user from his Gmail account. Initially i serve the Raw HTMl which i receive from calling the Passport-google-OAuth. Which works fine.
Then i login with valid Gmail accounts. Once the Callback Url is called by google the server goes into infinite loop and calls the callback url again and again after fixed interval of time.
My Passport strategy configuration for Google is like this:
// Use the GoogleStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Google
// profile), and invoke a callback with a user object.
passport.use(new GoogleStrategy({
clientID : "948630708036-2t6mestiv81gtv0s9n6iptoava4o1cpa.apps.googleusercontent.com",
clientSecret : "omugRnr7nad2yMmefiZdBaLL",
callbackURL : "http://localhost:4000/api/auth/google/callback"
},
function(token, refreshToken, profile, done) {
console.log('Inside global callback.');
// make the code asynchronous
// User.findOne won't fire until we have all our data back from Google
process.nextTick(function() {
// try to find the user based on their google id
User.findOne({ 'google.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
// if a user is found, log them in
return done(null, user);
} else {
// if the user isnt in our database, create a new user
var newUser = new User();
// set all of the relevant information
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.google.name = profile.displayName;
newUser.google.email = profile.emails[0].value; // pull the first email
return done(null, newUser);
}
});
});
}));
Then i am calling the Passport from the endpoint in the service project:
passport.authenticate('google', { session:false,scope : ['profile', 'email'] });
And the Callback URL contains the following code where i am sending the returned Google account details of the user in JSON format to the client which accessed the web service intially.
function(req, res) {
console.log('Callback by Google:'+res.body+' || '+ res.headers);
console.log('Response Object:'+util.inspect(res));
passport.authenticate('google', { session : false }),function(req,res){
console.log('Callback authenticated.User: +req.user);
res.json(req.user);
}
In the Log i am getting "Callback by Google: undefined || undefined".
I am disabling sessions since this will be the API Server feeding data to various clients.
I dont know what mistake i am doing. Kindly point out any resource or example where the Passport-Google-OAuth(OAuth2Strategy) is used in a API(Web Service) server. Do i need to follow some other way. Thanks for ur help in advance.
There may be a problem in your routes. Look at the tutorial here
https://scotch.io/tutorials/easy-node-authentication-google
It's the best I have seen. And I have implemented something similar.
Similar to the google login https://accounts.google.com/ServiceLogin when you check "stay signed in" log in then log out, you have the email field filled out as well as your image.
I think this is a cool UI feature to have, so I implemented https://github.com/jaredhanson/passport-remember-me but the cookie is only available when logged in, logging out destroys the cookie.
So messing around I did this
// when the user logs in
User.findOne({ _id: req.user.id }, function (err, user) {
user.save(function(err) {
if(err)
console.log('error');
else
res.render('app', { user: req.user });
res.cookie('testCookie', req.user.local.email, { maxAge: 900000, httpOnly: true });
});
});
I will need validation and only set the cookie if req.body.remember_me is true, but for now this sets a cookie like this
Then when I render the login page I do this
res.render('login', { user: req.cookies.testCookie });
Obviously I don't think this is good, but I am confused what is the purpose of https://github.com/jaredhanson/passport-remember-me if I can't access data for the logged out user?
Looking at googles cookies they don't expose the email like I did it looks like this
Looking at it there is an ID=b1051bd.
So IDK any help would be great, I am trying to remember the user that checked the remember me box when logged out.