Search code examples
androidgoogle-signinandroid-googleapiclientgoogle-smartlockpasswordsgooglesigninaccount

Android: Google Sign In - credential token list is always empty


I'm implementing the google smart lock sign in to automatically log a user in without input, however I'm running into the problem that the token list (getIdTokens) returned in the credential object token list is always empty even after a "successful" connection. At what point is the credential object actually populated with a token list?

I'm using this example to build the code:

https://github.com/googlesamples/android-credentials/blob/master/credentials-signin/app/src/main/java/com/google/example/credentialssignin/MainActivity.java#L101

private void googleSilentSignIn() {
        // Try silent sign-in with Google Sign In API
        OptionalPendingResult<GoogleSignInResult> opr =
                Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
        if (opr.isDone()) {
            GoogleSignInResult gsr = opr.get();
            handleGoogleSignIn(gsr);
        } else {
            opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
                @Override
                public void onResult(GoogleSignInResult googleSignInResult) {
                    handleGoogleSignIn(googleSignInResult);
                }
            });
        }
    }


 private void handleGoogleSignIn(GoogleSignInResult gsr) {
        Timber.i("handleGoogleSignIn:" + (gsr == null ? "null" : gsr.getStatus()));

        boolean isSignedIn = (gsr != null) && gsr.isSuccess();
        if (isSignedIn) {
            // Display signed-in UI
            GoogleSignInAccount gsa = gsr.getSignInAccount();
            String status = String.format("Signed in as %s (%s)", gsa.getDisplayName(),
                    gsa.getEmail());

            Timber.d("handleGoogleSignIn %s", status);

            // Save Google Sign In to SmartLock
            Credential credential = new Credential.Builder(gsa.getEmail())
                    .setAccountType(IdentityProviders.GOOGLE)
                    .setName(gsa.getDisplayName())
                    .setProfilePictureUri(gsa.getPhotoUrl())
                    .build();

            saveCredentialIfConnected(credential);

            Timber.d("handleGoogleSignIn: credential tokens was %s", credential.getIdTokens().toString());
}
}

  private void requestCredentials(final boolean shouldResolve, boolean onlyPasswords) {
    Timber.d("requestCredentials");

    CredentialRequest.Builder crBuilder = new CredentialRequest.Builder()
            .setPasswordLoginSupported(true);

    if (!onlyPasswords) {
        crBuilder.setAccountTypes(IdentityProviders.GOOGLE);
    }

    Auth.CredentialsApi.request(mGoogleApiClient, crBuilder.build()).setResultCallback(
            new ResultCallback<CredentialRequestResult>() {
                @Override
                public void onResult(CredentialRequestResult credentialRequestResult) {
                    Status status = credentialRequestResult.getStatus();

                    if (status.isSuccess()) {
                        // Auto sign-in success

                        Timber.d("requestCredentials:onsuccess with token size %d", credentialRequestResult.getCredential().getIdTokens().size() );

                        handleCredential(credentialRequestResult.getCredential());
                    } else if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED
                            && shouldResolve) {
                        // Getting credential needs to show some UI, start resolution
                        resolveResult(status, RC_CREDENTIALS_READ);
                    }
                }
            });
}

@Override
public void onStart() {
    super.onStart();
    if (!mIsResolving) {
        requestCredentials(true /* shouldResolve */, false /* onlyPasswords */);
    }
}


private void handleCredential(Credential credential) {

        Timber.i("handleCredential with %s %s %s %s %s", credential.getId(), credential.getAccountType(), credential.getGeneratedPassword(), credential.getName(), credential.getPassword());

        mCredential = credential;

        if (IdentityProviders.GOOGLE.equals(credential.getAccountType())) {
            // Google account, rebuild GoogleApiClient to set account name and then try
            buildGoogleApiClient(credential.getId());
            googleSilentSignIn();
        }
}

Solution

  • getIdTokens() on a credential object should return a list containing an OpenID Connect ID token when the credential has been retrieved via the Auth.CredentialsApi.request() or Auth.CredentialsApi.getHintPickerIntent() method and the credential corresponds to a Google Account signed in on a device running Play Services 8+

    Note that .setAccountTypes(IdentityProviders.GOOGLE) should be included when constructing the request:

        CredentialRequest request = new CredentialRequest.Builder()
                .setAccountTypes(IdentityProviders.GOOGLE)
                .setSupportsPasswordLogin(true)
                .build();
    

    or when getting a hint if no saved credential is available:

        HintRequest hintRequest = new HintRequest.Builder()
                .setAccountTypes(IdentityProviders.GOOGLE)
                .setEmailAddressIdentifierSupported(true)
                .build();
    

    There won't be an ID token if the credential is constructed with the Credential.Builder (as shown in part of the code in the question), or if the credential does not match the email address of a Google Account on the device.

    So some things to check:

    • test on a device running the latest version of Play Services (8.4)

    • ensure that the retrieved credential matches a Google Account signed in on the device and that the account is in good standing (is in sync, receiving emails, does not need password to be re-entered, etc.)

    • verify that the credential retrieved has been previously saved in the app or associated website (via Chrome password manager), or came from a HintRequest built with .setAccountType(IdentityProviders.GOOGLE)

    If you still have trouble getting an ID token, leave a comment with the details of your environment.