Use Keyring for Sign-in Across Different Apps and Platforms
Tags: security, android, app
Published: 2022-01-06 02:23:04

It's common for an app developer to develop multiple apps across different platforms. However, an issue that arises is that when signing in to different apps or the same app across different platforms on the same device, users usually needs to repeatedly enter their account name and password even if the same account is used, which has a negative impact on user experience.

Keyring provides developers with the ability to share user authentication credentials across different apps and app platforms, thus creating a seamless sign-in experience for users. In this article I will explain how to integrate Keyring and implement cross-platform & cross-app sign-in.

#Preparations

Before integrating the Keyring SDK, you'll need to perform the following preparations:
1.[Configure app information in AppGallery Connect.](https://developer.huawei.com/consumer/en/doc/development/Security-Guides/config-agc-0000001181242769)
2.Create an Android Studio project and configure the Maven repository address. If you are using Android Studio, you can integrate the HMS Core SDK via the Maven repository. Before you start developing an app, [integrate the HMS Core SDK](https://developer.huawei.com/consumer/en/doc/development/Security-Guides/hms-core-0000001208048739) into your Android Studio project.
3.Before building the APK, [configure the obfuscation configuration file](https://developer.huawei.com/consumer/en/doc/development/Security-Guides/config-obfuscation-scripts-0000001181234351) to prevent the HMS Core SDK from being obfuscated.

#Integrating the Keyring SDK

After completing the preparations, let's look at how to integrate the Keyring SDK.
1.Open the app-level build.gradle file in the project.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0zrcp0q2czkiyynuset.jpg)
2.Add a build dependency on the Keyring SDK in the dependencies block.
Find the dependencies block in the build.gradle file, add a dependency on the Keyring SDK in the block, and click Sync Now to make Android Studio synchronize the project.

```
dependencies{
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'


implementation "com.huawei.hms:keyring-credential:6.1.1.302"
}

```

#Scenario Simulated by the Sample Code

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ifnr568snks4n3hlugn0.jpg)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vq8m7tq38l35n9nirw4y.jpg)

The sample code provides two sample apps, which are App1 and App2.
App1 simulates the sign-in operation, and calls APIs of Keyring to save the user sign-in credentials. App1 then shares these credentials with App2.
App2 calls APIs of Keyring to query available credentials, and calls the **getContent** API to obtain the user's sign-in password.
To make the sample code work, we need to create two apps in AppGallery Connect, download the **agconnect-services.json** files of the two apps, and place the files in the corresponding Android Studio projects of App1 and App2. Refer to "Preparations" for details.

#Preparing Information

We need to collect the information listed below for use later on. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8le6x1yt3a0bn1xz2z4.png)

We need to set the sharing relationship in the code for App1 and App2. Therefore, we need to query and record the package names and signing certificate fingerprints of App1 and App2. For details about how to obtain the signing certificate fingerprint, please refer to "Preparations."

#Development Practice

1.Configure App1 to save user credentials using the Keyring SDK.

App1 simulates the app sign-in operation, the sample project has only one activity, that is, MainActivity, in which most code lines are used to implement the sign-in screen and basic sign-in logic. This activity has a member object of the CredentialClient type, which is the operation object for accessing Keyring and is initialized in the onCreate method of the activity.

```
  mCredentialClient = CredentialManager.getCredentialClient(this);
```

Then, implement the login method for the onClick method of the Login button.

```
private void login(View view) {
        if (!checkInput()) {
            return;
        }
        String username = mUsername.getText().toString().trim();
        String password = mPassword.getText().toString().trim();

        // connect server to login.


        saveCredential(username, password,
                "app2", "com.huawei.hms.keyring.sample.app2",
                "XX:XX:XX:XX:XX:XX",
                true);
    }

```
At the beginning of the **login** method, the **checkInput** method is called to check the validity of the user name and password in the input text boxes. In most cases, the service logic will connect to the background and use the user name and password for sign-in. The sample code does not include the code for implementing this. Here, we assume that the user name and password are correct, and that sign-in is performed successfully in the background.

The next step is to call the **saveCredential** method to save the credentials. The **saveCredential** method is a simple method encapsulated to highlight the input parameters. Such input parameters include the user name, password, information related to the sharing target app. It also includes the configuration specifying whether user authentication is required during credential reading.
App2's package name and certificate fingerprint recorded earlier will be used here. When calling the **saveCredential** method, we need to pass the package name and certificate fingerprint of App2 to the method as parameters.

