passportJS: req.user is returning undefined - node.js

i have simple passport-facebook & google oauth application and it works but problem it is not serializing users properly i guess because when i log req.user it returns undefined. here is my code for facebook oauth
passport.serializeUser((user,done)=>{
done(null,user.id)
})
passport.deserializeUser((id,done)=>{
const user = User.findById(id)
done(null, user)
})
passport.use(new FacebookStrategy({
clientID: process.env.FB_CLIENT_ID,
clientSecret: process.env.FB_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
async (accessToken, refreshToken, profile, done) => {
const user = await User.findOne({ userId: profile.id })
if(user){
console.log('existing user '+ user)
return done(null,user)
}else{
const newuser = User.create({ username: profile.displayName,userId: profile.id });
console.log(newuser.toString())
done(null,newuser)
}
}))
and here is passport-google-oauth2
passport.serializeUser((user,done)=>{
done(null,user.id)
})
passport.deserializeUser((id,done)=>{
const user = User.findById(id)
done(null, user)
})
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/callback"
},
async (accessToken, refreshToken, profile, done) => {
const user =await User.findOne({ userId: profile.id })
if(user){
console.log('existing user '+ user )
return done(null,user)
}else{
const newuser = User.create({ username: profile.displayName,userId:
profile.id });
console.log(newuser)
done(null,newuser)
}
}))
and here is route config
router.get('/facebook', passportFacebook.authenticate('facebook'));
router.get('/facebook/callback',passportFacebook.authenticate('facebook', { failureRedirect: '/auth/login' }),
function(req, res) {
res.redirect('/');
});
router.get('/google',
passportGoogle.authenticate('google', { scope: 'profile' }));
router.get('/google/callback',passportGoogle.authenticate('google', {
failureRedirect: '/auth/login' }),
function(req, res) {
res.redirect('/');
});
so what is wrong with this code?

The problem is you don't await for user in deserializeUser.
const user = User.findById(id)
You need to use then or await result from User.findById. So your deserializeUser code should be
passport.deserializeUser(async (id,done) => {
const user = await User.findById(id)
done(null, user)
})

Related

PassportJs Google Auth saves existing user as a new user in the database. How can I fix that?

I'm using passportJs Google Authetication. Although a user exist in database, When I login the system with this user, It creates this user again in the database as a new user. How can I fix this problem, can you help ?
Thats image of the database:
Here is my codes:
module.exports = passport.use(
new GoogleStrategy(
{
clientID: config.google.clientID,
clientSecret: config.google.clientKey,
callbackURL: "/auth/google/callback",
},
async (accessToken, refreshToken, profile, done) => {
try {
const user = await models.User.findOne({ google: { id: profile.id } });
if (user) {
done(null, user);
} else {
const newUser = new models.User({
google: profile,
isSocialAuth: true,
name: profile.name.givenName,
lastName: profile.name.familyName,
cart: { items: [] },
});
await newUser.save();
done(null, newUser);
}
} catch (error) {
done(error, null);
}
}
)
);
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
models.User.findById(id, (err, user) => done(err, user));
});
My Router:
router.get("/auth/google", passport.authenticate("google", { scope: ["profile"] }));
router.get("/auth/google/callback", passport.authenticate("google", { failureRedirect: "/login" }), async (req, res) => {
req.session.user = req.user;
req.session.isAuthenticated = true;
res.redirect("/");
});
module.exports = router;
My UserSession Middleware:
module.exports = (req, res, next) => {
if (!req.session.user) {
return next();
}
models.User.findById(req.session.user._id)
.then((user) => {
req.user = user;
next();
})
.catch((err) => {
console.log(err);
});
};
After signing in, in the Passport part,
the findOne query might have some issue. It is not able to find the user & hence it is registering again.
Replace
const user = await models.User.findOne({ google: { id: profile.id } });
to
const user = await models.User.findOne({ "google.id": profile.id });
& check if it works.

Error at Strategy.OAuth2Strategy.parseErrorResponse - NodeJS passport google oauth2.0

