Google authentication: failed to serialize user into session - javascript

I'm trying to get some basic Google authentication going with PassportJS.
The amount of information on the web seems rather limited and I keep hitting issues so thought I would try here.
I am using the code from https://github.com/mattgaidica/twitter-mongo with a few modifications to use it for Google OAuth (it doesn't use the Twitter keys, it uses passport-google, passport.authenticate('google', ...)).
I keep ending up with this error: 'Error: failed to serialize user into session'
passport.serializeUser(function(user, done) {
console.log(user); //does not have the fields we created earlier, user.uid does not exist.
done(null, user.uid);
});
My Passport Strategy:
passport.use(new GoogleStrategy({
returnURL: "http://localhost:3000/auth/google/callback"
},
function(identifier, profile, done) {
User.findOne({uid: profile.id}, function(err, user) {
if(user) {
done(null, user);
} else {
var user = new User();
user.provider = "google";
user.uid = identifier;
user.name = profile.displayName;
user.save(function(err) {
if(err) { throw err; }
done(null, user);
});
}
})
}
));
The fields of this other user are not the same as the one originally created, what happened to the other user?
I'm a bit lost as to what is going on here, if anyone could let me know what I'm doing wrong I would be very grateful.

In your query, you use profile.id:
User.findOne({uid: profile.id}, function(err, user) { ... });
But that should be identifier.
Also, you're using the OpenID-version of the Passport Google plug-in, which is rather old (and doesn't even work properly on Node 0.10). I'd suggest you use passport-google-oauth instead.

Related

Can't compare password with hash Passport NodeJS

I want to create function which will changing password. So in first place I have to compare old password with actually user password in db. Here is the problem, I write inside router function but it is not called. Can someone help me, and tell how could I call it to get answer that old password is equall with password user?
router.post('/change-password', function (req, res, next) {
console.log(req.body)
User.comparePassword(req.body.oldPpassword, user.password, function (err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
You shouldn't be saving the password in the DB, that is unsafe. You should hash it in node and then save the hashed password to the DB. Then when you want to check a user, you hash the new password they gave you and compare that to the hashed one in the DB. You never, never store unhashed passwords. That is a recipe for disaster.
Libraries like bcrypt make this easy.

Sign in With Twitter using JWT, NodeJS and AngularJS

I'm having a big issue that I'm not sure is just a code design issue or I'm trying to do something that makes no sense.
I've implemented a local sign up using passport for authentication and JWT for route access and permissions. I send the token to the front end and save it to LocalStorage using AngularJS.
Everything there works and makes sense. But with twitter, I can't figure out how to implement the same strategy for getting the user logged in.
I've set up everything on the Twitter side, use passport for login and registering. But I don't see how it is possible to send the token to the front end because I can only use a GET request to receive the information from the Twitter API.
I redirect to Twitter login, redirect to the callback URL. But what comes after that? I have some relevant code that just returns the JWT. What I'm asking is how can I pass that to Angular in the best way possible?
Routes:
app.get('/login/twitter', passport.authenticate('twitter'));
app.get('/login/twitter/callback', function(req, res) {
passport.authenticate('twitter' , function(err, user, info) {
if(err) {
res.json({
'message': err
});
}
var token;
token = user.generateJwt();
res.status(200);
res.json({
"token" : token
});
})(req, res);
});
Passport:
passport.use(new TwitterStrategy({consumerKey: auth.twitterAuth.consumerKey, consumerSecret: auth.twitterAuth.consumerSecret, callbackURL: auth.twitterAuth.callbackURL}, function(req, key, keySecret, profile, done) {
User.findOne({'twitter.id' : profile.id}, function(err, user) {
if(err) {
return done(err);
}
if(user) {
if(!user.twitter.token) {
user.twitter.token = key;
user.twitter.username = profile.username;
user.twitter.displayName = profile.displayName;
user.save(function(err) {
if(err) {
res.json({
'message': err
});
}
return done(null, user);
});
}
return done(null, user);
}
let newUser = new User();
newUser.twitter.id = profile.id;
newUser.twitter.token = key;
newUser.twitter.username = profile.username;
newUser.twitter.displayName = profile.displayName;
newUser.twitter.registerDate = Date.now();
newUser.save(function(err) {
if(err) {
res.json({
'message': err
});
}
return done(null, newUser);
});
});
}));
The best way to do this from my experience is to pass the information you want to store in the JWT to the url and store the token in localStorage on the callback page. Once the user identifier is stored in the browser, redirect to the desired page.

Express Application Instant Notification Architecture Suggestions

I have an express application where administrators should receive an instant notification when a new user registers into the database. Note that I am using Passport to log users in via the Google strategy:
passport.use(new GoogleStrategy({
clientID : configAuth.googleAuth.clientID,
clientSecret: configAuth.googleAuth.clientSecret,
callbackURL : configAuth.googleAuth.callbackURL,
},
function(token, refreshToken, profile, done){
process.nextTick(function(){
// try to find a 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 isn't in our database, create a new user
var newUser = new User({
'google.id': profile.id,
'google.token': token,
'google.name': profile.displayName,
'google.email': profile.emails[0].value, //pull the first email
'google.active': 0,
'google.level': 0
})
// save the user
newUser.save(function(err){
if(err)
throw err
return done(null, newUser)
})
// Give the new user the role of 'inactive'
acl.addUserRoles(newUser.id, 'inactive', function(err){})
// Email a notice to the new user that they have to be accepted by an administrator.
...
// Send an email to all "admin-level" users to activate this user.
acl.roleUsers('admin', function(err, users){
// Loop through all admin users
users.forEach(function(value){
// Find each individual admin user by _id
User.findOne({ '_id' : value }, function(err, user){
if(err)
return done(err)
// Send this admin an internal mail (IMail)
var iMail = new IMail()
iMail.body = newUser.google.name + ' needs approval.',
// Send each administrator a notification that a new user needs approval
iMail.recipients.push(user.id)
iMail.save(function(err){
if(err)
return err
console.log("IMail was sent to notify admin " + user.google.name + " to approve " + newUser.google.name + " as a system user.")
})
})
})
})
}
})
})
}))
Within the
iMail.save(function(err){
...
})
block, I know that some type of event needs to be fired which would somehow alert each admin who is currently logged in. Unfortunately, this is where my understanding breaks down at this point. How do I immediately notify an admin of this new notification? I'd like it to be like Gmail, for example, when the Inbox automatically displays (1) when a new email is sent.
As a side note, I've read up on the Publisher/Subscriber pattern (https://joshbedo.github.io/JS-Design-Patterns/) and think this might set me on the right track, I'm just not sure exactly how to implement it. A little push in the right direction from somebody would be appreciated.
#Maximuf Kingzy is right you need to send a notification to admin users and since you want them to get this notification when online then you can use sockets to send them but it can be tough to integrate it inside express sometimes.
One other option :
You can save notifications in database and create an express route that retrieve those notifications. when administrator logs in you just have to retrive those notifications. Or if you want a taste of real-time without real-time you can use setInterval to retrieve those notifications every X seconds and then show them to the administrator user.
Express really simple route (I mean really simple):
app.get('/imail', (req, res) => {
Imail.find({}, (err, imails) => {
if (err) {
return res.status(400).end();
}
res.json(imails);
});
});
And in browser :
setInterval(() => {
fetch('http://yourserver:serverport/imail').then((response) => {
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
return response.json().then(json => {
return response.ok ? json : Promise.reject(json);
});
} else {
return response.text().then(text => {
return response.ok ? text : Promise.reject(text);
});
}
}).then((imails) => {
// do what you want with imails
}).catch((err) => {
// error
});
}, 15000);

