1

We are updating our application to use the CredentialManager for logging in, but we are not finding a way to get the serverAuthCode from this new implementation. We need this information to maintain compatibility with our backend, which expects this information instead of the idToken.

Our old implementation:

val task = GoogleSignIn.getSignedInAccountFromIntent(activityResult.data)
val account = try {
    task.getResult(ApiException::class.java)
} catch (e: ApiException) {
    // Something treatment here
    null
}
val serverAuthCode = account?.serverAuthCode
// endpoint call

New implementation:

val googleIdOption = GetSignInWithGoogleOption.Builder("our serverClientId").build()
val request = GetCredentialRequest.Builder()
    .addCredentialOption(googleIdOption)
    .build()
val credentialManager = CredentialManager.create(context)
val response = credentialManager.getCredential(context, request)
val credential = response.credential
if (credential is CustomCredential && credential.type == TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
    try {
        val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
        // rest of the code and endpoint call

As I mentioned above, in this new implementation, it is not possible to get the serverAuthCode, only the idToken.

How should we go about getting the serverAuthCode and maintaining compatibility with our backend?

Here’s how I’m handling the authorization:

val authorizationLauncher = rememberAuthorizationLauncher()

val requestedScopes = listOf(
    Scope(Scopes.PLUS_ME),
    Scope(Scopes.PROFILE),
    Scope("https://www.googleapis.com/auth/user.birthday.read"),
    Scope("https://www.googleapis.com/auth/user.gender.read"),
    Scope("https://www.googleapis.com/auth/userinfo.email")
)

val authorizationRequest = AuthorizationRequest.builder().setRequestedScopes(requestedScopes).build()

Identity.getAuthorizationClient(context)
    .authorize(authorizationRequest)
    .addOnSuccessListener { authorizationResult ->
        if (authorizationResult.hasResolution()) {
            authorizationResult.pendingIntent?.intentSender?.let { intentSender ->
            authorizationLauncher.launch(IntentSenderRequest.Builder(intentSender).build())
            }
        } else {
            authorizationResult.serverAuthCode // always null
        }
    }

@Composable
private fun rememberAuthorizationLauncher(
    activity: Activity = LocalActivity.current
) = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.StartIntentSenderForResult()
) { activityResult ->
    if (activityResult.resultCode == Activity.RESULT_OK) {
        Identity.getAuthorizationClient(activity).getAuthorizationResultFromIntent(activityResult.data)
    }
}

2 Answers 2

1

CredentialManager APIs allow you do "authentication". In order to get an auth code, you'd need to do authorization, for which you can use the Authorization APIs (which is not part of the CredentialManager).

14
  • First, I'm authenticating the user (using CredentialManager) and then requesting the authorization (using AuthorizationRequest), is that correct? Even so, I couldn't get the serverAuthCode, it is coming always as null. Commented Jul 5 at 19:31
  • To use the Authorization APIs, you don't need to Authenticate the user first; you can directly call the Authorization APIs, unless you really want to authenticate your users regardless. If the Authorization APIs are not working for you, please include code snippets of what you are doing.
    – Ali Naddaf
    Commented Jul 7 at 16:05
  • We really need to perform the authentication first. So what I'm basically doing is calling the Credential Manager to authenticate, checking if everything worked fine, and then calling the Authorization API as added in the post. Commented Jul 8 at 21:17
  • That definitely works. Also, as a general comment, one main reason for us to separate authentication from authorization was to strongly encourage developers to ask for "authorization" in context; for example, in the old APIs, it was very common to include authentication and authorization together and that was presented at the very first "sign-in" page of the app; at that moment, users may not have the context on why this app is asking for, say, drive access. However, by separating the two, an app developer is encouraged to only ask for authorization... (to be continued )...
    – Ali Naddaf
    Commented Jul 9 at 1:46
  • .. (continued)... when there is a need foe that, at the moment that it make sense to ask for that from a user; for example when a game wants to save the score in user's drive; it is more likely for users to accept such requests if they fully understand why it is being asked and how it wil be used.
    – Ali Naddaf
    Commented Jul 9 at 1:47
0

The solution for this is to request offline access when building the authorization request:

AuthorizationRequest.builder()
    .setRequestedScopes(requestedScopes)
    .requestOfflineAccess(SERVER_CLIENT_ID) // THIS!
    .build()

See a working sample here

Not the answer you're looking for? Browse other questions tagged or ask your own question.