Changes

Linking and Unlinking Firebase Authentication Providers

11,823 bytes added, 19:31, 14 August 2017
Created page with "Firebase account linking allows users to sign into the same account using different authentication providers. By linking the user’s Facebook and Twitter credentials, for exa..."
Firebase account linking allows users to sign into the same account using different authentication providers. By linking the user’s Facebook and Twitter credentials, for example, the user can sign into the same account using either sign-in provider. As discussed in the previous chapter, linking is also key to preserving a user’s anonymous account.

This chapter will introduce the concepts of Firebase authentication account linking before extending the project created in the previous chapter to add the ability to link an email and password account to an anonymous account.

== Firebase Authentication Account Linking ==

The average user today probably has an email address and a multitude of online accounts including Google, Facebook and Twitter. All of the examples so far in this book have restricted the user to using one of these identities to sign into an account. Account linking allows the user to link an account from one provider to an account from another provider. Having linked these accounts, the user is then able to sign into the app using either of those accounts.

Consider, for the purposes of an example, a user registered to access an app using an account created via the Firebase Facebook authentication provider. If the user creates a new account using the Google authentication provider and links that new account to the original Facebook-based account, either account can then be used to sign into the app.

As outlined in the previous chapter, establishing a link involving an anonymous account is a special case. In order for a user to gain permanent access to any data stored while signed in with an anonymous account, the user must link that account with an account associated with an authentication provider (in other words an email, Google, Facebook, Twitter or GitHub account).

== Limitations of Firebase Account Linking ==

When using account linking it is important to be aware that some limitations exist. First, only two accounts can participate in a link. If an attempt is made to link to an account which is already linked, the new link will replace the original link.

It is also not possible to link two accounts associated with the same authentication provider. While a Facebook account may be linked with a Google account, for example, it is not possible to link two Google provider based accounts. An attempt to link accounts from the same provider will result in an exception containing a message which reads as follows:

<pre>
User has already been linked to the given provider
</pre>

Account linking can only be performed at the point at which a new account is created. It is not possible, in other words, to link two pre-existing accounts. A workaround to this limitation is to delete one of the two accounts and then establish the link while re-creating the account.

== Implementing Email/Password Authentication ==

At the end of the preceding chapter, the AnonAuth project allowed the user to sign in to an app as an anonymous user and then sign out. The user interface was designed such that the user is provided with the option of creating an account using the email authentication provider and linking that account to the anonymous account and includes fields into which the user may enter an email address and password.

The project already contains a placeholder for a method named createAccount() which is called when the user requests the creation of an account. Open the project, edit the AnonAuthActivity.java file and modify the createAccount() method as follows:

<pre>
public void createAccount() {
String email = emailText.getText().toString();
String password = passwordText.getText().toString();

if (email.length() == 0) {
emailText.setError("Enter an email address");
return;
}

if (password.length() < 6) {
passwordText.setError("Password must be at least 6 characters");
return;
}

AuthCredential credential =
EmailAuthProvider.getCredential(email, password);

fbAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this,
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (!task.isSuccessful()) {
Toast.makeText(AnonAuthActivity.this,
"Authentication failed.",
Toast.LENGTH_SHORT).show();
}
}
});
}
</pre>

The code begins with the familiar step of extracting the user’s email and password entries from the EditText objects and performing some rudimentary validation to ensure the fields contain content. The email and password are then used to obtain authentication credentials for the new account from Firebase.

Once the credentials have been obtained, the FirebaseUser object for the current user (in this case the anonymous account) is obtained and the linkWithCredential() method of that object called, passing through the credentials for the new email and password account. If an error is encountered, the user is notified via a Toast message. A successful linking operation, on the other hand, will trigger a call to the authentication state listener which will update the user interface with the user’s email address and switch the soft button back to anonymous sign-in mode.

== Testing the Project ==

Open the Firebase console within a browser window, navigate to the Authentication page and select the user account list. To avoid confusion during testing, delete any existing anonymous accounts from the list.

Build and run the app and click the sign out button if the app is currently signed into an account.

