使用 Credential Manager 实现恢复凭据

本页面介绍了如何创建、登录和删除恢复密钥。

版本兼容性

Credential Manager 的 Restore Credentials 适用于搭载 Android 9(API 级别 28)及更高版本、Google Play 服务 (GMS) 核心版本 24220000 或更高版本以及 androidx.credentials 库 1.5.0 或更高版本的设备。

前提条件

设置一个信赖方服务器,类似于通行密钥的服务器。如果您已设置服务器来处理通行密钥身份验证,请使用相同的服务器端实现来处理恢复密钥。

依赖项

将以下依赖项添加到应用模块的 build.gradle 文件中:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.7.0-alpha02")
    implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.7.0-alpha02"
    implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02"
}

“恢复凭证”功能适用于 1.5.0 及更高版本的 androidx.credentials 库。不过,建议您尽可能使用依赖项的最新稳定版本。

概览

  1. 创建恢复密钥:如需创建恢复密钥,请完成以下步骤:
    1. 实例化 Credential Manager:创建 CredentialManager 对象。
    2. 从应用服务器获取凭据创建选项:向客户端应用发送从应用服务器创建恢复密钥所需的详细信息。
    3. 创建恢复密钥:如果用户已登录您的应用,请为其账号创建恢复密钥。
    4. 处理凭据创建响应:将凭据从客户端应用发送到应用服务器进行处理,并处理任何异常。
  2. 使用恢复密钥登录:如需使用恢复密钥登录,请完成以下步骤:
    1. 从应用服务器获取凭据检索选项:向客户端应用发送从应用服务器检索恢复密钥所需的详细信息。
    2. 获取恢复密钥:当用户设置新设备时,向 Credential Manager 请求恢复密钥。这样,用户无需输入其他信息即可登录。
    3. 处理凭据检索响应:将恢复密钥从客户端应用发送到应用服务器,以便让用户登录。
  3. 删除恢复密钥。

创建恢复密钥

您的应用应涵盖用户登录的所有情况,以确保为活跃用户创建恢复密钥。在以下情况下创建恢复密钥:

  • 如果用户已登录,并且尚未创建恢复密钥(例如在主 Activity 的 onCreate 方法中)。
  • 用户登录或完成新账号注册流程时。

为了优化性能并避免在每次登录时创建或检查恢复凭证的开销,请在本地存储空间(例如 has_synced_restore_credential)中设置 boolean 标志或凭证创建时间戳,以跟踪密钥是否已创建。

实例化 Credential Manager

使用应用的 activity 上下文实例化 CredentialManager 对象。

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

从应用服务器获取凭据创建选项

在应用服务器中使用符合 FIDO 标准的库,向客户端应用发送创建恢复凭据所需的信息,例如有关用户、应用的信息以及其他配置属性。如需详细了解服务器端实现,请参阅服务器端指南。

创建恢复密钥

解析服务器发送的公钥创建选项后,通过将这些选项封装在 CreateRestoreCredentialRequest 对象中并使用 CredentialManager 对象调用 createCredential() 方法来创建恢复密钥。

// createRestoreRequest contains the details sent by the server 
val response = credentialManager.createCredential(context, createRestoreRequest)

代码要点

  • CreateRestoreCredentialRequest 对象包含以下字段:

    • requestJson:应用服务器以 Web Authentication API 格式发送的 PublicKeyCredentialCreationOptionsJSON 凭据创建选项。
    • isCloudBackupEnabled:Boolean 字段,用于确定是否应将恢复密钥备份到云端。默认情况下,此标志为 true。此字段具有以下值:

      • true:(推荐)如果用户启用了 Google 备份和端到端加密(例如屏幕锁定),此值会将恢复密钥备份到云端。
      • false:此值会将密钥保存在本地,而不是云端。如果用户选择从云端恢复,则新设备上无法使用该密钥。

处理凭据创建响应

Credential Manager API 会返回 CreateRestoreCredentialResponse 类型的响应。此响应包含 JSON 格式的公钥凭证注册响应。

将公钥从应用发送到依赖方服务器。此公钥与您创建通行密钥时生成的公钥类似。在服务器上处理通行密钥创建的相同代码也可以处理恢复密钥创建。如需详细了解服务器端实现,请参阅通行密钥指南。

