Passport & JWT & Google Strategy - Disable session & res.send() after google callback
Asked Answered
G

2

15

Using: passport-google-oauth2.

I want to use JWT with Google login - for that I need to disable session and somehow pass the user model back to client. All the examples are using google callback that magically redirect to '/'.

How do I:
1. Disable session while using passport-google-oauth2.
2. res.send() user to client after google authentication.

Feel free to suggest alternatives if I'm not on the right direction.

Goraud answered 27/11, 2016 at 12:29 Comment(2)
Check this out - It explains pretty clearly. cloud.google.com/nodejs/getting-started/authenticate-usersMelbamelborn
@MuliYulzary Thanks, but in the example you gave there is an excessive use of session which I try to avoid.Goraud
G
15

Manage to overcome this with some insights:
1. disable session in express - just remove the middleware of the session

// app.use(session({secret: config.secret}))

2. when using Google authentication what actually happens is that there is a redirection to google login page and if login is successful it redirect you back with the url have you provided.

This actually mean that once google call your callback you cannot do res.send(token, user) - its simply does not work (anyone can elaborate why?). So you are force to do a redirect to the client by doing res.redirect("/"). But the whole purpose is to pass the token so you can also do res.redirect("/?token=" + token).

app.get( '/auth/google/callback',
        passport.authenticate('google', {
            //successRedirect: '/',
            failureRedirect: '/'
            , session: false
        }),
        function(req, res) {
            var token = AuthService.encode(req.user);
            res.redirect("/home?token=" + token);
        });

But how the client will get the user entity? So you can also pass the user in the same way but it didn't felt right for me (passing the whole user entity in the parameter list...). So what I did is make the client use the token and retrieve the user.

    function handleNewToken(token) {
        if (!token)
            return;

        localStorageService.set('token', token);

        // Fetch activeUser
        $http.get("/api/authenticate/" + token)
            .then(function (result) {
                setActiveUser(result.data);
        });
    }

Which mean another http request - This make me think that maybe I didnt get right the token concept. Feel free to enlighten me.

Goraud answered 4/12, 2016 at 15:54 Comment(8)
have you come with some sort of a solution? I have the same problem and I can't set headers after Google callback redirectGarland
Please check passport-google-oauth-jwt also very useful if you don't want to use sessionHepato
And If you don't want to use session please use passpor-jwtHepato
@Hepato not sure if its relevant to the discussed issue, Can you please elaborate?Goraud
@Goraud As you were looking for session less authentication. passport-jwt is quite suitable for auth without session. And if any user comes here he/she will get the related info.Hepato
@Hepato How do I create no session in that code(textuploader.com/dlkxz) without using serialize/deserialize method? I am going to use JWT instead of session.Vanhorn
@Vanhorn youre question is not clear - could you elaborate?Goraud
I'm using the same technique: what I do is that the first redirect with the ?token= is sending a short-lived token stored in my DB, then when the client gets this token and makes a loginWithToken request, I generate a normal long term token thus allowing the user to login.Scopophilia
B
1

Initialize passport in index.js:

app.use(passport.initialize());

In your passport.js file:

passport.use(
new GoogleStrategy(
{
 clientID: process.env.GOOGLE_CLIENT_ID,
 clientSecret: process.env.GOOGLE_CLIENT_SECRET,
 callbackURL: 
'http://localhost:3000/auth/google/redirect',
},
async (accessToken, refreshToken, profile, 
callback) => {
  // Extract email from profile
  const email = profile.emails![0].value;

  if (!email) {
    throw new BadRequestError('Login failed');
  }

  // Check if user already exist in database
  const existingUser = await User.findOne({ email 
});

  if (existingUser) {
    // Generate JWT
    const jwt = jwt.sign(
      { id: existingUser.id },
      process.env.JWT_KEY,
      { expiresIn: '10m' }
    );

    // Update existing user
    existingUser.token = jwt
    await existingUser.save();

    return callback(null, existingUser);
  } else {

    // Build a new User
    const user = User.build({
      email,
      googleId: profile.id,
      token?: undefined
    });

    // Generate JWT for new user
      const jwt = jwt.sign(
      { id: user.id },
      process.env.JWT_KEY,
      { expiresIn: '10m' }
    );
   
    // Update new user
    user.token = jwt;

    await auth.save();

    return callback(null, auth);
  }
}));

Receive this JWT in route via req.user

app.get('/google/redirect', passport.authenticate('google', 
{failureRedirect: '/api/relogin', session: false}), (req, res) => {
// Fetch JWT from req.user
const jwt = req.user.token;
req.session = {jwt}
// Successful authentication, redirect home
res.status(200).redirect('/home');
}
Bussard answered 10/3, 2022 at 21:35 Comment(1)
Nice one, this was the bit that was throwing me. Specifically including the failureRedirect and session: false in the redirect route passport.authenticate('google', {failureRedirect: '/api/relogin', session: false}) Thanks for the suggestion!Onaonager

© 2022 - 2024 — McMap. All rights reserved.