DEV Community

Jackson for HMS Core

Posted on

Use Keyring for Sign-in Across Different Apps and Platforms

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.
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 into your Android Studio project.
3.Before building the APK, configure the obfuscation configuration file 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
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"
}

Enter fullscreen mode Exit fullscreen mode

Scenario Simulated by the Sample Code

Image description
Image description

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

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);
Enter fullscreen mode Exit fullscreen mode

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);
    }

Enter fullscreen mode Exit fullscreen mode

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);
            }
        });
    }

Enter fullscreen mode Exit fullscreen mode

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");}
            });
    }

Enter fullscreen mode Exit fullscreen mode

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");
                }
            });
        }

Enter fullscreen mode Exit fullscreen mode

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 for more information.

Top comments (0)