Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If user doesn't accept additional additional (not necessary scopes), return access_denied #370

Closed
mikeyyyzhao opened this issue Feb 27, 2024 · 8 comments
Labels
wontfix This will not be worked on

Comments

@mikeyyyzhao
Copy link

mikeyyyzhao commented Feb 27, 2024

Describe the bug
If a user does not accept the additional scopes. E.g. Calendar access but not contacts, the login module returns access_denied instead of the auth token.
Screenshot 2024-02-27 at 4 15 57 PM

However, for new users accepting scope (first time logging into an app), if the user doesn't accept the additional scopes, auth continues fine and returns the token. This bug only happens on subsequent tries when the user is prompted with additional scopes and do not accept them.

The exact same flow is working fine on web on first and nth try.

To Reproduce
Steps to reproduce the behavior:

  1. Login with existing account (already accept the mandatory scopes)
  2. Do not accept the additional scopes
  3. get access_denied error

Expected behavior
A clear and concise description of what you expected to happen.
For additional scopes, it should still return the user object with serverAuthCode, similar to how it works on web.

Environment

  • Device: iPhone
  • OS: iOS 17
@mikeyyyzhao mikeyyyzhao added bug Something isn't working triage Issues that need to be triaged labels Feb 27, 2024
@mikeyyyzhao mikeyyyzhao changed the title If user doesn't accept additional scopes (not necessary scopes), return access_denied If user doesn't accept additional additional (not necessary scopes), return access_denied Feb 27, 2024
@ywsang
Copy link
Contributor

ywsang commented Feb 28, 2024

Hi, thanks for the issue! Unfortunately I'm unable to test/repro this so far. Is the screenshot you shared above from the screen that looks like this?:
image

Are you able to reproduce the issue in the sample apps (in this folder)?

If not, would you be able to answer the following:

  • Which specific GIDSignIn methods are you calling, and which methods work as expected and which result in the bug?
  • Are there more details about the error (is there additional error info beyond access_denied)?
  • Do you get the bug if you accept a subset of the additional scopes? What about if you accept all of the additional scopes?

@mikeyyyzhao
Copy link
Author

mikeyyyzhao commented Feb 29, 2024

I've added our code for auth for reference (you just need to plug in your clientID and serverClientID. It's based off the birthday example so it should be fairly straight forward to plug in.