I'm trying to embed google authentication in Node.js using passport and google passport-google-oauth20. The problem is that when the google callback route opens up I get:
Error
at Strategy.OAuth2Strategy.parseErrorResponse (E:\Programowanie\NodeJS\Hydronide\node_modules\passport-oauth2\lib\strategy.js:329:12)
at Strategy.OAuth2Strategy._createOAuthError (E:\Programowanie\NodeJS\Hydronide\node_modules\passport-oauth2\lib\strategy.js:376:16)
at E:\Programowanie\NodeJS\Hydronide\node_modules\passport-oauth2\lib\strategy.js:166:45
at E:\Programowanie\NodeJS\Hydronide\node_modules\oauth\lib\oauth2.js:191:18
at passBackControl (E:\Programowanie\NodeJS\Hydronide\node_modules\oauth\lib\oauth2.js:132:9)
at IncomingMessage.<anonymous> (E:\Programowanie\NodeJS\Hydronide\node_modules\oauth\lib\oauth2.js:157:7)
at emitNone (events.js:110:20)
at IncomingMessage.emit (events.js:207:7)
at endReadableNT (_stream_readable.js:1059:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
I (more or less) follow this tutorial.
Here is my code:
Routes (starting with '/auth')
'use strict'
const passport = require('passport')
const router = require('express').Router()
router.get(
'/google',
(req, res, next) => {
if (req.query.return) {
req.session.oauth2return = req.query.return
}
next()
},
passport.authenticate('google', { scope: ['email', 'profile'] })
)
router.get(
'/google/callback',
passport.authenticate('google'),
(req, res) => {
const redirect = req.session.oauth2return || '/';
delete req.session.oauth2return;
res.redirect(redirect);
}
);
module.exports = router
There is a passport configuration:
'use strict'
const passport = require('passport')
const keys = require('./keys')
const GoogleStrategy = require('passport-google-oauth20').Strategy
const userController = require('../controllers/user-controller')
const passportConfig = {
clientID: keys.google.clientId,
clientSecret: keys.google.clientSecret,
callbackURL: 'auth/google/callback',
accessType: 'offline'
}
passport.use(new GoogleStrategy(passportConfig,
(accessToken, refreshToken, profile, done) => {
console.log(accessToken, refreshToken, profile, done)
userController.getUserByExternalId('google', profile.id)
.then(user => {
if (!user) {
userController.createUser(profile, 'google')
.then(user => {
return done(null, user)
})
.catch(err => {
return done(err)
})
}
return done(null, user)
})
.catch(err => {
return done(err)
})
}))
passport.serializeUser((user, cb) => {
cb(null, user)
})
passport.deserializeUser((obj, cb) => {
cb(null, obj)
})
As you can see I've added console.log in the new GoogleStrategy second parameter function, but it never fires.
//EDIT
I noticed that instead of assign require('passport-google-oauth20').Strategy I used require('passport-google-oauth20'). But fixing it doesn't chang anything, still the same error.
What I can add to a question is that in my main fail I call
// sets passport config
require('./config/jwt-auth')
require('./config/google-auth')
// initialize passport
app.use(passport.initialize())
So I don't expect anything wrong in there.
You have to specify the full url in the callbackURL section of the strategy:
for example: when if running the code locally on localhost:3000 with code like this:
passport.use(new googleStrategy({
clientID:keys.clientID,
clientSecret:keys.clientSecret,
callbackURL:'auth/google/callback'
},(accessToken,refreshToken, profile,done)=>{
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
}
));
app.get('/auth',passport.authenticate('google',{
scope:['profile','email']
}));
app.get('/auth/google/callback',
passport.authenticate('google'));
The above code will surely throw a TokenError: Bad request. You have to pass the complete URl to have a final code like shown below:
passport.use(new googleStrategy({
clientID:keys.clientID,
clientSecret:keys.clientSecret,
callbackURL:'http://localhost:3000/auth/google/callback'
},(accessToken,refreshToken, profile,done)=>{
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
}
));
app.get('/auth',passport.authenticate('google',{
scope:['profile','email']
}));
app.get('/auth/google/callback',
passport.authenticate('google'));
You can get help by putting some console.log inside your Oauth and Strategy under node modules, Specifically around the line on which you are getting error in logs.
E:\Programowanie\NodeJS\Hydronide\node_modules\passport-oauth2\lib\strategy.js
E:\Programowanie\NodeJS\Hydronide\node_modules\oauth\lib\oauth2.js
This will help you to get the root cause of parsing error . Seems like there is some problem with request/response data.
I solved the issue by checking this route
app.get('/auth',passport.authenticate('google',{
scope:['profile','email']
}));
I was trying to do something to new users and for that I was trying to get the users from the database if I get that I will do that work otherwise simply redirect to somewhere but the problem I faced is
you can check this route by consoling the log
const express = require('express');
const router = express.Router();
const { User } = require('../models/user.model');
const jwt = require('jsonwebtoken');
const config = require('../config/config.json');
const role = require('../lib/role');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
router.use(passport.initialize());
passport.serializeUser((user, cb) => {
cb(null, user);
});
passport.deserializeUser((obj, cb) => {
cb(null, obj);
});
passport.use(new GoogleStrategy({
clientID: "sssssssssssssssssssssssssss",
clientSecret: "Vsssssssssssssss",
callbackURL: "http://localhost:4000/api/auth/google/callback"
},
(request, accessToken, refreshToken, profile, cb) => {
User.findOne({ email: profile.emails[0].value }, (err, user) => {
if (err) {
cb(err); // handle errors!
}
if (!err && user !== null) {
cb(err, user);
}
else {
user = new User({
googleId: profile.id,
email: profile.emails[0].value,
firstname: profile.name.givenName,
lastname: profile.name.familyName,
role: role.Client,
isActive: true,
isGain: false,
});
user.save((err) => {
if (err) {
cb(err); // handle errors!
} else {
cb(null, user);
}
});
}
});
}
));
router.get('/', passport.authenticate('google', { session: false, scope: ['profile', 'email'] }));
// callback
router.get('/callback', passport.authenticate('google', { failureRedirect: '/failed' }),
(req, res) => {
const token = jwt.sign({ userId: req.user._id, email: req.user.email, role: req.user.role }, config.secret_key, { expiresIn: '10 h' })
res.status(200).json({ success: true, token, expireIn: `${new Date().getTime() + 120000}` })
}
);
//failed auth google
router.get('/failed', async (req, res) => { res.status(404).send('erreur authentification') })
module.exports = router;
In passport.js you need to change callbackURL from 'auth/google/callback' to '/auth/google/callback'. Do not forget to add '/' before auth.
passport.use(new googleStrategy({
clientID:keys.clientID,
clientSecret:keys.clientSecret,
callbackURL:'/auth/google/callback'
},(accessToken,refreshToken, profile,done)=>{
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
}
));
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/home"
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({ username: profile.displayName, googleId: profile.id },
function (err, user) {
return cb(err, user);
});
}));