Create an anonymous account and refer to the Firebase console to verify that the anonymous account has been created:


[[File:]]

Figure 18-1


Make a note of the User UID assigned to the anonymous account, then return to the app and enter an email address and password into the appropriate EditText fields before clicking on the Create an Account button.

After a brief delay, the user interface should update to display the user’s email address.

Reload the list of users within the Authentication panel of the Firebase console at which point the anonymous account should have been replaced by the new email and password based account using the same User UID as originally assigned to the anonymous account. There can sometimes be a delay before the change appears in the Firebase console. If the link is not reflected immediately, try switching to another area of the Firebase console then returning to the Authentication panel:


[[File:]]

Figure 18-2


== Linking Other Account Types ==

Obviously, account linking is not restricted to anonymous accounts. Links can be established between accounts regardless of the authentication provider used to create the account (assuming the two accounts are from different authentication providers). All that is required is the AuthCredential object for the new account and the FirebaseUser instance for the account with which the link is to be established. The following code, for example, links a Google account to the current user account:

<pre>
AuthCredential credential =
GoogleAuthProvider.getCredential(acct.getIdToken(), null);

fbAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {

if (!task.isSuccessful()) {
// Report failure to user
}
}
});
</pre>

== Unlinking an Authentication Provider ==

An existing link can be removed from an account by calling the unlink() method of the FirebaseUser object for the current user, passing through the provider ID of the account to be unlinked.

A list of provider IDs associated with an account can be obtained by calling the getProviderData() method of the current user’s FirebaseUser object as follows:

<pre>
FirebaseUser user = fbAuth.getCurrentUser();

List<? extends UserInfo> providerData = user.getProviderData();
</pre>

The above method call returns a list of UserInfo objects, each containing the provider for the corresponding account. The following code iterates through the UserInfo objects in the above providerData list and outputs the provider ID for each to the console:

<pre>
for (UserInfo userInfo : providerData ) {

String providerId = userInfo.getProviderId();
Log.d(TAG, "providerId = " + providerId);
}
</pre>

The provider ID takes the form of a string which represents the authentication provider used to create the account (“firebase”, “twitter.com”, “facebook.com”, “google.com”, “password” etc). When identifying the authentication providers assigned to a user, there will always be a “firebase” entry regardless of which other providers have also been configured. A user that only has an email/password based account will, for example, return two UserInfo objects within the provider data containing provider ids of “firebase” and “password” respectively. It is not possible to unlink the “firebase” provider.

Unlinking code should be implemented such that it identifies a specific provider and then unlinks it from the current user account. The following code, for example, unlinks the user’s Google provider-based account:

<pre>
for (UserInfo userInfo : providerData ) {

String providerId = userInfo.getProviderId();
Log.d(TAG, "providerId = " + userInfo.getProviderId());

if (providerId.equals("google.com")) {
user.unlink(providerId)
.addOnCompleteListener(this,
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (!task.isSuccessful()) {
// Handle error
}
}
});
}
}
</pre>

Once the Google authentication provider account has been unlinked, the user will no longer be able to sign into the app using those credentials leaving only the original account available for signing in.

Unlinking the only authentication provider registered for a user account will turn the account into an anonymous account. As such, the user will only have access to the account and associated Firebase stored data for the remainder of the current log in session and once the user logs out, that access will be lost. The user should either be prompted to link a different provider based account to the anonymous account before logging out or, more preferably, prevented from unlinking the last remaining authentication provider.

== Summary ==

Firebase account linking serves two purposes. The first is to allow a temporary anonymous account to be converted into a permanent account. Account linking also allows a user to sign in using the credentials of more than one authentication provider. By linking Google and Facebook accounts together, for example, the user is able to use either to log into the app in future.

Only two accounts can be linked together and those accounts cannot be from the same authentication provider. It is also not possible to link two existing accounts. A link may only be established during the creation of a new account. Linking between two pre-existing accounts is not possible unless one of the accounts is deleted and recreated.

In addition to linking accounts, accounts may also be unlinked by identifying the ID of the authentication provider to be removed and passing it through to the unlink() method of the current user’s Firebase User object.