25

We currently use google-api-python-client to handle our user-facing OAuth flow. We had a few users email us asking why their accounts didn't import successfully, and when we looked, their tokens didn't have the appropriate scopes. For example, we expect to find ALL of these scopes:

https://www.googleapis.com/auth/gmail.metadata openid https://www.googleapis.com/auth/userinfo.email

But we are only receiving the last 2. When we looked at the OAuth flow, we noticed that our OAuth flow looks like this (app name and account name redacted). Notice that there is a checkbox for the requested scope, and that checkbox is by default unchecked. (From mid 2020 to about a week ago, this flow had 2 additional confirmation steps, but that last checkbox was checked by default.)

enter image description here

Compare that to the OAuth flow for popular app Fantastical (many others, including Spark, look similar):

enter image description here

After finding this StackOverflow answer which links to this post on Google's blog, it appears that some apps are grandfathered in to the second flow, but the first flow is the new default. Two questions:

  1. The checkbox being unselected by default seems to be a relatively recent change, and I can't find any documentation about it. Is it the new default behavior, or is there a parameter / something in the OAuth flow we can change to have it checked by default? When it's unchecked, users assume they don't need to check it, so they press continue and then our app has to throw an error and explain to the user that they need to check that scope checkbox. It seems a bit crazy that now the checkbox is unchecked since it's a scope I'm explicitly asking for...it's the whole point of this flow.

  2. Is there any way to make our flow consistent with that older, much easier flow that Fantastical, Spark, and any other OAuth application created before ~2018 or so still has? Or is there an ETA for those other grandfathered apps to be transitioned to the new flow? The above Google blogpost says the new flow will be "extended to existing clients at the beginning of 2019", so we're ~2.5 years overdue on that change. As it stands, it puts "new" OAuth apps (ours was created in 2019, so hardly new...) at an incredible disadvantage because it's more steps, requires explicit consent, is more prone to user error, and makes it look like our app is in any way less scrutinized or "secure" because the flow is so different from what users have been trained to do for many other apps.

It's been an issue for months now, so also putting this post up to help any others in the same position. Some additional points in case it's helpful for anyone:

  • If I take the client_id and redirect_url from that Fantastical flow and use it in the link from our app, the flow looks the same as theirs. So it seems to be driven by the client ID, not the URL or something happening in our flow.

  • Our app has gone through Google's third party security assessment and passed, so it's likely not related to the scope we're requesting or our app's status in the approval process

  • Both our flow and Fantastical's happen in webviews, so it's unlikely that it's related to one being native vs. webview

  • This happens with several different scopes we request, so it's unlikely that it's related to the scopes we're asking for (e.g. Fantastical's scopes are more expansive / scrutinized even more harshly than the gmail.metadata scope in our flow)

zzz
  • 613
  • 1
  • 8
  • 13
  • 1
    Have you managed to find a solution for that? – Victor Buldakov Jun 10 '22 at 08:32
  • 1
    @VictorBuldakov no — as far as I can tell, it's a changed behavior for OAuth applications created in the last 1-2 years, and applications / keys created before then are grandfathered in and will be transitioned to this new flow eventually – zzz Jul 15 '22 at 15:10
  • I am facing the same. Have you found any solution for that problem? – Sandhiya Nov 23 '22 at 11:42
  • @Sandhiya still no solution as far as I know, unfortunately – zzz Nov 25 '22 at 02:37

3 Answers3

5

In my case (Android app) the disabled checkboxes appeared when requesting the scopes together with the sign-in options.

I avoided it by separating the sign-in from the scope request in two steps, like this:

First, do the sign-in with the requestEmail option only. For the user, this is a dialog where they can select the account, but it already grants basic stuff (profile, email and openid):

GoogleSignInOptions options =
        new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestEmail()
                .build();

GoogleSignInClient signInClient =
        GoogleSignIn.getClient(context, options);

signInClient
        .addOnCompleteListener(task -> {
            signInLauncher.launch(signInClient.getSignInIntent());
        });

Later, when handling the sign-in result, request the additional scopes (Google Drive in my case) and include the email scope again:

GoogleSignIn.requestPermissions(
        this,
        requestCode,
        account,
        emailScope, driveScope);

It's important to request the email scope again because otherwise it would be null when coming back from the permission request. But as it was already granted at sign-in, it doesn't appear on the checkboxes.

As pointed out at the end of Google's article, 'profile', 'email' and 'openid' are allowed at sign-in.

Credit goes to Max, who pointed out this approach in a comment to this answer.

jmart
  • 2,769
  • 21
  • 36
  • Doesn't it mean that you get 3 different dialogs this way, though? Meaning: 1. Choose account from list 2. grant basic stuff (email, server auth-code). 3. scopes ? On Android, this is what I got. Indeed it helped against the checkboxes not being checked (in fact they were disabled for #2 and didn't exist for #3), but it also means 3 steps for the user... – android developer Nov 24 '22 at 14:14
  • @androiddeveloper In my case, I only get two dialogs: 1. Choose account, which already grants basic stuff. 2. Scopes (I only request for Google Drive access as additional scope). – jmart Nov 24 '22 at 15:27
  • But do you request there to get email and server auth-code (or whatever it's called) ? – android developer Nov 25 '22 at 18:31
  • 1
    @androiddeveloper I revised my answer and included the code, so you can see the approach I'm using – jmart Nov 26 '22 at 11:30
  • At first I thought you are right, but after resetting the granted stuff on the Google account settings, now it shows 3 dialogs, as I said. But I use requestServerAuthCode too, and not just requestEmail. At least there is no checkbox... Also, on another app, probably because it uses an old certificate, it won't show anything on the last step of the scopes (shows something that immediately gets closed) – android developer Nov 27 '22 at 10:05
  • I think that perhaps, for old certificates the result is a bit random. Sometimes 2 dialogs, sometimes 3. Same for whether I see a checkbox or not in case I request all scopes right away (without "requestPermissions") . – android developer Nov 27 '22 at 11:55
0

The way I ended up doing it is by inspecting the response by the Google oAuth server in my front-end.

In the response, it includes a space-separated string of the scopes that were granted (checkbox checked) by the user i.e.

"scope": "email profile openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/calendar.events"

Now we can use this info to see if the checkboxes for scopes we needed are there or not.

For example if I know my app needs https://www.googleapis.com/auth/calendar.events scope, then I can check to see if that string is in the response scope.

Based on that I can stop the process and let the user know that they must mark the checkbox and present the consent dialog again.

Not sure if this will work in all cases, but it's the current solution I'm using.

amerikan
  • 29
  • 4
0

What worked for me was setting

enable_serial_consent: false

BUT as its mentioned here

more granular Google Account permissions will be disabled for OAuth client IDs created before 2019. No effect for newer OAuth client IDs, since more granular permissions is always enabled for them.