Why is passport.serializeUser executed on each request?

I am using passport.js + passport-facebook-token to secure my API build with Strongloop's Loopback Framework.
Why is passport serializing the deserialized user again after it has successfully been deserialized? Also the passport.authenticate method is called on every request! What am I doing wrong?
Here is node's log:
deserializeUser, id: XXXXXXXXXXXXXXXX
User found.
serializeUser, id: XXXXXXXXXXXXXXXX
GET /api/events?access_token=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 304 182ms
Here is the js code:
passport.use(new FacebookTokenStrategy({
clientID: XXXXXXXXXXXXXXXX,
clientSecret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX'
},
function(accessToken, refreshToken, profile, done) {
//check user table for anyone with a facebook ID of profile.id
User.findOne({
'facebookId': profile.id
}, function(err, user) {
if (err) {
return done(err);
}
if (user) {
console.log("User found.");
return done(err, user);
} else {
console.log("User not found.");
User.create({
email: profile.emails[0].value,
facebookId: profile.id,
password: 'secret'
}, function(err, user) {
console.log(user.id);
console.log(user.email);
console.log(user.facebookId);
console.log("User created");
return done(err, user);
});
}
});
}));
passport.serializeUser(function(user, done) {
console.log('serializeUser, id: ' + user.facebookId);
done(null, user.facebookId);
});
passport.deserializeUser(function(id, done) {
console.log('deserializeUser, id: ' + id);
User.findOne({
'facebookId': id
}, function(err, user) {
if (!err) {
done(null, user);
} else {
done(err, user);
}
});
});
Regarding your question about why passport.authenticate is called on every request, it is because you defined it as a middleware, probably before any routing logic happens.
If you have private and public sections on your app, you could do something like that :
// Define a specific that will handle authentication logic
app.get("/auth", passport.authenticate('facebook-token',function(){...});
// Public sections which do not require authentication
app.get("/public1",...);
app.post("/public2",...);
// Private sections which do require authentication
app.get("/private1", function(req,res,next){
if (req.isAuthenticated()){ // Check if user is authenticated
// do things...
}else{ // Wow, this guy is not authenticated. Kick him out of here !
res.redirect("/auth");
}
});
Now, if you have multiple private sections, you'll probably find it a bit tidious to do the same thing for each private section.
You could define a custom function that will check if the user is authenticated, and allow the request to proceed if he is.
Something like
function isThisGuyAuthenticated(req,res,next){
if (req.isAuthenticated()){
return next(); // Ok this guy is clean, please go on !
}else{
res.redirect("/auth"); // This guy is shady, please authenticate !
}
}
And use it like :
app.get("/private1",isThisGuyAuthenticated, doCrazySecretStuff); // doCrazySecretStuff will not be called if the user is not authenticated
app.get("/private2", isThisGuyAuthenticated, getCocaColaRecipe);
app.get("/private3", isThisGuyAuthenticated, flyToMars);
app.get("/public", showInfo); // showInfo will be called whether the user is authenticated or not
Now, if your app only has private sections, you could avoid repeating calls to isThisGuyAuthenticated by defining it as middleware (but not by defining passport.authenticate itself as a middleware !);
// Endpoint that will be hit is the user is redirected to /auth
// BEWARE it needs to be above the middleware, otherwise you'll end up with an infinite redirection loop
app.get("/auth", passport.authenticate('facebook-token',function(){...});
// Middleware that will be called on every request
app.use(isThisGuyAuthenticated);
// You app's endpoints
app.get("/private1", doCrazySecretStuff); // doCrazySecretStuff will not be called if the user is not authenticated
app.get("/private2", getCocaColaRecipe);
app.get("/private3", flyToMars);
Is that clear ?
EDIT : I mistakenly put the middleware before the "/auth" endpoint. Make sure it's placed after

Passport.js is not passing user to request in req.login()

My passport.js configuration goes like so:
const Local = require("passport-local").Strategy;
const USMODEL = require("../models/user.js");
passport.serializeUser(function(user, done) {
console.log("SERIALIZING USER");
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log("DESUSER", id);
var US = mongoose.model("RegUser", USMODEL);
US.findById(id, function(err, user) {
done(err, id);
});
});
passport.use("local-login", new Local({
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},function(req, email, password, done) {
var US = mongoose.model("RegUser", USMODEL);
US.findOne({"email": email}, function(err, user){
if(err) throw err;
if(!user) return done(null, false);
if(!user.validPassword(password)) {
console.log("password not valid");
return done(null, false);
}
return done(null, user);
});
}));
I'm changing the mongoose model within each function because I juggle with multiple collections at a time and I like to have complete control of what's going on.
My router.js file has the following paths that make use of the passport middleware:
app.get("/user/login", function(req, res) {
res.render("signin");
});
app.post('/user/login', function (req, res){
passport.authenticate('local-login', function(err, user, info){
if (err) return res.redirect("/");
if (!user) return res.redirect('/');
else {
req.login(user, function(err) {
if (err) return next(err);
console.log("Request Login supossedly successful.");
return res.redirect('/admin/filter');
});
}
})(req, res);
});
Which, upon successful authentication, redirects to /admin/filter in the same router that goes like so.
app.get("/admin/filter", isLoggedIn, function(req, res){
//rendering stuff here
});
Now, the admin/filter request goes past a middleware called isLoggedIn which, in theory protects my endpoints. It goes like so:
function isLoggedIn(req, res, next) {
console.log("This is the authentication middleware, is req authenticated?");
console.log(req.isAuthenticated());
console.log("Does req.user exist?")
console.log(req.user);
return next();
}
Now, you would expect that because I called req.login and I got redirected to my endpoint of choice, the request would be authenticated. This is not the case.
Request Login supossedly successful.
This is the authentication middleware, is req authenticated?
false
Does req.user exist?
undefined
I can't seem to find the source of my problem. Everything checks out, as the strategy is being invoked, as well as the callback function and req.login which would render, in theory, a req.user object with data in it. One odd thing I've observed is that I don't see the passport.deserializeUser() method in action. Ever. But that could be tangential to the problem. Passport is definitely using my strategy and rendering a user object, but somehow this same object is not going into the request. Do you have any suggestion or idea about what is going on?
I solved the problem by juggling around with the tutorial I started with when I first learned how to use the Passport middleware. Turns out I was doing the configuring wrong: My code used to be like this in the server file:
pass = require("passport");
app.use(pass.initialize());
app.use(pass.session());
require("./app/config/passport.js")(pass);
when it should have been this:
pass = require("passport");
require("./app/config/passport.js")(pass);
app.use(pass.initialize());
app.use(pass.session());
Either I missed the part in the documentation where it's specified that configuration must come before initialization or it's simply written off as a trivial thing to remark. Either way, I solved my problem.
Make sure withCredentials: true while sending the post request.
// register
axios.post(uri, {
email: email,
password: password,
confirmPassword: confirmPassword
}, {
withCredentials: true
})

Categories