Tom Harrington
Tom Harrington

Reputation: 70976

Logging out with Google OAuth on iOS

I'm using Google's GTMAppAuth to prompt users to log in and authorize access to their Google account. That much is working, and API calls work as expected.

What's not working is logging out. In the GTMAppAuth code, removing authorization is handled by setting the GTMAppAuthFetcherAuthorization instance to nil, so that the app can't make API calls for the user account.

Except, when you reauthorize, Google's authorization flow does not require a password to get authorization back. It shows a list of previously used accounts and asks which one you want. If you choose on one, voila, you're in! No need for a password. I have to ask the user's permission, but what if it's a different user? They can get in to the previous user's account, and I need to prevent that. For my app this is not an unusual scenario.

So how do I really log out, so that a password would be required to reauthenticate? I'm setting my own GTMAppAuthFetcherAuthorization to nil, and I'm making sure to remove Google's keychain entries, but still no password is required.

Upvotes: 1

Views: 1300

Answers (2)

Tom Harrington
Tom Harrington

Reputation: 70976

In the end the only thing that does what I need is to load Google's "logout" page, as described in another answer. Basically, load https://www.google.com/accounts/Logout.

I had thought that the approach would be to revoke the app's OAuth tokens, but that's not actually what I need. I can revoke them, but Google will then issue another one without requiring a password (or it might be the same key-- it doesn't really matter to me if no password is required). I gather that this is based on browser cookies, but since I'm using SFSafariViewController on iOS I can't inspect the cookies. Revocation is a waste of time if tokens will be reissued like this.

Loading the logout page seems like kind of a hack but it has the useful effect of clearing out whatever browser state allows resuming access without requiring a password from the user.

On iOS this could produce an annoying UI artifact of displaying a web view when the user didn't expect one. But it's easy to prevent that by presenting the SFSafariViewController in a way that keeps the web view hidden. I did it like this, but there are other approaches.

func logout(presentingViewController:UIViewController?) -> Void {
    guard let presentingViewController = presentingViewController else {
        fatalError("A presenting view controller is required")
    }

    let logoutUrl = URL(string: "https://www.google.com/accounts/Logout")!
    let logoutVC = SFSafariViewController(url: logoutUrl)
    logoutVC.delegate = self

    presentingViewController.addChildViewController(logoutVC)
    presentingViewController.view.addSubview(logoutVC.view)
    presentingViewController.view.sendSubview(toBack: logoutVC.view)
    logoutVC.didMove(toParentViewController: presentingViewController)

    // Remove our OAuth token
    self.authorization = nil
}

The delegate assignment is important. In the delegate, I implemented this to dismiss the SFSafariViewController as soon as the logout page loads:

func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
    controller.didMove(toParentViewController: nil)
    controller.view.removeFromSuperview()
    controller.removeFromParentViewController()
}

Upvotes: 2

Mihir Thanekar
Mihir Thanekar

Reputation: 518

So I understand that you want the user to have to re-enter their password no matter what.

In that case a sub- optimal strategy of revoking the access tokens and disconnecting the app from given account could work. See: https://github.com/google/GTMAppAuth/issues/9 which leads you to https://developers.google.com/identity/protocols/OAuth2InstalledApp#tokenrevoke with their REST api because it's not natively supported in the library you are using.

Aside from that, a better strategy could be to consider the usage of the Cocoapods GoogleSignIn as they natively support the revoking feature. See: https://developers.google.com/identity/sign-in/ios/disconnect

Sidenote: Google's auth flow is designed that way so users can return to the app as quickly as possible, and since most people don't share their phones with others, you wouldn't really have to revoke their certificates.

Hope this helped! Good luck :)

Upvotes: 1

Related Questions