10

I'm attempting to use the new incremental authorization for an installed app in order to add scopes to an existing authorization while keeping the existing scopes. This is done using the new include_granted_scopes=true parameter. However, no matter what I've tried, the re-authorization always overwrites the scopes completely. Here's a minimal Bash PoC script I've written to demo my issue:

client_id='716905662885.apps.googleusercontent.com' # throw away client_id (non-prod)
client_secret='CMVqIy_iQqBEMlzjYffdYM8A' # not really a secret
redirect_uri='urn:ietf:wg:oauth:2.0:oob'

while :
do
  echo "Please enter a list of scopes (space separated) or CTRL+C to quit:"
  read scope

  # Form the request URL
  # http://goo.gl/U0uKEb
  auth_url="https://accounts.google.com/o/oauth2/auth?scope=$scope&redirect_uri=$redirect_uri&response_type=code&client_id=$client_id&approval_prompt=force&include_granted_scopes=true"

  echo "Please go to:"
  echo
  echo "$auth_url"
  echo
  echo "after accepting, enter the code you are given:"
  read auth_code

  # swap authorization code for access token
  # http://goo.gl/Mu9E5J
  auth_result=$(curl -s https://accounts.google.com/o/oauth2/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d code=$auth_code \
    -d client_id=$client_id \
    -d client_secret=$client_secret \
    -d redirect_uri=$redirect_uri \
    -d grant_type=authorization_code)
  access_token=$(echo -e "$auth_result" | \
               grep -Po '"access_token" *: *.*?[^\\]",' | \
               awk -F'"' '{ print $4 }')

  echo
  echo "Got an access token of:"
  echo $access_token
  echo

  # Show information about our access token
  info_result=$(curl -s --get https://www.googleapis.com/oauth2/v2/tokeninfo \
    -H "Content-Type: application/json" \
    -d access_token=$access_token)
  current_scopes=$(echo -e "$info_result" | \
                   grep -Po '"scope" *: *.*?[^\\]",' | \
                   awk -F'"' '{ print $4 }')

  echo "Our access token now allows the following scopes:"
  echo $current_scopes | tr " " "\n"
  echo
  echo "Let's add some more!"
  echo

done

The script simply performs OAuth authorization and then prints out the scopes the token is currently authorized to use. In theory it should continue to add scopes each time through but in practice, the list of scopes is getting overwritten each time. So the idea would be on the first run, you'd use a minimal scope of something like email and then the next run, tack on something more like read-only calendar https://www.googleapis.com/auth/calendar.readonly. Each time, the user should only be prompted to authorize the currently requested scopes but the resulting token should be good for all scopes including those authorized on previous runs.

I've tried with a fresh client_id/secret and the results are the same. I know I could just include the already authorized scopes again but that prompts the user for all of the scopes, even those already granted and we all know the longer the list of scopes, the less likely the user is to accept.

UPDATE: during further testing, I noticed that the permissions for my app do show the combined scopes of each incremental authorization. I tried waiting 30 seconds or so after the incremental auth, then grabbing a new access token with the refresh token but that access token is still limited to the scopes of the last authorization, not the combined scope list.

UPDATE 2: I've also toyed around with keeping the original refresh token. The refresh token is only getting new access tokens that allow the original scopes, the incrementally added scopes are not included. So it seems effectively that include_granted_scopes=true is having no effect on the tokens, the old and new refresh tokens continue to work but only for their specified scopes. I cannot get a "combined scope" refresh or access token.

Jay Lee
  • 13,415
  • 3
  • 28
  • 59
  • I am having the exact same problem (subsequent access tokens are only valid for the scopes requested in that auth link, not any scopes requested earlier, even though include_granted_scopes was true). This makes incremental auth completely unusable for us and forces us to maintain different access tokens for the different scopes we need access to, which is really annoying. Anyone got any insight into why this doesn't work (at least for some of us)? – David Apr 15 '14 at 13:46

2 Answers2

9

Google's OAuth 2.0 service does not support incremental auth for installed/native apps; it only works for the web server case. Their documentation is broken.

  • Yep, I can confirm this. I've just run into this problem and ended up here after googling for 30 seconds. Incremental authorization does not work for installed apps. – GetFree Mar 13 '15 at 04:49
1

Try adding a complete list of scopes to the second request, where you exchange authorization code for an access token. Strangely enough, scope parameter doesn't seem to be documented, but it is present in requests generated by google-api-java-client. For example:

code=foo&grant_type=authorization_code
&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fmyapp%2FoauthCallback
&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.me+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.stream.write

In the web server scenario, a complete list of granted scopes is returned together with authorization code when include_granted_scopes is set to true. This is another bit of information that seems to be missing from linked documentation.

Edit 1 Including a complete list of scopes in the code exchange request works for us in our Java app, but I have just tried your original script with no modification (except for client id/secret) and it works just fine (edited just the ids and tokens):

$ bash tokens.sh
Please enter a list of scopes (space separated) or CTRL+C to quit:
https://www.googleapis.com/auth/userinfo.profile
Please go to:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/userinfo.profile&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=189044568151-4bs2mcotfi2i3k6qp7vq8c6kbmkp2rf8.apps.googleusercontent.com&approval_prompt=force&include_granted_scopes=true

after accepting, enter the code you are given:
4/4qXGQ6Pt5QNYqdEuOudzY5G0ogru.kv_pt5Hlwq8UYKs_1NgQtlUFsAJ_iQI

Got an access token of:
ya29.1.AADtN_XIt8uUZ_zGZEZk7l9KuNQl9omr2FRXYAqf67QF92KqfvXliYQ54ffg_3E

Our access token now allows the following scopes:
https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/plus.me
https://www.googleapis.com/auth/plus.circles.read

You can see that the previously granted scopes are included...

serengeti
  • 19
  • 2
  • I tried this but I'm finding that if scope parameter includes the "old scopes", I get an invalid scopes error on the exchange request. – Jay Lee Mar 13 '14 at 18:53
  • You know what, I just tried using your script without modification and it seems to work just fine. I'll update my answer above in a minute. – serengeti Mar 14 '14 at 07:36
  • I can reproduce this error with the given script (and my own server-side app), the access token(s) I get from subsequent request(s) is/are always only valid for the scopes requested for that call, not the scopes requested earlier. This makes incremental auth unusable for us. – David Apr 15 '14 at 13:44