Skip to content

Commit 2ccaa0b

Browse files
authored
Merge pull request #302 from samtstern/version-1.0.0-dev
SmartLock Deletion
2 parents e3a731a + afbf1ed commit 2ccaa0b

File tree

6 files changed

+157
-4
lines changed

6 files changed

+157
-4
lines changed

app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,8 @@ public void onClick(DialogInterface dialogInterface, int i) {
114114
}
115115

116116
private void deleteAccount() {
117-
FirebaseAuth.getInstance()
118-
.getCurrentUser()
119-
.delete()
117+
AuthUI.getInstance()
118+
.delete(this)
120119
.addOnCompleteListener(new OnCompleteListener<Void>() {
121120
@Override
122121
public void onComplete(@NonNull Task<Void> task) {

auth/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,32 @@ public void onClick(View v) {
262262
}
263263
```
264264

265+
### Deleting accounts
266+
267+
With the integrations provided by FirebaseUI Auth, deleting a user is a multi-stage process:
268+
269+
1. The user must be deleted from Firebase Auth.
270+
2. SmartLock for Passwords must be told to delete any existing Credentials for the user, so
271+
that they are not automatically prompted to sign in with a saved credential in the future.
272+
273+
This process is encapsulated by the `AuthUI.delete()` method, which returns a `Task` representing
274+
the entire operation:
275+
276+
```java
277+
AuthUI.getInstance()
278+
.delete(this)
279+
.addOnCompleteListener(new OnCompleteListener<Void>() {
280+
@Override
281+
public void onComplete(@NonNull Task<Void> task) {
282+
if (task.isSuccessful()) {
283+
// Deletion succeeded
284+
} else {
285+
// Deletion failed
286+
}
287+
}
288+
});
289+
```
290+
265291
### Authentication flow chart
266292

267293
The authentication flow implemented on Android is more complex than on other

auth/src/main/java/com/firebase/ui/auth/AuthUI.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
import com.firebase.ui.auth.util.GoogleApiClientTaskHelper;
3434
import com.firebase.ui.auth.util.Preconditions;
3535
import com.firebase.ui.auth.util.ProviderHelper;
36+
import com.firebase.ui.auth.util.SmartlockUtil;
3637
import com.google.android.gms.auth.api.Auth;
38+
import com.google.android.gms.auth.api.credentials.Credential;
3739
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
3840
import com.google.android.gms.common.api.GoogleApiClient;
3941
import com.google.android.gms.common.api.Status;
@@ -42,7 +44,9 @@
4244
import com.google.android.gms.tasks.Tasks;
4345
import com.google.firebase.FirebaseApp;
4446
import com.google.firebase.auth.FirebaseAuth;
47+
import com.google.firebase.auth.FirebaseUser;
4548

49+
import java.util.ArrayList;
4650
import java.util.Arrays;
4751
import java.util.Collections;
4852
import java.util.HashSet;
@@ -307,6 +311,56 @@ public Void then(@NonNull Task<GoogleApiClient> task) throws Exception {
307311
return Tasks.whenAll(disableCredentialsTask, googleSignOutTask);
308312
}
309313

314+
/**
315+
* Delete the use from FirebaseAuth and delete any associated credentials from the Credentials
316+
* API. Returns a {@code Task} that succeeds if the Firebase Auth user deletion succeeds and
317+
* fails if the Firebase Auth deletion fails. Credentials deletion failures are handled
318+
* silently.
319+
* @param activity the calling {@link Activity}.
320+
*/
321+
public Task<Void> delete(@NonNull Activity activity) {
322+
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
323+
if (firebaseUser == null) {
324+
// If the current user is null, return a failed task immediately
325+
return Tasks.forException(new Exception("No currently signed in user."));
326+
}
327+
328+
// Delete the Firebase user
329+
Task<Void> deleteUserTask = firebaseUser.delete();
330+
331+
// Initialize SmartLock helper
332+
GoogleApiClientTaskHelper gacHelper = GoogleApiClientTaskHelper.getInstance(activity);
333+
gacHelper.getBuilder().addApi(Auth.CREDENTIALS_API);
334+
CredentialsApiHelper credentialHelper = CredentialsApiHelper.getInstance(gacHelper);
335+
336+
// Get all SmartLock credentials associated with the user
337+
List<Credential> credentials = SmartlockUtil.credentialsFromFirebaseUser(firebaseUser);
338+
339+
// For each Credential in the list, create a task to delete it.
340+
List<Task<?>> credentialTasks = new ArrayList<>();
341+
for (Credential credential : credentials) {
342+
credentialTasks.add(credentialHelper.delete(credential));
343+
}
344+
345+
// Create a combined task that will succeed when all credential delete operations
346+
// have completed (even if they fail).
347+
final Task<Void> combinedCredentialTask = Tasks.whenAll(credentialTasks);
348+
349+
// Chain the Firebase Auth delete task with the combined Credentials task
350+
// and return.
351+
return deleteUserTask.continueWithTask(new Continuation<Void, Task<Void>>() {
352+
@Override
353+
public Task<Void> then(@NonNull Task<Void> task) throws Exception {
354+
// Call getResult() to propagate failure by throwing an exception
355+
// if there was one.
356+
task.getResult(Exception.class);
357+
358+
// Return the combined credential task
359+
return combinedCredentialTask;
360+
}
361+
});
362+
}
363+
310364
/**
311365
* Starts the process of creating a sign in intent, with the mandatory application
312366
* context parameter.

auth/src/main/java/com/firebase/ui/auth/ui/account_link/SaveCredentialsActivity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.google.firebase.auth.FacebookAuthProvider;
4343
import com.google.firebase.auth.FirebaseUser;
4444
import com.google.firebase.auth.GoogleAuthProvider;
45+
import com.google.firebase.auth.TwitterAuthProvider;
4546

4647
public class SaveCredentialsActivity extends AppCompatBase
4748
implements GoogleApiClient.ConnectionCallbacks, ResultCallback<Status>,
@@ -117,7 +118,10 @@ public void onConnected(@Nullable Bundle bundle) {
117118
translatedProvider = IdentityProviders.GOOGLE;
118119
} else if (mProvider.equals(FacebookAuthProvider.PROVIDER_ID)) {
119120
translatedProvider = IdentityProviders.FACEBOOK;
121+
} else if (mProvider.equals(TwitterAuthProvider.PROVIDER_ID)) {
122+
translatedProvider = IdentityProviders.TWITTER;
120123
}
124+
121125
if (translatedProvider != null) {
122126
builder.setAccountType(translatedProvider);
123127
}

auth/src/main/java/com/firebase/ui/auth/util/SmartlockUtil.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,33 @@
22

33
import android.app.Activity;
44
import android.content.Intent;
5+
import android.support.annotation.NonNull;
56
import android.support.annotation.Nullable;
7+
import android.text.TextUtils;
8+
import android.util.Log;
69

710
import com.firebase.ui.auth.ui.FlowParameters;
811
import com.firebase.ui.auth.ui.account_link.SaveCredentialsActivity;
12+
import com.google.android.gms.auth.api.credentials.Credential;
13+
import com.google.android.gms.auth.api.credentials.IdentityProviders;
14+
import com.google.firebase.auth.EmailAuthProvider;
15+
import com.google.firebase.auth.FacebookAuthProvider;
916
import com.google.firebase.auth.FirebaseUser;
17+
import com.google.firebase.auth.GoogleAuthProvider;
18+
import com.google.firebase.auth.TwitterAuthProvider;
19+
import com.google.firebase.auth.UserInfo;
20+
21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.List;
1024

1125
/**
1226
* Helper class to deal with Smartlock Flows.
1327
*/
1428
public class SmartlockUtil {
1529

30+
private static final String TAG = "SmartLockUtil";
31+
1632
/**
1733
* If SmartLock is enabled and Google Play Services is available, start the save credential
1834
* Activity. Otherwise, finish the calling Activity with RESULT_OK.
@@ -48,6 +64,60 @@ public static void saveCredentialOrFinish(Activity activity,
4864
activity.startActivityForResult(saveCredentialIntent, requestCode);
4965
}
5066

67+
/**
68+
* Translate a Firebase Auth provider ID (such as {@link GoogleAuthProvider#PROVIDER_ID}) to
69+
* a Credentials API account type (such as {@link IdentityProviders#GOOGLE}).
70+
*/
71+
public static String providerIdToAccountType(@NonNull String providerId) {
72+
switch (providerId) {
73+
case GoogleAuthProvider.PROVIDER_ID:
74+
return IdentityProviders.GOOGLE;
75+
case FacebookAuthProvider.PROVIDER_ID:
76+
return IdentityProviders.FACEBOOK;
77+
case TwitterAuthProvider.PROVIDER_ID:
78+
return IdentityProviders.TWITTER;
79+
case EmailAuthProvider.PROVIDER_ID:
80+
// The account type for email/password creds is null
81+
return null;
82+
}
83+
84+
return null;
85+
}
86+
87+
/**
88+
* Make a list of {@link Credential} from a FirebaseUser. Useful for deleting Credentials,
89+
* not for saving since we don't have access to the password.
90+
*/
91+
public static List<Credential> credentialsFromFirebaseUser(@NonNull FirebaseUser user) {
92+
if (TextUtils.isEmpty(user.getEmail())) {
93+
Log.w(TAG, "Can't get credentials from user with no email: " + user);
94+
return Collections.emptyList();
95+
}
96+
97+
List<Credential> credentials = new ArrayList<>();
98+
for (UserInfo userInfo : user.getProviderData()) {
99+
// Get provider ID from Firebase Auth
100+
String providerId = userInfo.getProviderId();
101+
102+
// Convert to Credentials API account type
103+
String accountType = providerIdToAccountType(providerId);
104+
105+
// Build and add credential
106+
Credential.Builder builder = new Credential.Builder(user.getEmail())
107+
.setAccountType(accountType);
108+
109+
// Null account type means password, we need to add a random password
110+
// to make deletion succeed.
111+
if (accountType == null) {
112+
builder.setPassword("some_password");
113+
}
114+
115+
credentials.add(builder.build());
116+
}
117+
118+
return credentials;
119+
}
120+
51121
private static void finishActivity(Activity activity) {
52122
activity.setResult(Activity.RESULT_OK, new Intent());
53123
activity.finish();

common/constants.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ project.ext.support_library_version = '23.4.0'
33

44
project.ext.submodules = ['database', 'auth']
55
project.ext.group = "com.firebaseui"
6-
project.ext.version = '0.5.1'
6+
project.ext.version = '1.0.0-SNAPSHOT'
77
project.ext.pomdesc = 'Firebase UI Android'
88
project.ext.buildtools = '23.0.3'
99
project.ext.compileSdk = 23

0 commit comments

Comments
 (0)