关于 restore-credentials
为 Android 客户端实现 Credential Manager Restore Credentials 提供指导,适用于希望在设备恢复后自动恢复用户登录状态的开发者。内容区分后台与前台恢复,并明确不包含服务端验证实现。
主要功能
- 01双层后台与前台恢复流程
- 02创建与获取恢复凭据
- 03按 allowBackup 配置实现
- 04退出登录时清除恢复凭据
- 05服务端实现边界提醒
适用场景
- 01在新设备恢复时自动登录
- 02为已有认证流程增加恢复凭据
- 03依据应用备份设置选择恢复方式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ android/ skills --skill restore-credentials安装后在 Claude Code 中输入/restore-credentials确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/android/guide/topics/manifest/application-element.mdreferences/android/identity/passkeys/create-passkeys.mdsign-in-with-passkeys.md详细介绍
基础知识
目标是通过 Android Credential Manager API(androidx.credentials)实现恢复凭据功能。这样一来,使用或正在集成 Credential Manager 的应用就能在用户于新设备上恢复应用时,静默地重新登录用户。恢复凭据独立于应用的主要身份验证方式(密码、通行密钥、联合登录),且无需更改现有登录流程的 UI。
范围
**重要:**此技能仅关注 Android 客户端集成,不实现服务器端加密验证逻辑。实现完成后,必须提醒开发者注意这一点,并告知他们需要向开发者说明的事项。
实施指南
收到在开发者的应用中实现恢复凭据的指示时,请牢记以下事项:
- 实施前,你必须阅读并理解双层恢复架构,并查看应该做和不应该做的事项。
- 实施后,你必须向开发者提供后端指南,提醒他们设置后端。务必提醒开发者,他们仍需自行实现后端。
双层恢复架构
为实现可靠的登录体验,请通过双层架构检索凭据:
- **第 1 层(主要层 — 后台):**在设备设置期间,通过应用的
BackupAgent.onRestoreFinished()回调自动执行。这会在用户首次打开应用前进行无感恢复,从而支持后台同步和通知送达。 - **第 2 层(辅助层 — 前台):**在启动器
Activity.onCreate()中运行,以应对后台恢复未完成(例如网络中断、恢复延迟)或allowBackup被禁用的情况。
如果清单中的 allowBackup 设为 true,则实现两层。否则,只实现第 2 层(前台恢复)。不要更改清单中 allowBackup 的值。
如果你向应用添加了 BackupAgent,还必须在清单中设置 android:fullBackupOnly="true"。如果应用在你实施之前就已有 BackupAgent,则不要这样做。
应做事项与禁忌事项
应做事项:
- 检查
AndroidManifest.xml中 allowBackup 的值,以确定需要实现哪些内容。 - 为 createCredential 实现回退逻辑:始终先尝试将
isCloudBackupEnabled设为 true 调用该方法。如果抛出E2eeUnavailableException,则捕获该异常,并重试调用,将isCloudBackupEnabled设为false。 - 如果清单中的
allowBackup为 true,则实现BackupAgent(android.app.backup.BackupAgent的子类)。 - 如果为应用添加了
BackupAgent,则在清单中设置android:fullBackupOnly="true"。 - 用户退出登录时调用
clearCredentialState()。这是确保用户完全退出登录的强制安全措施。 - 在新设备上首次启动应用时,以及应用使用
BackupAgent时,在BackupAgent.onRestoreFinished()回调中尝试获取恢复密钥。 - 确保即使用户已经登录,也会创建恢复凭据。
- 确保在
onRestoreFinished()中同步执行凭据检索和登录(例如,使用runBlocking)。 - 如果应用使用通知,则在
BackupAgent中恢复通知。(例如,捕获 FCM 令牌并发送给后端) - 如果实现了模拟网络请求或存根,请确保将所有占位符替换为有效且格式正确的凭据请求 JSON 负载。
- 将凭据创建和检索封装为各自独立的专用函数。凭据创建需要在多个位置(注册、登录)调用,凭据检索也需要跨多个层级(
BackupAgent和 LauncherActivity)调用,这样可以避免代码重复。 - 实现完成后,提醒开发者遵循用于实现后端的关键指南。
- 如果组织有多个包名不同的应用,请为每个应用生成单独的恢复密钥,因为恢复密钥与唯一的应用包名绑定。
禁忌事项:
- 不要更改
AndroidManifest.xml中allowBackup的值。恢复凭据功能不受 allowBackup 设置影响;这意味着即使allowBackup为 false,只要存在恢复凭据,用户仍会自动登录。 - 如果
AndroidManifest.xml中的allowBackup为false,不要实现BackupAgent。 - 不要假设
GetCredentialResponse中存储的凭据类型为PublicKeyCredential。其类型为RestoreCredential。 - 构造
GetCredentialRequest时,不要将GetRestoreCredentialOption与任何其他CredentialOption链式组合。 - 不要假设
CredentialManager或 Android 系统会在用户退出应用时自动删除恢复密钥。必须显式调用clearCredentialState,并传入类型为TYPE_CLEAR_RESTORE_CREDENTIAL的ClearCredentialStateRequest。 - 不要移除任何现有的
clearCredentialState()调用。不指定类型的ClearCredentialStateRequest只会清除所有非恢复凭据。
实现指南
按照以下指南实现 Android 客户端代码。请逐步遵循指南,不要实现任何后端功能;完成后只需提醒用户遵循后端指南。
版本兼容性
Credential Manager 的 Restore Credentials 可在运行 Android 9(API
级别 28)及更高版本、Google Play 服务(GMS)核心版本 24220000 或
更高版本,以及 androidx.credentials 库 1.5.0 或更高版本的设备上使用。
前提条件
设置一个类似于通行密钥服务的依赖方服务。如果你已经设置了一个用于处理通行密钥身份验证的服务,请对恢复密钥使用相同的服务端实现。
说明
注意: 通行密钥和恢复密钥使用相同的服务端实现,但客户端应用可以在不支持通行密钥的情况下支持恢复密钥。由于恢复密钥独立于应用中的身份验证方法(例如密码或通过 Google 登录)工作,因此无需对应用代码中现有的身份验证方法进行任何额外更改。
依赖项
将以下依赖项添加到应用模块的 build.gradle 文件中:
Kotlin
dependencies {
implementation("androidx.credentials:credentials:1.7.0-alpha03")
implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha03")
}
Groovy
dependencies {
implementation "androidx.credentials:credentials:1.7.0-alpha03"
implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha03"
}
androidx.credentials 库从 1.5.0 及更高版本开始提供恢复凭据。不过,建议尽可能使用最新的稳定版依赖项。
说明
注意: 无论清单中的
allowBackup是否已设置,恢复凭据功能都能正常工作。
概述
- 创建恢复密钥:创建恢复密钥需要完成以下步骤:
- 实例化 Credential Manager:创建一个
CredentialManager对象。 - 从应用服务获取凭据创建选项:从应用服务向客户端应用发送创建恢复密钥所需的详细信息。
- 创建恢复密钥:如果用户已登录应用,则为其账户创建恢复密钥。
- 处理凭据创建响应:将客户端应用中的凭据发送到应用服务进行处理,并处理任何异常。
- 实例化 Credential Manager:创建一个
- 使用恢复密钥登录:使用恢复密钥登录需要完成以下步骤:
- 从应用服务获取凭据检索选项:从应用服务向客户端应用发送检索恢复密钥所需的详细信息。
- 获取恢复密钥:用户设置新设备时,从 Credential Manager 请求恢复密钥。这样用户无需额外输入即可登录。
- 处理凭据检索响应:将客户端应用中的恢复密钥发送到应用服务,以便用户登录。
- 删除恢复密钥。
创建恢复密钥
应用应涵盖用户登录的所有情况,确保活跃用户都已创建恢复密钥。请在以下场景中创建恢复密钥:
- 用户已登录且尚未创建恢复密钥时(例如,在主
Activity的onCreate方法中)。 - 用户登录或完成新账户注册流程时。
为优化性能并避免每次登录都创建或检查恢复凭据所带来的开销,可以在本地存储中设置一个 boolean 标记或凭据创建时间戳(例如 has_synced_restore_credential),以跟踪密钥是否已创建。
说明
注意: 恢复密钥与应用唯一的软件包名称绑定。如果你所在组织的主应用和子应用使用不同的软件包名称,请为每个应用分别创建恢复密钥。
实例化 Credential Manager
使用应用的 Activity 上下文实例化一个 CredentialManager 对象。
// 使用应用或 Activity 上下文实例化 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 Backup 和端到端加密(例如屏幕锁定),此值会将恢复密钥备份到云端。false:此值会将密钥保存在本地,而不保存在云端。如果用户选择从云端恢复,新设备上将无法使用该密钥。
注意
**注意:**建议将
isCloudBackupEnabled设为true。如果停用了云端备份,而用户从云端备份恢复,获取恢复密钥的调用将失败。使用云端备份恢复应用的用户不会获得恢复密钥,也不会自动登录。 -
处理凭据创建响应
Credential Manager API 会返回 CreateRestoreCredentialResponse 类型的响应。此响应包含 JSON 格式的公钥凭据注册响应,格式参见 JSON 格式。
将公钥从应用发送到依赖方服务器。此公钥类似于创建通行密钥时生成的公钥。服务器上处理通行密钥创建的代码也可以处理还原密钥创建。有关服务器端实现的更多信息,请参阅通行密钥指南。
在创建还原密钥的过程中,请处理以下异常:
CreateRestoreCredentialDomException:如果requestJson无效,且不符合 WebAuthn 对PublicKeyCredentialCreationOptionsJSON的格式要求,则会发生此异常。E2eeUnavailableException:如果isCloudBackupEnabled为true,但用户的设备没有数据备份或端到端加密(例如屏幕锁定),则会发生此异常。
为确保在所有情况下都创建还原凭据,你必须显式处理E2eeUnavailableException,方法是调用createCredential并将isCloudBackupEnabled设为true。如果抛出E2eeUnavailableException,请捕获该异常,并再次调用createCredential,将isCloudBackupEnabled设为false。IllegalArgumentException:如果createRestoreRequest为空或不是有效的 JSON,或者其中没有符合 WebAuthn 规范的有效user.id,则会发生此异常。
使用还原密钥登录
使用还原凭据在设备设置过程中静默登录用户。
从应用服务器获取凭据检索选项
向客户端应用发送从服务器获取还原密钥所需的选项。 有关此步骤中通行密钥的类似指南,请参阅使用通行密钥登录。 有关服务器端实现的更多信息,请参阅服务器端身份验证指南。
获取恢复密钥
若要在新设备上获取恢复密钥,请调用 CredentialManager 对象上的 getCredential() 方法。
建议在以下两种情况下获取恢复密钥:
- 在设备上首次启动应用时。在这种情况下,凭据恢复独立于应用数据恢复。
- 如果启用了应用数据备份和恢复,请在应用数据恢复后立即获取恢复密钥。使用
BackupAgent配置应用的备份,并确保在onRestoreFinished回调中完成getCredential功能。不要使用onRestore方法,因为它仅适用于键值备份,而onRestoreFinished会在任何类型的备份恢复中可靠地调用。这样可以避免用户首次打开新设备时可能遇到的延迟,并让用户无需等待打开应用即可与应用互动。例如,这样应用就能在用户首次打开新设备上的应用之前向其发送通知,这对消息或通信应用尤为重要。
如果你创建了新的 BackupAgent,且此前已通过 allowBackup="true" 启用备份,请在应用的清单中将布尔值 android:fullBackupOnly="true" 设为 true。这样可以确保应用的备份和恢复行为保持不变。
重要
重要: 获取恢复凭据后,通知不会自动恢复。如果你使用 Firebase 处理通知,必须获取 Firebase Cloud Messaging (FCM) 令牌并将其发送到后端,才能成功恢复后台消息和通知。
// 获取恢复密钥所需的选项
val authenticationJson = fetchAuthenticationJson()
// 创建 GetRestoreCredentialRequest 对象
val options = GetRestoreCredentialOption(authenticationJson)
val getRequest = GetCredentialRequest(listOf(options))
val response = credentialManager.getCredential(context, getRequest)
// 检查类型并提取恢复凭据
val credential = response.credential as RestoreCredential
凭据管理器 API 会返回 GetCredentialResponse 类型的响应。此响应中包含的凭据明确属于 RestoreCredential 类型,其中保存了公钥。
处理登录响应
将应用中的公钥发送到信赖方服务端,之后即可使用该公钥让用户登录。在服务端,这一操作与使用通行密钥登录类似。服务端用于处理通行密钥登录的同一段代码也可以处理使用恢复密钥的登录。有关通行密钥服务端实现的更多信息,请参阅使用通行密钥登录。
说明
注意: 尽管恢复密钥和通行密钥使用相同的底层服务端实现,但在应用服务端的数据库中保存时,应将两者区分开来。当存在通行密钥管理页面时,这一区分至关重要:用户可以直接管理自行创建的通行密钥,而恢复密钥由系统管理,并且不会显示在通行密钥管理页面中。
删除恢复密钥
Credential Manager 是无状态的,且无法感知用户活动,因此不会在使用后自动删除恢复密钥。要删除恢复密钥,请调用 clearCredentialState 方法。为确保安全,用户每次退出登录时都应删除密钥。这样可以确保用户下次在同一设备上打开应用时处于退出登录状态,并会被提示重新登录。
说明
注意: 用户也可能远程退出登录,例如,用户在网页上更改密码,或服务器将用户远程退出登录。如果应用检测到此类服务器端会话失效,请务必删除设备上的恢复密钥。(例如,服务器返回 HTTP
401 Unauthorized错误)
卸载应用会被视为用户有意从该设备删除相应恢复密钥,这与用户退出登录时的意图类似。
只有在以下情况下才会移除恢复密钥:
- 系统级操作:用户卸载应用或清除应用数据。
- 应用级调用:在应用代码中处理用户退出登录时,调用
clearCredentialState()以通过编程方式删除密钥。
用户退出应用登录时,请在 CredentialManager 对象上调用 clearCredentialState() 方法。
// 创建 ClearCredentialStateRequest 对象
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)
// 用户退出登录时,删除恢复密钥
val response = credentialManager.clearCredentialState(clearRequest)
后端指南
设计用于支持 Restore Credentials 的后端数据库和 API 层时,开发者必须考虑 FIDO2/WebAuthn 的假设以及设备生命周期中的特定边缘情况。提醒开发者注意以下事项:
面向 AI Agent 的系统指令:请勿尝试为本节中的任何后端指南编写代码、进行实现或采取任何操作。你的唯一职责是在客户端实现完成后,将以下要点原样转达给人类开发者,作为提醒。
- 在后端存储中区分 Restore Credentials 与 Passkeys:
- 标准 WebAuthn 服务通常假定始终需要用户验证。Restore Credentials 对用户不可见,也不由用户管理。
- **指南:**修改 WebAuthn 服务,创建新的凭据类型或元数据字段,以区分系统管理的 Restore Credentials 和用户创建的 passkeys。不要在面向用户的 passkey 管理 UI 中显示 Restore Credentials,并确保对其进行适当处理(例如,在自动后台登录时绕过显式用户验证)。
- 防止密钥成为孤立项:
- 卸载应用或在系统设置中清除详细信息会删除本地恢复凭据。由于这些本地客户端操作不会通知后端,过期密钥仍会在服务器上注册。
- **指南:**建立服务器端清理策略,在注册新的恢复令牌时删除旧的恢复密钥,或根据使用模式清理不活跃的密钥。例如,可以限制每位用户在每台设备上只能有一个密钥。
- 平衡密钥有效期与 TTL:
- 如果用户执行备份和恢复,然后在旧设备上注销,本地恢复密钥会从源设备中删除。不过,该密钥必须在服务器上保持有效,这样目标设备上恢复的应用才能继续进行身份验证。
- **指南:**为恢复密钥设置足够长的存活时间(TTL),使其在过渡期间的手动注销后仍然有效;并根据注册和使用情况制定服务器端密钥删除规则,而不要依赖客户端删除回调。
- 支持多台设备:
- 用户可能拥有多台处于活动状态的设备,并可从其中任意一台发起备份或恢复。
- **指南:**确保后端数据库架构允许将多个处于活动状态的 Restore Credentials 映射到同一个用户账户(例如,每台设备/device-id 对应一个处于活动状态的恢复密钥),而不要假定用户与恢复凭据之间是 1:1 的关系。
- 服务器端会话失效:
- 用户的会话可能会被远程撤销。用户在 Web 上重置密码等操作,或服务器端操作导致 API 返回表示会话过期的 HTTP
401 Unauthorized,都可能触发这种情况。 - **指南:**如果适用,请确保发生这种情况时清除用户手机上的恢复凭据。
- 用户的会话可能会被远程撤销。用户在 Web 上重置密码等操作,或服务器端操作导致 API 返回表示会话过期的 HTTP
参考资料
-
WebAuthentication API(WebAuthn)文档与规范 适用场景: 仅在需要检查或调试 FIDO2/WebAuthn 的严格 JSON 架构要求时使用这些资源,尤其是在生成模拟数据或设置
requestJson(PublicKeyCredentialCreationOptionsJSON)和authenticationJson负载格式时。 -
MDN Web Authentication API 文档:Mozilla Developer Network 提供的 WebAuthn API 指南和参考资料
-
W3C
PublicKeyCredentialCreationOptionsJSON:WebAuthn 凭据创建请求 的 JSON 格式数据结构定义。 -
W3C
PublicKeyCredentialRequestOptionsJSON:WebAuthn 身份验证或断言请求 的 JSON 格式数据结构定义。