I am implementing a Facebook authentication using Passport in node.js. I can get the users profile from Facebook successfully. The problem is, the code should redirect the user to the successRedirect: /profile but instead it always goes to /fbFailure. No matter what I tried, it always goes to the url : somesite.com:6161/fbFailure#=
Passport middleware code:
var FacebookStrategy = require('passport-facebook').Strategy;
var models = require('../models');
passport.use('facebook', new FacebookStrategy(
{
clientID: '1735552370024305', clientSecret: 'a004f1aee925b615022d7573984d5c26', callbackURL: "http://www.somesite.com:6161/facebookSuccess", profileFields: ['id', 'displayName', 'photos', 'emails'],
},
function(access_token, refreshToken, profile, done) {
console.log('profile', profile);
models.Member.findOne({ '__id' : profile.id }, function(err, user) {
if (err)
return done(err);
if(user){
return done(null, user);
}
done(null, user);
})
}
));
My Routes:
app.get('/facebook', passport.authenticate('facebook', { scope : 'email' }));
app.get('/facebookSuccess', passport.authenticate('facebook', {
successRedirect : '/profile',
failureRedirect : '/fbFailure'
}));
app.get('/profile', function(req, res, next) {
res.send('Successfully authenticated');
});
app.get('/fbFailure', function(req, res, next) {
res.send('Failed to authenticate');
});
Console Output:
json:
{ id: '10154130288931542',
name: ‘Tom Verra’,
picture: { data: [Object] },
email: ‘tom.vera#mysite.com' } }
GET /facebookSuccess?code=AQBzdIfKPQ..............YU6TvYuf......
Any help would be highly appreciated. Thanks in advance.
You could try redirecting using res.redirect('/profile');, just the way it is done in the passport-facebook readme on GitHub (https://github.com/jaredhanson/passport-facebook#authenticate-requests):
app.get('/facebookSuccess',
passport.authenticate('facebook', { failureRedirect: '/fbFailure' }),
function(req, res) {
res.redirect('/profile');
});
Related
I am trying to signUp and login with passport for my project but i am new in passport and i have some isuues, i have a sequelize database that have (name,password,email), when i post /users it it never go to serializeUser and it load forever.
here is my post request for signUp:
router.post('/', async function(req, res, next) {
console.log(req.body, 'create new user');
const user = await User.create(
{ Name: req.body.Name, Password: req.body.Password, Email: req.body.Email }
);
const userss = await User.findOne({ where: { Email: req.body.Email } });
console.log(user, userss);
console.log('1');
try {
console.log('inserting');
await user.save(function(err) {
console.log(user.dataValues.id);
req.logIn(user.dataValues.id, function(err) { // here is the problem!
console.log('logIn');
console.log('inserted');
return res.redirect('/');
});
console.log('inserted?');
});
} catch (err) {
console.log(err);
return res.render('users/new', { user, error: req.flash('error') });
}
});
and here is my passport:
passport.serializeUser(function(user, done) {
console.log(user);
console.log('serialized1');
done(null, user);
console.log('serialized2');
return;
});
passport.deserializeUser(async function(id, done) {
console.log('here0');
const user = await User.findByPk(id);
console.log('here2');
done(null, user);
console.log('here4');
});
and here is the login rout:
router.post('/',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/sessions',
failureFlash: true
}),
function(req, res, next) {
console.log('new user connected');
socketio.io.sockets.emit('msg', `New user connected: ${req.user}`);
}
);
It appears that you have both two routes pointing to the same path.
I would recommend updating the login routes path.
router.post('/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/sessions',
failureFlash: true
}),
The issue described in the title also happens with Passport v0.6.0 due to this issue in the library. So req.login() silently does nothing, serializeUser() never gets called, and moving back to Passport v0.5.3 solves the issue.
I'm trying to setup google auth with PassportJS, but my deserializeUser function is never called.
This is my passport.js config:
...
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(sessionUser, done) {
User.findById(sessionUser, function(err, user) {
done(err, user);
});
});
passport.use('google', new GoogleStrategy({
clientID : process.env.GOOGLE_CLIENTID,
clientSecret : process.env.GOOGLE_CLIENTSECRET,
callbackURL : process.env.GOOGLE_CALLBACKURL,
passReqToCallback: true,
},
function(req, token, refreshToken, profile, done) {
process.nextTick(function() {
// console.log(profile);
var values = {
where: { google_id: profile.id },
defaults: {google_id: profile.id, name: profile.displayName}
};
User.findOrCreate(values)
.spread(function(user, created) {
return done(null,user);
});
});
}
));
And these are my routes:
app.get('/auth/google',
passport.authenticate('google', { scope : ['profile', 'email'] }));
// the callback after google has authenticated the user
app.get('/auth/google/callback',
passport.authenticate('google', {
successRedirect : '/portfolio/crypto',
failureRedirect : '/'
}));
And since deserializeUser is never called, req.user is never defined. How can I fix this?
I'm using passport-github for authenticating with GitHub, but every time a user logs in, the access token updates. Is that intended behaviour?
Strategy configuration and Express routing:
passport.use(new GithubStrategy({
clientID: process.env.IMPAKT_ID,
clientSecret: process.env.IMPAKT_SECRET,
callbackURL: process.env.IMPAKT_CALLBACK
}, function(accessToken, refreshToken, profile, cb) {
User.findOneAndUpdate({id: profile.id}, {accessToken, username: profile.username}, {upsert: true, new: true}, function(err, user) { // Update code, since access token changes.
return cb(err, user);
});
}
));
app.get("/auth", passport.authenticate('github', {scope: ['public_repo']}));
app.get("/auth/callback", passport.authenticate('github', { failureRedirect: '/auth' }), function(req, res) {
res.redirect('/');
});
I'm building a Node application in which the users must register or login, then when they drag and drop some elements (the front end is all working) I store on the database their action with their corresponding userId.
My understanding is that once they are registered/logged in, I can use the req.user to access their id and correctly store their actions, however it isn't working.
Here is the section of my server.js file that deals with Passport. Also, I'm using Sequelize as an ORM, but everything dealing with the database works perfect without the req.user part.
app.use(cookieParser());
app.use(bodyParser.json());
app.use(passport.initialize());
app.use(passport.session());
/****** Passport functions ******/
passport.serializeUser(function (user, done) {
console.log('serialized');
done(null, user.idUser);
});
passport.deserializeUser(function (id, done) {
console.log("start of deserialize");
db.user.findOne( { where : { idUser : id } } ).success(function (user) {
console.log("deserialize");
console.log(user);
done(null, user);
}).error(function (err) {
done(err, null);
});
});
//Facebook
passport.use(new FacebookStrategy({
//Information stored on config/auth.js
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: configAuth.facebookAuth.callbackURL,
profileFields: ['id', 'emails', 'displayName', 'name', 'gender']
}, function (accessToken, refreshToken, profile, done) {
//Using next tick to take advantage of async properties
process.nextTick(function () {
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(err) {
return done(err);
}
if(user) {
return done(null, user);
} else {
//Create the user
db.user.create({
idUser : profile.id,
token : accessToken,
nameUser : profile.displayName,
email : profile.emails[0].value,
sex : profile.gender
});
//Find the user (therefore checking if it was indeed created) and return it
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(user) {
return done(null, user);
} else {
return done(err);
}
});
}
});
});
}));
/* FACEBOOK STRATEGY */
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback//
app.get('/auth/facebook', passport.authenticate('facebook', { scope : ['email']}));
/* FACEBOOK STRATEGY */
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/' }),
function (req, res) {
// Successful authentication, redirect home.
res.redirect('../../app.html');
});
app.get('/', function (req, res) {
res.redirect('/');
});
app.get('/app', isLoggedIn, function (req, res) {
res.redirect('app.html');
});
app.post('/meal', function (req, res) {
//Testing Logs
/*console.log(req.body.foodId);
console.log(req.body.quantity);
console.log(req.body.period);
console.log(req.body);
*/
//Check whether or not this is the first food a user drops on the diet
var dietId = -1;
db.diet.findOne( { where : { userIdUser : req.user.idUser } } ).then(function (diet, err) {
if(err) {
return done(err);
}
if(diet) {
dietId = diet.idDiet;
} else {
db.diet.create( { userIdUser : req.user.idUser }).then(function (diet) {
dietId = diet.idDiet;
});
}
});
db.meal.create({
foodId : req.body.foodId,
quantity : req.body.quantity,
period : req.body.period
}).then(function (meal) {
console.log(meal.mealId);
res.json({ mealId : meal.mealId});
});
});
From what I read on the documentation for Passport, the deserializeUser function that I implemented should be called whenever I use req.user, however, with my console.logs(), I found out that serializeUser is called after logging in, therefore it is storing my session, but deserializeUser is never called! Ever.
Any idea on how to get around this? Any help is appreciated, thank you!
You need the express session middleware before calling passport.session(). Read the passportjs configuration section on documentation for more info.
Make sure to set cookieParser and express-session middlewares, before setting passport.session middleware:
const cookieParser = require('cookie-parser')
const session = require('express-session')
app.use(cookieParser());
app.use(session({ secret: 'secret' }));
app.use(passport.initialize());
app.use(passport.session());
To test if passport session is working or not, use:
console.log(req.session.passport.user)
(put in on a middleware for example)
In my case, i was using LocalStrategy and i was thinking i can protect and endpoint with simple username and password as form parameters, and i though passport will only use form parameters when it can't find user in session. but it was wrong assumption. in passport localStrategy, you should have separate endpoints for login and protected endpoint.
So Make sure you're using right middlewares for each endpoints. in my case:
wrong:
Protected endpoint:
app.get('/onlyformembers', passport.authenticate('local'), (req, res) => {
res.send({"res": "private content here!"})
})
correct :
Login:
app.post('/login', passport.authenticate('local'), (req, res) => {
res.send('ok')
})
Protected endpoint:
var auth = function (req, res, next) {
if (req.isAuthenticated())
return next();
res.status(401).json("not authenticated!");
}
app.get('/onlyformembers', auth, (req, res) => {
res.send({"res": "private content here!"})
})
I am trying to set up goals on Google Analytics to track Sign Ups, so I set up a 'thank you ' page as my url goal. It works well when my users sign up with their email address but not when they use facebook to sign up/login. When they login, they are redirected to the thank you page as there is only one url callback when setting up Facebook using Passport JS and Node.
Here is my code:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(id, done) {
UserActivity.findOne(id,'uid ref', function (err, user) {
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: 'XXXXXXXXXXXX',
clientSecret: 'XXXXXXXXXXXXXXXX',
callbackURL: "https://www.xxxxxxx.com/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
//console.log(profile);
User.findOne({ uid: profile.id }, function(err, uInfo) {
if(err) console.log('Error: '+err);
else{
//User exists: we are done
if(uInfo){
done(err, uInfo);
}
else{
//User doesn't exist: we create a new one
var newUser = new User ({
uid: profile.id,
email:profile.emails[0].value,
ref: 'Facebook'
});
// Saving it to the database.
newUser.save(function (err,uInfo) {
if (err) console.log ('Error on save!');
done(err, uInfo);
});
}
}
})
}
));
app.get('/auth/facebook', passport.authenticate('facebook',{ scope: 'email' }));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { successRedirect: '/thankyou',
failureRedirect: '/login' }));
If the user exists, I would like to redirect to their dashboard ('/dashboard) and if they are new users, I need to redirect them to /thankyou.
Any idea how to achieve this?
Thanks a lot!
Nevermind, found the answer. Here is the updated code below. Pay attention to the use of passReqToCallback and req.session.newu
passport.use(new FacebookStrategy(
{
clientID: 'XXX',
clientSecret: 'XXX',
callbackURL: "https://www.XXX.co/auth/facebook/callback",
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
//console.log(profile);
User.findOne({ uid: profile.id }, function(err, uInfo) {
if(err) console.log('Error: '+err);
else{
if(uInfo){
done(err, uInfo);
}
else{
var newUser = new User ({
uid: profile.id,
email:profile.emails[0].value,
ref: 'Facebook'
});
// Saving it to the database.
newUser.save(function (err,uInfo) {
if (err) console.log ('Error on save!');
req.session.newu=true;
done(err, uInfo);
});
}
}
})
}
));
app.get('/auth/facebook', passport.authenticate('facebook',{ scope: 'email' }));
app.get('/auth/facebook/callback',function(req, res, next) {
passport.authenticate('facebook', 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); }
var redLink = '/dashboard';
if(req.session.newu)redLink = '/dashboard?newu'
return res.redirect(redLink);
});
})(req, res, next);
});
An existing user will be redirected to /dashboard and a new user will be redirected to /dashboard?newu
Google Analytics doesn't need 2 different urls, it just needs a query string. When I set up the url goal, I selected url start with /dashboard?newu.
Hope this helps
The question is a bit old but it might still help someone like me. The OP's answer works but it means you have to take care of log-in user and session, etc. In case you still want to leave those work to PassportJS, use req.session.returnTo in strategy's callback with successReturnToOrRedirect option in passport.authenticate() would work.