问题
I have an express app which manages authentication via Passport, initially with a local strategy. To this I have just added Google sign in / account creation and almost everything works as per the docs.
The problem I have is that a user can create an account using the Google Strategy but I cannot quite get it so that an authenticated user (via the local strategy) can simply add additional Google details to their account so that they can use either the local or Google strategy.
In 'index.js' where I define my routes I define const passportGoogle = require('../handlers/google');
which has the details of my Google Strategy.
Further down in index.js
I have my authenticate
and authorise
routes:
/* GOOGLE ROUTES FOR AUTHENTICATION*/
router.get('/google',
passportGoogle.authenticate('google',
{ scope: ['profile', 'email'] }));
router.get('/google/callback',
passportGoogle.authenticate('google',
{
failureRedirect: '/',
failureFlash: 'Failed Login!',
successRedirect: '/account',
successFlash: 'You are logged in!'
}
));
/* GOOGLE ROUTES FOR AUTHORISATION - IE A USER IS ALREADY LOGGED IN AND WANTS TO CONNECT THEIR GOOGLE ACCOUNT*/
// send to google to do the authentication
router.get('/connect/google',
passportGoogle.authorize('google',
{ scope : ['profile', 'email'] }
));
// the callback after google has authorized the user
router.get('/connect/google/callback',
passportGoogle.authorize('google', {
successRedirect : '/profile',
failureRedirect : '/'
})
);
As above my Google strategy is defined in google.js
:
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var User = require('../models/User');
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENTID,
clientSecret: process.env.GOOGLE_CLIENTSECRET,
callbackURL: "http://127.0.0.1:7777/google/callback"
},
// google will send back the token and profile
function(req, token, refreshToken, profile, done) {
// console.log('here is res.locals.user'+ res.locals.user);
console.log('here is req.user'+ req.user);
// asynchronous
process.nextTick(function() {
// check if the user is already logged in
if (!req.user) {
console.log('THERE IS NO REQ.USR');
// find the user in the database based on their facebook id
User.findOne({ 'google.id': profile.id }, function(err, user) {
// if there is an error, stop everything and return that
// ie an error connecting to the database
if (err)
return done(err);
// if the user is found, then log them in
if (user) {
return done(null, user); // user found, return that user
} else {
// if there is no user found with that google id, create them
var newUser = new User();
// set all of the facebook information in our user model
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.name = profile.displayName;
newUser.email = profile.emails[0].value;
// save our user to the database
newUser.save(function(err) {
if (err)
throw err;
// if successful, return the new user
return done(null, newUser);
});
}
});
} else {
const user = User.findOneAndUpdate(
{ _id: req.user._id },
{ $set: {"user.google.id":profile.id,
"user.google.token":accessToken,
"user.google.name":profile.displayName,
"user.google.email":profile.emails[0].value
}
},
{ new: true, runValidators: true, context: 'query' }
)
.exec();
return done(null, user);
req.flash('success', 'Google details have been added to your account');
res.redirect(`back`);
}
});
}));
module.exports = passport;
However when a user is signed in and follows the link to /connect/google
a new user is always created rather than their details updated. My logging shows that if (!req.user)
condition in the Google stratgy is always firing but I'm not sure why that is since the user is definitely logged in.
Any help much appreciated!
回答1:
In order to access the req in your callback, you need a passReqToCallback: true
flag in your GoogleStrategy config object:
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENTID,
clientSecret: process.env.GOOGLE_CLIENTSECRET,
callbackURL: "http://127.0.0.1:7777/google/callback",
passReqToCallback: true
},
// google will send back the token and profile
function(req, token, refreshToken, profile, done) {
// console.log('here is res.locals.user'+ res.locals.user);
console.log('here is req.user'+ req.user);
....
})
If this flag is omitted, the expected callback form is
function(accessToken, refreshToken, profile, done){...}
So your code is looking for a user
property on the accessToken that Google sends back, which should always fail. I also bring this up because, if I'm right, other parts of your function should also be misbehaving. (Like User.findOne({'google.id': profile.id})
should always fail, because the function is called with done
as its fourth argument rather than profile
.)
来源:https://stackoverflow.com/questions/51812088/req-user-not-available-in-google-passport-strategy