Mongoose User.findOne neither return error nor success

In passport-facebook Strategy, when I query using Mongoose User.findOne() it doesn't return error or success object. Nodejs when come to this block, escape this block User.findOne(){...} without giving any error and success i.e.silently escape.
passport.use(new FacebookStrategy({
clientID: config.get('facebook.clientID'),
clientSecret: config.get('facebook.clientSecret'),
callbackURL: config.get('facebook.callbackURL'),
passReqToCallback: true,
profileFields: ['id', 'email', 'first_name', 'last_name']
},
function(req, accessToken, refreshToken, profile, done) {
// check if the user is already logged in
let fbInfo = profile._json;
// console.log(fbInfo);
if (!req.user) {
User.findOne({ 'email': fbInfo.email }, function(err, user) {
if (err)
return done(err);
if (user) {
I think you don't make it inside the if statement.
Change if (!req.user) to if (req.user) and it should work.
--
However here is some sample code using Passport Facebook authentication strategy:
passport.use(new FacebookStrategy({
clientID: oauth.facebook.clientID,
clientSecret: oauth.facebook.clientSecret,
callbackURL: oauth.facebook.callbackURL,
// passReqToCallback: true,
profileFields: ['id', 'emails', 'name']
},
function (accessToken, refreshToken, profile, done) {
process.nextTick(function () {
User.findOne({
$or: [
{ 'facebook.id': profile.id },
{ 'email': profile.emails[0].value }
]
}, function (err, user) {
if (err) {
return done(err)
}
if (user) {
if (user.facebook.id == undefined) {
user.facebook.id = profile.id
user.facebook.token = accessToken
user.facebook.email = profile.emails[0].value
user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName
user.save()
}
return done(null, user)
} else {
let newUser = new User()
...
newUser.save(err => {
if (err) {
console.log(err)
throw err
}
return done(null, newUser)
})
}
})
})
}
))

Auto login while using passport-google-oauth20

I followed a course and it implemented user authentication using passport, passport-google-oauth20, cookie-session and it all works fine (login, logout, session handling) but when i send a request for a Log in/Sign Up it doesnt ask/prompt the google authentication window to enter the credentials, it always logs in with the same account.
Here is the passport-strategy configuration:
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const mongoose = require('mongoose');
const keys = require('../config/keys');
const User = mongoose.model('users');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
passport.use(
new GoogleStrategy(
{
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
callbackURL: '/auth/google/callback',
proxy: true,
authorizationParams: {
access_type: 'offline',
approval_prompt: 'force'
}
},
async (accessToken, refreshToken, profile, done) => {
const existingUser = await User.findOne({ googleID: profile.id })
if (existingUser) {
// we already have a record with the given profile ID
return done(null, existingUser);
}
// we don't have a user record with this ID, make a new record!
const user = await new User({ googleID: profile.id, name: profile.displayName }).save()
done(null, user);
})
);
Add prompt: 'select_account' to the passport.authenticate() middleware in your /auth/google route.
app.get('/auth/google', passport.authenticate('google', {
scope: ['profile', 'email'],
prompt: 'select_account'
});
Visit this page: https://developers.google.com/identity/protocols/OpenIDConnect#scope-param

Save accessToken to local storage using passport-facebook

I'm new to MEAN Stack, I'm having trouble saving passport-facebook accessToken to localStorage. How do I do this? Below is my setup.
passport.use(new FacebookStrategy({
clientID: passport_config.facebook.clientID,
clientSecret: passport_config.facebook.clientSecret,
callbackURL: passport_config.facebook.callbackURL
},
function(accessToken, refreshToken, profile, done) {
FBAccount.findOne({fbId : profile.id}, function(err, oldUser){
if(oldUser){
done(null,oldUser);
}else{
var newUser = new FBAccount({
fbId : profile.id ,
name : profile.displayName
}).save(function(err,newUser){
if(err) throw err;
console.log(newUser);
done(null, newUser);
});
}
});
}
));
Try this
var localStorage = require('localStorage')
localStorage.setItem('accessToken', accessToken);
FBAccount.findOne({ ....
You can also add token in the cookies by some middleware like
passport.use(new FacebookStrategy({
clientID: '566950043453498',
clientSecret: '555022a61da40afc8ead59c6c26306ed',
callbackURL: 'http://www.localhost:3000/auth/facebook/callback'
}, function(accessToken, refreshToken, profile, done) {
console.log("hello " + profile.displayName);
done(null);
}
));
//Authentication
app.get('/auth/facebook', passport.authenticate('facebook'));
router.get('/auth/facebook/callback', passport.authenticate('facebook', {
failureRedirect: '/login?failedSocial=facebook'
}), auth.authCallback);
and in auth service
exports.authCallback = function (req, res) {
res.res.cookie('token', JSON.stringify(req.user.token));
res.redirect('/');
}

Resources