2
votes

Hi I have the Google OAuth 2 explicit flow working according to:

https://developers.google.com/identity/protocols/OAuth2WebServer

I also have the implicit flow working on iOS with GIDSignIn, specifically I get the GIDSignInDelegate::sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) callback and have access to user.authentication.accessToken and user.authentication.refreshToken.

I'm trying to pass that refreshToken back to our private app server so that it can make requests on behalf of the user (mainly because it's easier to just sign in with the SDK than make the front end developers deal with raw URL requests).

However, when I try to use that refresh token to get a new access token on the back end:

curl --request POST \
  --url https://www.googleapis.com/oauth2/v4/token \
  --header 'cache-control: no-cache' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'client_id={client_id}&client_secret={client_secret}&refresh_token={refresh_token}&grant_type=refresh_token'

It returns:

{ "error": "invalid_grant", "error_description": "Bad Request" }

I think the issue is that my server has a client id and secret, but iOS only has a client id. Without a secret (sometimes called key) corresponding to the iOS client id, there is no way for me to refresh the token on the back end. I was hopeful that Google would detect that both the server client id and iOS client id were registered to the same app and let the server refresh the token with its credentials, but that doesn't work.

I've looked at both the "API keys" and "OAuth 2.0 client IDs" sections at:

https://console.developers.google.com/apis/credentials

But am at a loss.

Does anyone know a curl command for refreshing a token acquired through the iOS SDK?

Or is this simply not possible?

Thanks!

2

2 Answers

1
votes

For anyone reading this, I got it working:

On a whim I tried calling the refresh token endpoint without passing client_secret (as there isn't one when using implicit flow on mobile). Here is the curl:

curl --request POST \
  --url https://www.googleapis.com/oauth2/v4/token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'client_id={client_id}&refresh_token={refresh_token}&grant_type=refresh_token'

This appears to be undocumented. The closest example I found is at https://developers.google.com/identity/protocols/OAuth2InstalledApp#refresh but it shows client_secret=your_client_secret& and never mentions that with implicit flow, front end web and mobile apps don't normally use a client secret.

Please don't upvote my answer, as I haven't done anything. OAuth is generally not documented very well, or contains code snippets pertaining to platform SDKs without the underlying URL requests or curl examples. SDKs are often opaque/closed-source so would require dropping down to tcpdump or packet sniffing to see what's going on. I didn't do the due diligence, I just got lucky.

I have sent feedback to Google but if this issue has affected you, you can click the gear in Gmail and "Send feedback" to hopefully get documentation updated more quickly.

0
votes

Please refer to this link: https://developers.google.com/identity/sign-in/ios/offline-access

From the reference, you have to do 2steps below to let your server able to refresh your access token

  1. Make sure your server and iOS app use credentials of the same project, 1 browser key and 1 iOS key
  2. Add this line: [GIDSignIn sharedInstance].serverClientID = your_server_client_id