3
votes

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?

3
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
@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

3 Answers

5
votes

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
        });
0
votes

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.

0
votes

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);
    });   } ));