29mins remaining

Keyring

29mins remaining
Read mode

1. Introduction

Overview

Keyring offers the Credentials Management API for storing user credentials locally on Android phones and tablets and sharing them between different apps and different platform versions of an app.

What You Will Create

In this codelab, you will call the APIs of Keyring in the demo project and experience how to:

  • Use App1 to save the sign-in credential and share the credential to App2.
  • Use App2 to access the sign-in credential shared by App1.

What You Will Learn

In this codelab, you will learn how to:

  • Create an app in AppGallery Connect.
  • Integrate the Keyring SDK into your app.
  • Call APIs of Keyring.

2. What You Will Need

Hardware Requirements

  • A computer (desktop or laptop) running Windows 7 or Windows 10
  • A Huawei phone or tablet running EMUI 5.0 or later, or a non-Huawei phone or tablet (with the USB cable) running Android 7.0 or later, which is used for debugging

Software Requirements

  • For computers
    • JDK 1.7 or later
    • Android Studio 3.X or later
    • minSdkVersion: 19 or later
    • targetSdkVersion: 30 (recommended)
    • compileSdkVersion: 30 (recommended)
    • Gradle version: 5.4.1 or later (recommended)
  • Huawei phone and tablet
    • EMUI 5.0 or later
    • HMS Core (APK) 6.0.0 or later
  • Non-Huawei phone and tablet
    • Android 7.0 or later
    • HMS Core (APK) 6.0.0 or later

Required Knowledge

  • Android app development basics
  • Android multithreading

3. Integration Preparations

To integrate Keyring, you must complete the following preparations:

  1. Create an app in AppGallery Connect.
  2. Create an Android Studio project.
  3. Generate a signing certificate.
  4. Generate a signing certificate fingerprint.
  5. Add the signing certificate fingerprint to the app in AppGallery Connect.
  6. Enable Keyring in AppGallery Connect.
  7. Add the app package name and save the configuration file.
  8. Add the AppGallery Connect plugin and the Maven repository in the project-level build.gradle file.
  9. Configure the signing certificate in Android Studio.
For details, please refer to Preparations for Integrating HUAWEI HMS Core.

4. Enabling Keyring

  1. Sign in to AppGallery Connect, click My projects, find your project, and click your desired app. Then, go to Project settings > Manage APIs.
  2. Toggle on the Keyring switch.

Now, you have successfully enabled Keyring for your app.

5. Integrating the HMS Core SDK

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.

Adding the AppGallery Connect Configuration File of Your App

If you have enabled certain services in AppGallery Connect, add the agconnect-services.json file to your app.
Step 1 - Sign in to AppGallery Connect and click My projects.
Step 2 - Find your app project and click the app that needs to integrate the HMS Core SDK.
Step 3 - Go to Project settings > General information. In the App information area, download the agconnect-services.json file.

Step 4 - Copy the agconnect-services.json file to your app's root directory.

—-End

Configuring the Maven Repository Address for the HMS Core SDK

The procedure for configuring the Maven repository address in Android Studio is different for Gradle plugin earlier than 7.0, Gradle plugin 7.0, and Gradle plugin 7.1 or later. Click a relevant link below to find the configuration procedure for the specific Gradle plugin version.

Gradle plugin earlier than 7.0

Gradle plugin 7.0

Gradle plugin 7.1 or later

