jaredhanson / passport-google-oauth

Google authentication strategies for Passport and Node.js.
https://www.passportjs.org/packages/passport-google-oauth/?utm_source=github&utm_medium=referral&utm_campaign=passport-google-oauth&utm_content=about
MIT License
776 stars 327 forks source link

Security of Signed In User #95

Closed samuelkilada closed 9 years ago

samuelkilada commented 9 years ago

This is more of a general question about the security of this strategy. In the example for OAuth2, if I were to take the profile ID and search for that user in my MySql database, and then create the user or pull data from the user based on the profile.id value, is that secure? Example:

passport.use('google', new GoogleStrategy({
    clientID: clientId,
    clientSecret: clientSecret,
    callbackURL: "[callback url here]"
}, function (accessToken, refreshToken, profile, done) {
    users.findByUserId(profile.id, function (err, user) {
        return done(err, user);
    });
}));

I ask because this page seems to say that using the userId is dangerous (see the red box at the top): https://developers.google.com/identity/sign-in/web/backend-auth

Am I right in understanding that this strategy is properly validating the tokens, so the final profile.id value you get at this point is legit and you can safely pull the user data based only on the userId, or do I need to provide additional validation as described in the linked document at this point in the code? It would be super helpful if this info was in the docs.

Thanks, Sam

konradjurk commented 9 years ago

You don't have to do further validation for the actual google authentication step.

However, the evaluation to which user a specific request belongs to is part of your application and of course - as google sates - you should not send a userId as authentication to your backend API. How you should do it instead depends on what you would like to do. Most likely the request will come from a browser so it's rather simple. You could have a middleware that checks if there is a user serialized into the current session and if not --> throw a 401.

If you want to make authenticated HTTP requests with curl (or something like that) you should consider to use OAuth. :)

samuelkilada commented 9 years ago

Thanks for the response! I am using express-session so I'm able to do req.isAuthenticated, but there still comes a point where I need to pull user data from my database, and the only piece of data that stays the same is the userId (I can't use the token as a password or it will change). I'm just not sure how to go about doing this.

samuelkilada commented 9 years ago

The docs seem to reference this page for token validation in nodejs. Do I just need to call the function highlighted on this page and pass it the token and the audience (I assume the client ID of the app), and if it succeeds I can safely use the profile.id?

https://github.com/google/google-auth-library-nodejs/blob/master/lib/auth/oauth2client.js#L384

samuelkilada commented 9 years ago

I say that because you get the profile and the token at the same time in the passport authentication method, so maybe I could do something like this?

passport.use('google', new GoogleStrategy({
clientID: clientId,
clientSecret: clientSecret,
callbackURL: "[callback url here]"
}, function (accessToken, refreshToken, profile, done) {
   googleAuthLib.verifyIdToken(accessToken, clientId, function(err, login) {
      if (!err) {
        users.findByUserId(profile.id, function (err, user) {
            return done(err, user);
        });
      }
   });
}));

Am I on the right track here? If this validation succeeds it seems to suggest that the profile.id is associated with a valid token, in which case it should be safe to pull data from the database based on the profile.id, and at any point after that if the user passes req.isAuthenticated. Let me know if I'm totally missing the point.

samuelkilada commented 9 years ago

Just thought I'd say that more or less what I said in my last post is what I ended up doing. It's not using the googleAuthLib as shown above, but an oauthClient created using the google-auth-library module. See the bottom of this page: https://developers.google.com/identity/sign-in/web/backend-auth

You can also look at the rest of the steps that come before that to show how the id token was retrieved.

I should mention that this approach required that I switch back to a local strategy instead of the google strategy on this github page. This is because the token returned is not quite the right one to use for verifyIdToken, so I had to rethink it. It seems this google strategy works better for endpoint token validation, using a url like the following: https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123

That wasn't going to work for me because the Google docs seem to state that endpoint validation only works for a limited amount of users, and my app needs to be scalable.