0

Situation

I am providing a Google login option in my android app using google's recommended GoogleSignInClient and GoogleSignInOptions. The GoogleSignInOptions are specified like this:

val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestServerAuthCode(webClientConfiguration.clientId.clientId)
    .requestEmail()
    .requestScopes(ScopesProvider.CLOUD_MANAGEMENT_SCOPE, ScopesProvider.FIREBASE_SCOPE)
    .build()

These options mean:

  • we require an authorization code
  • the users email
  • a few scopes(not explained more detailed here)
  • and we are using the default sign in.

After the use logged in with his the callback gives us an GoogleSignInAccount object(see google documentation for more detailed infos).

What my app now needs is an access token as it needs access to certain resources that are included within the permission scopes shown above. We simply request this token like it is specified in google's oauth2 documentation (using OkHttp3 in this example):

    val httpUrl = HttpUrl.parse(webClientConfiguration.tokenUri.toString())!!.newBuilder()
        .addQueryParameter("client_id", webClientConfiguration.clientId.clientId)
        .addQueryParameter("client_secret", webClientConfiguration.clientId.clientSecret)
        .addQueryParameter("code", account!!.serverAuthCode)
        .addQueryParameter("grant_type", "authorization_code")
        .addQueryParameter("redirect_uri", "http://localhost:1234")
        .build()

    val tokenRequest = Request.Builder()
        .method(
            "POST", RequestBody.create(
                MediaType.parse("application/x-www-form-urlencoded"),
                ""
            )
        )
        .url(httpUrl)
        .build()

    val client = OkHttpClient.Builder().build()

    client.newCall(tokenRequest).enqueue(
        object : okhttp3.Callback {
            override fun onFailure(call: okhttp3.Call, e: IOException) {
                Log.d(LOGTAG, "token exchange request failed", e)
            }

            override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
                Log.d(LOGTAG, "repsonse! ${response.body()!!.string()}")
            }

        }
    )

As specified in the docs, this will return a json object with all the required information:

{
      "access_token": "<your-individual-access-token>",
      "expires_in": 3599,
      "id_token": "<user-signin-id-token>"
    }

The problem

This request does not contain the required refreshToken. As the initial request(which contains the refreshtoken) which the GoogleSignIn takes care of is supposed to signal the server that we need an refresh token. As discussed here, we would need to add an access_type: offline parameter to the authentication request. But we can't edit the request, since the GoogleSignIn client takes care of this. How can we recieve an refresh token?

laim2003
  • 299
  • 4
  • 19

1 Answers1

3

After researching for a long time I found a simple solution which works in my case. When creating the GoogleSignInOptions, we tell the GoogleSignIn that we need an authorization code:

.requestServerAuthCode(webClientConfiguration.clientId.clientId)

it turns out that you literally only have to add true as the second parameter("forceCodeForRefreshToken"):

.requestServerAuthCode(webClientConfiguration.clientId.clientId, true)

Then the client will return a different authorization code, which in return will make the google oauth server give you a refresh token when using the code in an request to get an access token.

laim2003
  • 299
  • 4
  • 19