Gradle plugin earlier than 7.0

  • Open the build.gradle file in the root directory of your Android Studio project.
  • Add the AppGallery Connect plugin and the Maven repository.
    • Go to buildscript >repositories and configure the Maven repository address for the HMS Core SDK.
    • Go to allprojects > repositories and configure the Maven repository address for the HMS Core SDK.
    • If the agconnect-services.json file has been added to the app, go to buildscript > dependencies and add the AppGallery Connect plugin configuration.
    • buildscript { repositories { google() jcenter() // Configure the Maven repository address for the HMS Core SDK. maven {url 'https://developer.huawei.com/repo/'} } dependencies { ... // Add the AppGallery Connect plugin configuration. Please refer to AppGallery Connect Plugin Dependency to select a proper plugin version. classpath 'com.huawei.agconnect:agcp:1.6.0.300' } } allprojects { repositories { google() jcenter() // Configure the Maven repository address for the HMS Core SDK. maven {url 'https://developer.huawei.com/repo/'} } }

Gradle plugin 7.0

  • Open the build.gradle file in the root directory of your Android Studio project.
  • Add the AppGallery Connect plugin and the Maven repository.
    • Go tobuildscript >repositories and configure the Maven repository address for the HMS Core SDK.
    • If the agconnect-services.json file has been added to the app, go to buildscript > dependencies and add the AppGallery Connect plugin configuration.
    • buildscript { repositories { google() jcenter() // Configure the Maven repository address for the HMS Core SDK. maven {url 'https://developer.huawei.com/repo/'} } dependencies { ... // Add the Android Gradle plugin configuration. You need to replace {version} with the actual Gradle plugin version, for example, 7.0.1. classpath 'com.android.tools.build:gradle:{version}' // Add the AppGallery Connect plugin configuration. Please refer to AppGallery Connect Plugin Dependency to select a proper plugin version. classpath 'com.huawei.agconnect:agcp:1.6.0.300' } }
  • Open the project-level settings.gradlefile and configure the Maven repository address for the HMS Core SDK.
  • dependencyResolutionManagement { ... repositories { google() jcenter() // Configure the Maven repository address for the HMS Core SDK. maven {url 'https://developer.huawei.com/repo/'} } }

Gradle plugin 7.1 or later

  • Open the build.gradle file in the root directory of your Android Studio project.
  • If the agconnect-services.json file has been added to the app, go to buildscript > dependencies and add the AppGallery Connect plugin configuration.
    buildscript { dependencies { ... // Add the Android Gradle plugin configuration. You need to replace {version} with the actual Gradle plugin version, for example, 7.1.1. classpath 'com.android.tools.build:gradle:{version}' // Add the AppGallery Connect plugin configuration. Please refer to AppGallery Connect Plugin Dependency to select a proper plugin version. classpath 'com.huawei.agconnect:agcp:1.6.0.300' } } plugins { ... }
  • Open the project-level settings.gradle file and configure the Maven repository address for the HMS Core SDK.
  • pluginManagement { repositories { gradlePluginPortal() google() mavenCentral() // Configure the Maven repository address for the HMS Core SDK. maven { url 'https://developer.huawei.com/repo/' } } } dependencyResolutionManagement { ... repositories { google() mavenCentral() // Configure the Maven repository address for the HMS Core SDK. maven { url 'https://developer.huawei.com/repo/' } } }

Adding Build Dependencies

Step 1 - Open the app-level build.gradle file of your project.

Step 2 - Add the AppGallery Connect plugin configuration in either of the following methods:

  • Method 1: Add the following configuration under the declaration in the file header:
    apply plugin: 'com.huawei.agconnect'
  • Method 2: Add the plugin configuration in the plugins block.
    plugins { id 'com.android.application' // Add the following configuration: id 'com.huawei.agconnect' }

Step 3 - Add a dependency in the dependencies block.

dependencies { implementation 'com.huawei.hms:keyring-credential:{version}' }

—-End

Defining Multi-language Settings

  • By default, your app supports all languages provided by the HMS Core SDK. If your app uses all these languages, skip the operation procedure in this section.
  • If your app only supports certain languages, you can configure the languages during this step.
    a.Open the app-level build.gradle file in your project.

    b. Go to android > defaultConfig, add resConfigs, and configure the supported languages as follows:
    android { defaultConfig { ... resConfigs "en", "zh-rCN", "Other languages supported by your app" } }

For details about the languages supported by the HMS Core SDK, please refer to Languages Supported by the HMS Core SDK.

Synchronizing the Project

Click Sync Now to synchronize your project.

6. Development Procedure

User Sign-in Scenario

Process

  1. After a user opens your app, it checks Keyring to see whether a credential is available. If so, go to 2. If not, go to 3.
  2. If only one credential is found, you can specify your app to ask the user whether to use the credential to sign in, or skip this step and directly go to 5. If there are multiple credentials, it is recommended that your app show them in a list for the user to choose from.
  3. Your app asks the user to enter the user name and password. After the user taps the sign-in button, go to 4.
  4. Your app makes a record that the user has entered a new credential for saving the credential after the sign-in is successful. Then, go to 5.
  5. Your app server checks whether the user credential is valid. If so, go to 6. If not, go to 3 to ask for the input of the user name and password.
  6. Your app checks whether the user has entered a new credential. If so, go to 7. If not, the sign-in is successful.
  7. Your app calls the credential saving API to save the new credential to Keyring.

Development Procedure

  1. Initialize the CredentialClient object in the onCreate method of the activity.
    Java:
    CredentialClient credentialClient = CredentialManager.getCredentialClient(this);

    Kotlin:

    val credentialClient = CredentialManager.getCredentialClient(this)
  2. Check whether a credential is available.
    Java:
    List<AppIdentity> trustedAppList = new ArrayList<>(); trustedAppList.add(new AndroidAppIdentity("yourAppName", "yourAppPackageName", "yourAppCodeSigningCertHash")); trustedAppList.add(new WebAppIdentity("youWebSiteName", "www.yourdomain.com")); trustedAppList.add(new WebAppIdentity("youWebSiteName", "login.yourdomain.com")); SharedCredentialFilter sharedCredentialFilter = SharedCredentialFilter.acceptTrustedApps(trustedAppList); credentialClient.findCredential(sharedCredentialFilter, new CredentialCallback<List<Credential>>() { @Override public void onSuccess(List<Credential> credentials) { if (credentials.isEmpty()) { Toast.makeText(MainActivity.this, R.string.no_available_credential, Toast.LENGTH_SHORT).show(); } else { for (Credential credential : credentials) { } } } @Override public void onFailure(long errorCode, CharSequence description) { Toast.makeText(MainActivity.this, R.string.query_credential_failed, Toast.LENGTH_SHORT).show(); } });

    Kotlin:

    val credentialClient = CredentialManager.getCredentialClient(this) val trustedAppList: MutableList<AppIdentity> = ArrayList() trustedAppList.add(AndroidAppIdentity("yourAppName", "yourAppPackageName", "yourAppCodeSigningCertHash")) trustedAppList.add(WebAppIdentity("youWebSiteName", "www.yourdomain.com")) trustedAppList.add(WebAppIdentity("youWebSiteName", "login.yourdomain.com")) val sharedCredentialFilter = SharedCredentialFilter.acceptTrustedApps(trustedAppList); credentialClient.findCredential(sharedCredentialFilter, object : CredentialCallback<List<Credential>> { override fun onSuccess(credentials: List<Credential>) { if (credentials.isEmpty()) { Toast.makeText(this@MainActivity, R.string.no_available_credential, Toast.LENGTH_SHORT).show() } else { for (credential in credentials) { } } } override fun onFailure(errorCode: Long, description: CharSequence) { Toast.makeText(this@MainActivity, R.string.query_credential_failed, Toast.LENGTH_SHORT).show() } })
  3. Call Credential.getContent to obtain the credential content and obtain the result from CredentialCallback.
    Java:
    private Credential mCredential; // Obtained credentials. mCredential.getContent(new CredentialCallback<byte[]>() { @Override public void onSuccess(byte[] bytes) { String hint = String.format(getResources().getString(R.string.get_password_ok), new String(bytes)); Toast.makeText(MainActivity.this, hint, Toast.LENGTH_SHORT).show(); mResult.setText(new String(bytes)); } @Override public void onFailure(long l, CharSequence charSequence) { Toast.makeText(MainActivity.this, R.string.get_password_failed, Toast.LENGTH_SHORT).show(); mResult.setText(R.string.get_password_failed); } });

    Kotlin:

    credential!!.getContent(object : CredentialCallback<ByteArray?> { override fun onSuccess(content: ByteArray?) { // Processing if the credentials are obtained successfully. //var password = String(content!!) val hint: String = kotlin.String.format(getResources().getString(R.string.get_password_ok), String(content!!)) Toast.makeText(this@MainActivity, hint, Toast.LENGTH_SHORT).show() mResult.setText(String(content!!)) } override fun onFailure(l: Long, charSequence: CharSequence?) { // Processing if the credentials fail to be obtained. Toast.makeText(this@MainActivity, R.string.get_password_failed, Toast.LENGTH_SHORT).show() mResult.setText(R.string.get_password_failed) } })
  4. After the user enters a new credential, call the credential saving API.
    Java:
    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("user_niceday"); credential.setSharedWith(sharedAppList); credential.setSyncable(true); credentialClient.saveCredential(credential, new CredentialCallback<Void>() { @Override public void onSuccess(Void unused) { Toast.makeText(MainActivity.this, R.string.save_credential_ok, Toast.LENGTH_SHORT).show(); } @Override public void onFailure(long errorCode, CharSequence description) { Toast.makeText(MainActivity.this, R.string.save_credential_failed + " " + errorCode + ":" + description, Toast.LENGTH_SHORT).show(); } });

    Kotlin:

    val app2 = AndroidAppIdentity(sharedToAppName, sharedToAppPackage, sharedToAppCertHash) val sharedAppList: MutableList<AppIdentity> = ArrayList() sharedAppList.add(app2) val credential = Credential(username, CredentialType.PASSWORD, userAuth, password.toByteArray()) credential.setDisplayName("user_niceday") credential.setSharedWith(sharedAppList) credential.syncable = true credentialClient.saveCredential(credential, object : CredentialCallback<Void?> { override fun onSuccess(unused: Void?) { Toast.makeText(this@MainActivity, "save credential OK", Toast.LENGTH_SHORT).show() } override fun onFailure(errorCode: Long, description: CharSequence) { Toast.makeText(this@MainActivity, "save credential failed. $errorCode:$description", Toast.LENGTH_SHORT).show() } })

User Sign-out Scenario

Process

  1. A user taps the sign-out button.
  2. Your app server signs the user out.
  3. To prevent your app from automatically signing the user in after they enter the sign-in screen, your app needs to stop the automatic sign-in function, and enable the function after the user manually signs in successfully.
  4. (Recommended) Your app deletes token-type credentials that become invalid after users sign out from Keyring.

Development Procedure

  1. Initialize the CredentialClient object in the onCreate method of the activity.
    Java:
    CredentialClient credentialClient = CredentialManager.getCredentialClient(this);

    Kotlin:

    val credentialClient = CredentialManager.getCredentialClient(this)
  2. Check whether a credential is available.
    Java:
    List<AppIdentity> trustedAppList = new ArrayList<>(); trustedAppList.add(new AndroidAppIdentity("yourAppName", "yourAppPackageName", "yourAppCodeSigningCertHash")); trustedAppList.add(new WebAppIdentity("youWebSiteName", "www.yourdomain.com")); trustedAppList.add(new WebAppIdentity("youWebSiteName", "login.yourdomain.com")); SharedCredentialFilter sharedCredentialFilter = SharedCredentialFilter.acceptTrustedApps(trustedAppList); credentialClient.findCredential(sharedCredentialFilter, new CredentialCallback<List<Credential>>() { @Override public void onSuccess(List<Credential> credentials) { if (credentials.isEmpty()) { Toast.makeText(MainActivity.this, R.string.no_available_credential, Toast.LENGTH_SHORT).show(); } else { for (Credential credential : credentials) { // Further process the available credentials, including obtaining the credential information and content and deleting the credentials. } } } @Override public void onFailure(long errorCode, CharSequence description) { Toast.makeText(MainActivity.this, R.string.query_credential_failed, Toast.LENGTH_SHORT).show(); } });

    Kotlin:

    val credentialClient = CredentialManager.getCredentialClient(this) val trustedAppList: MutableList<AppIdentity> = ArrayList() trustedAppList.add(AndroidAppIdentity("yourAppName", "yourAppPackageName", "yourAppCodeSigningCertHash")) trustedAppList.add(WebAppIdentity("youWebSiteName", "www.yourdomain.com")) trustedAppList.add(WebAppIdentity("youWebSiteName", "login.yourdomain.com")) val sharedCredentialFilter = SharedCredentialFilter.acceptTrustedApps(trustedAppList); credentialClient.findCredential(sharedCredentialFilter, object : CredentialCallback<List<Credential>> { override fun onSuccess(credentials: List<Credential>) { if (credentials.isEmpty()) { Toast.makeText(this@MainActivity, R.string.no_available_credential, Toast.LENGTH_SHORT).show() } else { for (credential in credentials) { // Further process the available credentials, including obtaining the credential information and content and deleting the credentials. } } } override fun onFailure(errorCode: Long, description: CharSequence) { Toast.makeText(this@MainActivity, R.string.query_credential_failed, Toast.LENGTH_SHORT).show() } })
  3. Call deleteCredential to delete the credential and obtain the result from CredentialCallback.
    Java:
    credentialClient.deleteCredential(credential, new CredentialCallback<Void>() { @Override public void onSuccess(Void unused) { String hint = String.format(getResources().getString(R.string.delete_ok), credential.getUsername()); Toast.makeText(MainActivity.this, hint, Toast.LENGTH_SHORT).show(); } @Override public void onFailure(long errorCode, CharSequence description) { String hint = String.format(getResources().getString(R.string.delete_failed), description); Toast.makeText(MainActivity.this, hint, Toast.LENGTH_SHORT).show(); } });

    Kotlin:

    credentialClient.deleteCredential(credential, object : CredentialCallback<Void?> { override fun onSuccess(unused: Void?) { val hint = String.format(resources.getString(R.string.delete_ok), credential.getUsername()) Toast.makeText(this@MainActivity, hint, Toast.LENGTH_SHORT).show() } override fun onFailure(errorCode: Long, description: CharSequence) { val hint = String.format(resources.getString(R.string.delete_failed), description) Toast.makeText(this@MainActivity, hint, Toast.LENGTH_SHORT).show() } })

7. Test

  1. Use App1 to simulate user sign-in. Enter user name user1 and its password, and click Login. Then, enter user name user2 and its password, and click Login.
  2. Open App2. You can see multiple sign-in credentials shared by App1 to App2.
  3. In App2, select the credential user1 and click Login. App2 can obtain the content (that is the user password) of credential user1 for password-free sign-in.

8. Congratulations

Well done. You have successfully completed this codelab and learned how to:

  • Create an app in AppGallery Connect.
  • Enable Keyring.
  • Integrate the Keyring SDK into your app.
  • Calling APIs of Keyring.

9. Reference

For more information, please click the following link:

Related documents
To download the sample code, please click the button below:

Download

Code copied