javaandroidandroid-credential-manager

What is the replacement for GoogleSignIn.getLastSignedInAccount when migrating to Credential Manager API/ Authorization API?


GoogleSignIn.getLastSignedInAccount is deprecated.

May I know, what is the alternative when migrating to Credential Manager API / Authorization API?

Currently, I do not use Credential Manager API.

I am using Authorization API to perform client login based on code snippet - https://developers.google.com/identity/authorization/android

List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder().setRequestedScopes(requestedScopes).build();
Identity.getAuthorizationClient(this)
        .authorize(authorizationRequest)
        .addOnSuccessListener(
            authorizationResult -> {
              if (authorizationResult.hasResolution()) {
                    // Access needs to be granted by the user
                PendingIntent pendingIntent = authorizationResult.getPendingIntent();
                try {
                    // This code will enable user to perform login.
                    startIntentSenderForResult(pendingIntent.getIntentSender(),
REQUEST_AUTHORIZE, null, 0, 0, 0, null);
                } catch (IntentSender.SendIntentException e) {
                Log.e(TAG, "Couldn't start Authorization UI: " + e.getLocalizedMessage());
                }
              } else {
            // Access already granted, continue with user action
                saveToDriveAppFolder(authorizationResult);
              }
            })
        .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

How I can get the last login account information?


I have tried the following code snippet from AI. But, it is not what I want. The following code snippet, will prompt a login dialog. What I want to do, is silently retrieve the last login account I use to access Google Drive.

enter image description here

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.credentials.CredentialManager;
import androidx.credentials.CredentialManagerCallback;
import androidx.credentials.GetCredentialRequest;
import androidx.credentials.GetCredentialResponse;
import androidx.credentials.exceptions.GetCredentialException;
import com.google.android.libraries.identity.googleid.GetGoogleIdOption;
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential;

public class AuthManager {
    private final Context context;
    private final CredentialManager credentialManager;

    public AuthManager(Context context) {
        this.context = context;
        this.credentialManager = CredentialManager.create(context);
    }

    public interface AuthCallback {
        void onSuccess(GoogleIdTokenCredential credential);
        void onFailure(Exception e);
    }

    public void getLastSignedInAccount(AuthCallback callback) {
        GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
                .setServerClientId(WENOTE_CLOUD_STORAGE_CLIENT_ID)
                .build();

        GetCredentialRequest request = new GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOption)
                .build();

        android.util.Log.i("CHEOK", "Call getCredentialAsync");

        credentialManager.getCredentialAsync(
                context,
                request,
                null,   // pass in a CancelationSignal to allow cancelling the request
                Runnable::run,          // Execute the callback immediately
                new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
                    @Override
                    public void onResult(@NonNull GetCredentialResponse result) {
                        android.util.Log.i("CHEOK", "onResult " + result);

                        if (result != null) {
                            GoogleIdTokenCredential credential = (GoogleIdTokenCredential) result.getCredential();
                            callback.onSuccess(credential);
                        } else {
                            callback.onFailure(new Exception("No credential found"));
                        }
                    }

                    @Override
                    public void onError(@NonNull GetCredentialException e) {
                        android.util.Log.i("CHEOK", "onError " + e);

                        // Handle errors
                        callback.onFailure(e);
                    }
                }
        );
    }
}

Using Identity.getAuthorizationClient

Another approach I have been trying is using Identity.getAuthorizationClient. But, it is giving me null all-time-time.

private void xxx() {
    Log.i("CHEOK", "xxx");

    List<Scope> requestedScopes = Arrays.asList(
            new Scope(DriveScopes.DRIVE_APPDATA),
            new Scope("email"),
            new Scope("profile"),
            new Scope("openid")
    );

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

    Identity.getAuthorizationClient(getContext())
            .authorize(authorizationRequest)
            .addOnSuccessListener(authorizationResult -> {
                if (!authorizationResult.hasResolution()) {
                    // No user intervention required, we have the user details
                    GoogleSignInAccount account = authorizationResult.toGoogleSignInAccount();
                    Log.i("CHEOK", "Email = " + account.getEmail());
                    Log.i("CHEOK", "getDisplayName = " + account.getDisplayName());
                    Log.i("CHEOK", "getId = " + account.getId());
                    Log.i("CHEOK", "getIdToken = " + account.getIdToken());
                }
            })
            .addOnFailureListener(e -> Log.e("CHEOK", "Failed to authorize", e));
}

Solution

  • There is no replacement for that API in the Identity or CredentialManager worlds. Here is what I suggest you do:

    Now let's see how this works: if a user is new, following the above flow results in seeing the bottomsheet, user selects an account and you get an ID Token that has the user information (so you take a note of that), they go in and at the right moment, you prompt them for Drive access, they accept the consent and you get an AccessToken. So all is good for that flow. Now, the next time the user opens your app, you do the same as above but this time, since the user had previously signed into your app, they will not be prompted with the bottomsheet as before; instead they see a bottomsheet that only shows up for a second or two, telling the user that he/she is being signed in (and requires no interaction from the user) and disappears on its own and you will get an ID Token which has the user info (which you will take a note of it) and again when you reach the appropriate moment, you call, as outlined above, the Authorization APIs but this time you get an AccessToken immediately (with no user interaction required) and you are good to go.

    Does this flow give you what you want?