Skip to content

refactor: clear credman state after signOut and user deletion #2191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 49 additions & 11 deletions auth/src/main/java/com/firebase/ui/auth/AuthUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
Expand All @@ -32,13 +33,15 @@
import com.firebase.ui.auth.util.data.PhoneNumberUtils;
import com.firebase.ui.auth.util.data.ProviderAvailability;
import com.firebase.ui.auth.util.data.ProviderUtils;
import com.google.android.gms.auth.api.identity.Identity;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.ActionCodeSettings;
Expand Down Expand Up @@ -68,6 +71,8 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import androidx.annotation.CallSuper;
import androidx.annotation.DrawableRes;
Expand All @@ -76,6 +81,9 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.StringDef;
import androidx.annotation.StyleRes;
import androidx.credentials.ClearCredentialStateRequest;
import androidx.credentials.CredentialManagerCallback;
import androidx.credentials.exceptions.ClearCredentialException;

/**
* The entry point to the AuthUI authentication flow, and related utility methods. If your
Expand Down Expand Up @@ -280,8 +288,9 @@ public Task<Void> signOut(@NonNull Context context) {
if (!playServicesAvailable) {
Log.w(TAG, "Google Play services not available during signOut");
}

return signOutIdps(context).continueWith(task -> {
signOutIdps(context);
Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
return clearCredentialState(context, singleThreadExecutor).continueWith(task -> {
task.getResult(); // Propagate exceptions if any.
mAuth.signOut();
return null;
Expand All @@ -303,9 +312,10 @@ public Task<Void> delete(@NonNull final Context context) {
String.valueOf(CommonStatusCodes.SIGN_IN_REQUIRED),
"No currently signed in user."));
}

return signOutIdps(context).continueWithTask(task -> {
task.getResult(); // Propagate exception if there was one.
signOutIdps(context);
Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
return clearCredentialState(context, singleThreadExecutor).continueWithTask(task -> {
task.getResult(); // Propagate exceptions if any.
return currentUser.delete();
});
}
Expand Down Expand Up @@ -338,15 +348,43 @@ public int getEmulatorPort() {
return mEmulatorPort;
}

private Task<Void> signOutIdps(@NonNull Context context) {
private void signOutIdps(@NonNull Context context) {
if (ProviderAvailability.IS_FACEBOOK_AVAILABLE) {
LoginManager.getInstance().logOut();
}
if (GoogleApiUtils.isPlayServicesAvailable(context)) {
return GoogleSignIn.getClient(context, GoogleSignInOptions.DEFAULT_SIGN_IN).signOut();
} else {
return Tasks.forResult((Void) null);
}
}

/**
* A Task to clear the credential state in Credential Manager.
* @param context
* @param executor
* @return
*/
private Task<Void> clearCredentialState(
@NonNull Context context,
@NonNull Executor executor
) {
TaskCompletionSource<Void> completionSource = new TaskCompletionSource<>();

ClearCredentialStateRequest clearRequest = new ClearCredentialStateRequest();
GoogleApiUtils.getCredentialManager(context)
.clearCredentialStateAsync(
clearRequest,
new CancellationSignal(),
executor,
new CredentialManagerCallback<>() {
@Override
public void onResult(Void unused) {
completionSource.setResult(unused);
}

@Override
public void onError(@NonNull ClearCredentialException e) {
completionSource.setException(e);
}
}
);
return completionSource.getTask();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import androidx.credentials.exceptions.GetCredentialException
import com.firebase.ui.auth.AuthUI.EMAIL_LINK_PROVIDER
import com.firebase.ui.auth.util.ExtraConstants.GENERIC_OAUTH_BUTTON_ID
import com.firebase.ui.auth.util.ExtraConstants.GENERIC_OAUTH_PROVIDER_ID
import com.firebase.ui.auth.util.GoogleApiUtils
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
Expand All @@ -105,7 +106,7 @@ class AuthMethodPickerActivity : AppCompatBase() {
// For demonstration, assume that CredentialManager provides a create() method.
private val credentialManager by lazy {
// Replace with your actual CredentialManager instance creation.
CredentialManager.create(this)
GoogleApiUtils.getCredentialManager(this)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.credentials.CredentialManager;

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final class GoogleApiUtils {
Expand All @@ -25,4 +26,9 @@ public static boolean isPlayServicesAvailable(@NonNull Context context) {
public static SignInClient getSignInClient(@NonNull Context context) {
return Identity.getSignInClient(context);
}

@NonNull
public static CredentialManager getCredentialManager(@NonNull Context context) {
return CredentialManager.create(context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import android.app.Application
import android.content.Context
import androidx.lifecycle.viewModelScope
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CredentialManager
import androidx.credentials.CreateCredentialResponse
import androidx.credentials.exceptions.CreateCredentialException
import com.firebase.ui.auth.ErrorCodes
import com.firebase.ui.auth.FirebaseUiException
import com.firebase.ui.auth.IdpResponse
import com.firebase.ui.auth.data.model.Resource
import com.firebase.ui.auth.util.GoogleApiUtils
import com.firebase.ui.auth.viewmodel.AuthViewModelBase
import com.google.firebase.auth.FirebaseUser
import kotlinx.coroutines.launch

class CredentialManagerHandler(application: Application) :
AuthViewModelBase<IdpResponse>(application) {

private val credentialManager = CredentialManager.create(application)
private val credentialManager = GoogleApiUtils.getCredentialManager(application)
private var response: IdpResponse? = null

fun setResponse(newResponse: IdpResponse) {
Expand Down
Loading