在恢复密钥创建过程中,请处理以下异常:

  • CreateRestoreCredentialDomException:如果 requestJson 无效且不符合 PublicKeyCredentialCreationOptionsJSON 的 WebAuthn 格式,则会发生此异常。
  • E2eeUnavailableException:如果 isCloudBackupEnabled 为 true,但用户的设备没有数据备份或端到端加密(例如屏幕锁定),则会发生此异常。
    为确保在所有情况下都能创建恢复凭据,您必须通过调用 createCredential(将 isCloudBackupEnabled 设置为 true)来明确处理 E2eeUnavailableException。如果抛出 E2eeUnavailableException,请捕获并再次调用 createCredential,并将 isCloudBackupEnabled 设置为 false。
  • IllegalArgumentException:如果 createRestoreRequest 为空或不是有效的 JSON,或者没有符合 WebAuthn 规范的有效 user.id,则会发生此异常。

使用恢复密钥登录

在设备设置过程中,使用恢复凭据功能以静默方式登录用户。

从应用服务器获取凭据检索选项

向客户端应用发送从服务器获取恢复密钥所需的选项。 如需了解此步骤的类似通行密钥指南,请参阅使用通行密钥登录。 如需详细了解服务器端实现,请参阅服务器端身份验证指南。

获取恢复密钥

如需在新设备上获取恢复密钥,请对 CredentialManager 对象调用 getCredential() 方法。

建议在以下两种情况下都提取恢复密钥:

  • 应用在设备上首次启动时。在这种情况下,凭据恢复与应用数据恢复无关。
  • 如果启用了应用数据备份和恢复功能,请在应用数据恢复后立即获取恢复密钥。使用 BackupAgent 配置应用的备份,并确保在 onRestoreFinished 回调中完成 getCredential 功能。请勿使用 onRestore 方法,因为系统仅会针对键值对备份调用该方法,而对于任何类型的备份恢复,系统都会可靠地调用 onRestoreFinished。 这样可以避免用户首次打开新设备时可能出现的延迟,并让用户无需等待打开应用即可与应用互动。例如,这样可以让应用在用户首次在新设备上打开应用之前向用户发送通知,这对于即时通讯或通信应用尤为重要。

如果您创建了新的 BackupAgent,并且之前已使用 allowBackup="true" 启用了备份,请在应用的清单中设置布尔值 android:fullBackupOnly="true"。这样可确保应用的备份和恢复行为保持不变。

// Fetch the options required to get the restore key
val authenticationJson = fetchAuthenticationJson()

// Create the GetRestoreCredentialRequest object
val options = GetRestoreCredentialOption(authenticationJson)
val getRequest = GetCredentialRequest(listOf(options))

val response = credentialManager.getCredential(context, getRequest)

// Type-check and extract the restore credential
val credential = response.credential as RestoreCredential

Credential Manager API 会返回 GetCredentialResponse 类型的响应。此响应中包含的凭据明确属于 RestoreCredential 类型,其中包含公钥。

处理登录响应

将公钥从应用发送到依赖方服务器,然后该公钥可用于登录用户。在服务器端,此操作类似于使用通行密钥登录。在服务器上处理通行密钥登录的代码也可以处理恢复密钥登录。如需详细了解通行密钥的服务器端实现,请参阅使用通行密钥登录。

删除恢复密钥

Credential Manager 是无状态的,不知道用户活动,因此不会自动删除已使用的恢复密钥。如需删除恢复密钥,请调用 clearCredentialState 方法。为安全起见,请在用户每次退出登录时删除密钥。这样可确保用户下次在同一设备上打开应用时,系统会将其退出登录并提示其重新登录。

卸载应用会被解读为有意从相应设备中删除对应的恢复密钥,类似于用户退出账号时的意图。

只有在以下情况下,恢复密钥才会被移除:

  • 系统级操作:用户卸载应用或清除其数据。
  • 应用级调用:在应用代码中处理用户退出操作时,通过调用 clearCredentialState() 以编程方式删除密钥。

当用户退出您的应用时,请对 CredentialManager 对象调用 clearCredentialState() 方法。

// Create a ClearCredentialStateRequest object
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)

// When the user logs out, delete the restore key
val response = credentialManager.clearCredentialState(clearRequest)