Steps to reproduce:

  1. Login with an google account that is new to the app
  2. Accept every permission minus the two contacts permissions (contacts.readonly and contacts.other.readonly")
  3. Log out (this is to simulate logging in again - we have both a web app and an iOS app so following these steps makes it so you don't have to use a web app)
  4. just for extra safety -> quit the app (swipe up) so we remove local caches
  5. login again -> still do not accept the two contact permissions
  6. the response from the library is access_denied

Note: if you remove access to the app through accounts.google.com -> third party access -> remove access to the app, you can repeat the steps above using the same email so this way, you don't have to recreate emails to test.

Are there more details about the error (is there additional error info beyond access_denied)?

  • Not getting much else other than access_denied unfortunately. I saw another ticket about giving back more detailed errors but I don't think that's been implemented in the library yet.

Do you get the bug if you accept a subset of the additional scopes? What about if you accept all of the additional scopes?

  • if you accept all the scopes it works fine. However, if you follow the same steps above on web (without accepting all permissions), you still get the auth code back.
private let additionalScopes = [
        "email",
        "profile",
        "https://www.googleapis.com/auth/calendar",
        "https://www.googleapis.com/auth/contacts.readonly",
         "https://www.googleapis.com/auth/contacts.other.readonly",
         "https://www.googleapis.com/auth/admin.directory.resource.calendar.readonly",
         "https://www.googleapis.com/auth/admin.directory.user.readonly",
     ]

    private lazy var configuration: GIDConfiguration = {
        return GIDConfiguration(clientID: clientID, serverClientID: serverClientId)
    }()
    
    private let authViewModel: AuthenticationViewModel
    
    /// Creates an instance of this authenticator.
    init(authViewModel: AuthenticationViewModel) {
        self.authViewModel = authViewModel
    }
    
     func signIn() {
         guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
               let window = windowScene.windows.first,
               let rootViewController = UIApplication.shared.windows.first?.rootViewController else {
             print("There is no root view controller!")
             return
         }
         GIDSignIn.sharedInstance.configuration = configuration
         GIDSignIn.sharedInstance.signIn(
             withPresenting: rootViewController,
             hint: nil,
             additionalScopes: additionalScopes
    ) { user, error in
        print("serverAuthCode: \(user?.serverAuthCode)")
        ....

@ywsang
Copy link
Contributor

ywsang commented Mar 5, 2024

Thanks for all the detail! I was able to repro the issue in the DaysUntilBirthday sample app. I'll investigate further, but my initial thought is that somewhere within the auth flow, not giving any additional access to scopes (i.e. in step 5, where the UI presents this dialog:
image)
gets interpreted as an access denied error, despite the user already having granted access to scopes previously.

@mdmathias mdmathias removed the triage Issues that need to be triaged label Mar 5, 2024
@mikeyyyzhao
Copy link
Author

Thank you @ywsang! Let me know if there's anything else that I can provide that could be helpful

@ywsang
Copy link
Contributor

ywsang commented Mar 11, 2024

Hi @mikeyyyzhao! I did some research and have learned that it's the expected behavior for the Google OAuth server to return an access_denied error if no new consent is granted (which is the error being propagated back to you via the GoogleSignIn-iOS library).

Is there a specific reason why you need to ask for non-consented scopes on the user signing-in again? It seems that the recommendation would be to separate sign-in from the request for additional scopes, so that if the user signs out, signing in again does not ask for additional access (and the result would be a successful re-sign in). There's some additional guidance in the documentation here: https://developers.google.com/identity/protocols/oauth2/policies#unbundled-consent.

I'm still working to understand what the expectation here would be for the GoogleSignIn-iOS library. Could you let me know more about the specific integration that you're using for Google Sign In on the web, i.e. which SDK you're using, version info, etc?

@mikeyyyzhao
Copy link
Author

mikeyyyzhao commented Mar 13, 2024

@ywsang since the user is logged out, there's no way for us to know what the user has accepted? Therefore, we always show the default set of permissions that we need. GoogleSignIn-iOS has tried to separate the two steps in the past but reverted because it provides a terrible user experience.

Also, following the same steps above on web produces a valid auth code. We're not using a sdk for web but just a redirect URL

const googleLoginURL =
    `https://accounts.google.com/o/oauth2/auth?` +
    `client_id=${GOOGLE_CLIENT_ID}` +
    `&redirect_uri=${REDIRECT_URL}` +
    `&scope=${GOOGLE_LOGIN_SCOPES}` + // same scopes as iOS
    `&prompt=consent select_account` +
    "&access_type=offline" +
    "&include_granted_scopes=true" +
    `&response_type=code`

If the user accepts the same scopes as before, it should keep granting the auth code. I don't fully understand how/why not granting it would be the expected behavior.

@mikeyyyzhao
Copy link
Author

@ywsang just following up on this as this bug is critical for apps that have are on both mobile and web

@ywsang
Copy link
Contributor

ywsang commented May 21, 2024

Hi @mikeyyyzhao, sorry for the delay here!

I've confirmed that this is the intended behavior of GoogleSignIn-iOS. As you've mentioned, this doesn't provide the best experience for the user, but while we are exploring how we may improve the end user experience, the current recommendation remains that you separate user sign-in from the request for scopes. I understand that this experience may not be the most ideal, but it is the current best practice and will at least unblock the sign-in experience for your app.

@ywsang ywsang closed this as completed May 21, 2024
@ywsang ywsang added wontfix This will not be worked on and removed bug Something isn't working labels May 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

3 participants