Google OAuth 2.0 include_granted_scopes not working for installed app
bash, google-api, google-oauth, oauth-2.0
Solution
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.
Problem
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.