5

The question: when I authenticate a user using oauth2 (initiated from my server), how do I get the initial user id from the oauth2 callback so I can map it back to the initial auth request on my server?

Contex:

I'm working on a web app where I need to ask user to grant access to their google calendar.

Consider the oauth flow:

web client (sends request to) -> backend (sends oauth request to) -> google (grants access) -> backend (how do I know the user in this step to store the refresh_token?)

Here is more details default flow:

  • user logs in to my web app (client)
  • the web app asks the user to start oauth2 flow (client)
  • that sends the "start auth flow" request to my backend (backend)
  • on the backend I send oauth request to google like below:
const authUrl = new google.auth.OAuth2(clientId, secret, redirectUrl)).generateAuthUrl(options)
res.redirect(authUrl)
  • this redirects user to the google consent page. (google)
  • Once the user granted the permission, they are redirected back to the url specified in OAuth2 client (backend, my "callback" endpoint)
  • at this point I need to save the refresh_token to the user's database location. (backend)

So how do I understand from the "callback" that this is still the same user who started the flow?

vir us
  • 9,920
  • 6
  • 57
  • 66
  • Why do you need to keep track of where the user is in the flow? AFAIK there is not really a way to do this. When you get the access token, you should also receive some basic profile information about the person who just granted access. – Mike Wilcox Apr 28 '19 at 20:22
  • @MikeWilcox - I just need to store the "refresh_token" for that specific user. How would you achieve that? Regarding the basic information - if I'm not mistaken you are talking about google account information (like email, google id?) which is not necessary the same as the information user provided during signup with my app (they could use a different email at sign up as opposite to the google account they are using to grant the permission, hence I can't use that to match the both). – vir us Apr 29 '19 at 09:32

3 Answers3

9

It appeared that there is a parameter called state that one can use to pass specific data and google will return it back. This solves my question since I can pass user id or my session token as state and read it in the callback.

From the doc:

state Recommended.

Specifies any string value that your application uses to maintain state between your authorization request and the authorization server's response. The server returns the exact value that you send as a name=value pair in the hash (#) fragment of the redirect_uri after the user consents to or denies your application's access request.

In case of nodejs google oauth2 library that may look like this:

oauth2ClientGlobal.generateAuthUrl({
            access_type: 'offline',
            scope: this.scopes(),
            state: base64UserId // <- google will return this param back
        });
vir us
  • 9,920
  • 6
  • 57
  • 66
0

You should keep the refresh token on the server in a database. This is the most secure way and prevents attacker from accessing the refresh token over the wire and using it to access their account.

Create an id for the user, use that id to map to client data on server such as refresh token. Pass that id back to the browser and client in cookies or JWT. Then, whenever user makes a request back to your server they will always pass that id you created in cookies. You can use that id to lookup the user in the database and get the refresh token.

Mike Wilcox
  • 188
  • 2
  • 9
  • Thanks Mike. Yes, that's basically what I'm trying to do - to store the refresh token on server. The question is how to do that as long as the "callback" from google doesn't include any identifiers of the user (user id as stored in my system or signup email, for example) so that I can use that and save the token for that specific user. As the question goes, the intended flow - user starts the oauth from browser and at the end I store the refresh token on server for that user. But I'm not getting how to accomplish that as I can't identify the user in the "callback". Any ideas on that? – vir us Apr 29 '19 at 18:55
0

Try using passportJs which provides authentication using third party authentication providers.

var GoogleStrategy = require( 'passport-google-oauth2' ).Strategy;

passport.use(new GoogleStrategy({
    clientID:     GOOGLE_CLIENT_ID,
    clientSecret: GOOGLE_CLIENT_SECRET,
    callbackURL: "http://yourdormain:3000/auth/google/callback",
    passReqToCallback   : true   },   function(request, accessToken, refreshToken, profile, done) {
    User.findOrCreate({ googleId: profile.id }, function (err, user) {
      return done(err, user);
    });   } ));
hashed_name
  • 553
  • 6
  • 21
  • 1
    Thanks for the suggestion. The question is more about the fundamentals of oauth2 which I seem to be missing or misunderstanding. How do people identify the callback identity, ie who made the initial request? So far it only seem to be possible to do involving client and some sort of session. – vir us May 16 '19 at 12:17
  • In the callback function you received request parameter which contains original request parameters which can be used to distinguish between different requests. – hashed_name May 16 '19 at 12:26
  • omg, thanks a lot! Your comment lead me to this question which essentially solves my problem: https://stackoverflow.com/questions/7722062/google-oauth2-redirect-uri-with-several-parameters. If you can post your comment as an answers, I'll accept it as correct to mark this question solved – vir us May 16 '19 at 13:11