google-signingoogle-oauthgoogle-api-js-clientoauth2-playground

Google OAuth 2.0 - Incremental authorization with offline access


I'm trying to implement incremental authorization with Google Oauth2 in my application.

At first, when users sign in, the application is requesting some scopes and offline access. I'm generating and storing the refreshToken in my database so I can refresh access tokens to make API calls when needed.

Now I'm going to implement a new functionality that will request a new scope, but I would like to ask for this scope only if users try to use this new functionality.

The problem I'm facing is that, when the application promts users to consent for new scope, Google is requesting access for all the previously accepted scopes.

I have tried without offline access (using 'grant' method) and it seems to be working (as the description says 'Request additional scopes to the user.'). Doing that, Google is only requesting new scope and the access token generated seems to work fine. But if I try with offline access (using 'grantOfflineAccess' method), Google asks for all the scopes. If I accept all the scopes again, it returns an authorization code and I can generate the refreshToken again, but I would like to do that only accepting the new scope.

I was wondering maybe it's not possible to do it when requesting offline access, as you need to generate a new refreshToken with all the scopes, but I couldn't find any information about this.

Am I doing anything wrong or is it not possible to achieve this with offline access?

UPDATE: Also generating the URL (instead of using the JS library) as explained here, Google asks for all the scopes. This would be an example of the generated URL:

https://accounts.google.com/o/oauth2/v2/auth?
  scope=my_new_scope&
  redirect_uri=https://myapp.example.com/callback&
  response_type=code&
  client_id=my_client_id&
  prompt=consent&
  include_granted_scopes=true

UPDATE2: I've found a reported issue here, where someone commented that it's not possible to do it for installed applications. But there is a comment explaining how to do it with web server flow, these are the steps using OAuth2 playground (https://developers.google.com/oauthplayground):

1) Select the Google Calendar scope.

2) Authorize access

3) Exchange code for token.

4) Make request to the token info endpoint: https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=...

5) Verify that the token only contains the Google Calendar scope.

6) De-select the Google Calendar scope and select the Google Drive scope.

7) Edit the URL in the authorization window and append &include_granted_scopes=true.

8) Authorize the Drive scope.

9) Exchange the code for a token.

10) Make request to the token info endpoint.

11) Verify that it contains both the Calendar and Drive scopes.

Doing the same I get Calendar scope in the first verification and Drive scope in the second one. Even using the refresh token I cannot get an access token with both scopes. After that, and this is pretty strange, I've checked the applications with access to my account and OAuth2 playground has both scopes:

enter image description here

Thank you in advance!


Solution

  • I've finally accomplished to implement incremental authorization using JS library. Using grantOfflineAccess function you can send the new scopes you want to request. By default include_granted_scopes is true when you use gapi.auth2.authorize.

    So you just need to send the new scopes, for example:

    GoogleUser.grantOfflineAccess({scope: 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/calendar'}
    

    Then you will receive an authorization code to exchange for a refreshToken. You can check everything works by generating an access_token with the new refreshToken and calling the token info endpoint: https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=...

    I'm sure this was something I've tried at first, so I guess I was doing something wrong.

    Anyways, I have decided to not use incremental authorization due to an issue with the library. Basically the problem is that, for a better user experience and to avoid problems, you should be able to use prompt: 'consent' so the user doesn't need to select an account to accept new scopes. But this is not possible and you could have problems if users select different accounts to accept different scopes from your application.