```
private void saveCredential(String username, String password,
                                String sharedToAppName, String sharedToAppPackage,
                                String sharedToAppCertHash, boolean userAuth) {
        AndroidAppIdentity app2 = new AndroidAppIdentity(sharedToAppName,
                sharedToAppPackage, sharedToAppCertHash);
        List<AppIdentity> sharedAppList = new ArrayList<>();
        sharedAppList.add(app2);

        Credential credential = new Credential(username,
                CredentialType.PASSWORD, userAuth,
                password.getBytes());
        credential.setDisplayName("nickname_" + username);
        credential.setSharedWith(sharedAppList);
        credential.setSyncable(true);

        mCredentialClient.saveCredential(credential, new CredentialCallback<Void>() {
            @Override
            public void onSuccess(Void unused) {showMessage("save credential to Keyring success");}

            @Override
            public void onFailure(long errorCode, CharSequence description) {
                showMessage("save to Keyring failed" + " ====" + errorCode + ":" + description);
            }
        });
    }

```
In the **saveCredential** method, we use the package name and certificate fingerprint of **App2** to create an app object app2. Then, we place this object in **sharedAppList** of **AppIdentity** and use the list as a parameter of the credential object. After that, we create a password-type credential object, and call a series of **set** methods for this object to set the sharing app list and other configurations of the credentials. Finally, we call the **saveCredential** method of the Keyring operation object **mCredentialClient** to save the credentials. We also need to set the callback methods called when credentials are successfully saved or failed to be saved.

We have now saved the credentials in Keyring.

2.Configure App2 to use the Keyring SDK to query and read credentials.
App2 simulates the sign-in operation of another app made by the same developer. App2 can use the Keyring SDK to query credentials saved by itself earlier and credentials shared with it by the specified app, and read the credential content for seamless sign-in. App2 also has only one activity, that is, **MainActivity**, which simulates the sign-in screen. App2, like App1, also defines a member object **mCredentialClient** of the **CredentialClient** 
type and uses the object as the operation object for accessing Keyring. The operation object is also initialized in the **onCreate** method of the activity. The queryCredential method is called at the end of the **onCreate** method to query available credentials.
Now, let's look at how to implement the **queryCredential** method.

```
private void queryCredential() {
	        final AndroidAppIdentity app1 = new AndroidAppIdentity("app1",
	                "com.huawei.hms.keyring.sample.app1",
	                "XX:XX:XX:XX:XX:XX"
	        );
	        List<AppIdentity> trustedOwnerList = new ArrayList<>();
	        trustedOwnerList.add(app1);
	

	        mCredentialClient.findCredential(trustedOwnerList, new CredentialCallback<List<Credential>>() {
	            @Override
	            public void onSuccess(List<Credential> credentials) {
	                if (credentials.isEmpty()) {
	                    noAvailableCredential();
	                } else {
	                    MainActivity.this.setCredentialList(credentials);
	                }
	            }
	

	            @Override
	            public void onFailure(long errorCode, CharSequence description) {
	                noAvailableCredential();
	            }
	

	            private void noAvailableCredential()
{showMessage("no shared credential");}
	        });
	}

```
When preparing for SDK integration earlier, we recorded the package name and certificate fingerprint of App1. Now we use the information in the **queryCredential** method to create an **AndroidAppIdentity** object representing App1. Then, place the object in **trustedOwnerList** of **AppIdentity**.
Next, we call the **findCredential** method of the Keyring operation object **mCredentialClient** and pass **trustedOwnerList** and the result callback to the method. The **findCredential** method will query and list credentials that are available to this app. Such credentials include those stored by this app and those shared with this app by other apps specified in trustedOwnerList. In the sample code of App2, the **findCredential** method will query credentials saved by App2 and credentials shared with App2 by App1.
Available credentials exist if the **onSuccess** method is called and the **credentials** parameter is not empty. Then, the **setCredentialList** method will be called to display the credential list in the **RecyclerView** component on the app screen.
The **choose** method in the **CredentialHolder** class will be called if one of the credentials displayed on the screen is chosen. In the **choose** method, the selected credential object will be assigned to the member object **mChooseCredential**. After the user taps the **Login** button on the app screen, the **login** method will be called.

```
private void login(View v) {
	        if (mChooseCredential == null) {
	            showMessage("please_choose_account");
	            return;
	        }
	        mChooseCredential.getContent(new CredentialCallback<byte[]>() {
	            @Override
	            public void onSuccess(byte[] bytes) {
	                // String password = new String(bytes);
	                showMessage("Login success");
	            }
	

	            @Override
	            public void onFailure(long l, CharSequence charSequence) {
	                showMessage("Get password failed");
	            }
	        });
	    }

```

In the **login** method, if **mChooseCredential** is not empty, the **getContent** method of the chosen credential object will be called to obtain the credential content. When calling the **getContent** method, we also need to pass the result callback to it.
The **bytes** parameter in the **onSuccess** method indicates the credential content, that is, the user sign-in password saved by App1. In a real app, we can directly use the password for sign-in, creating a seamless sign-in experience.
That's all for this tutorial. You can visit the [Keyring official website](https://developer.huawei.com/consumer/en/doc/development/Security-Guides/introduction-0000001133141068) for more information.