开发者关系工程师
随着行业逐渐淘汰密码和实体钱包,要提供现代化的用户体验历程,既需要无缝身份验证,也需要可信的身份验证。虽然通行密钥可实现安全的一键登录,但切换设备仍然是用户互动度下降幅度最大的环节之一。了解如何使用 Restore Credentials API 减少迁移阻力,以满足新的 Google Play 零点按登录标准。
本演讲将探讨如何通过与 Android Credential Manager 的单一集成,统一通行密钥、静默设备恢复和数字凭证,让您能够直接从设备验证身份属性,并打造轻松、安全的初始配置流程。
转写文稿
大家好
感谢大家今天的参与。
今天,我们很高兴能与大家探讨身份验证和数字身份的未来。
大家好,我是 Tom Colvin。
我是 Apptaura 的 CTO,也是 Android 领域的 GDE。今天,Google 的高级 Android 开发者关系工程师 Chiping Yeh 也将与我一起分享。
古老的密码时代已经终结。
或者,至少应该如此。
今天,我将向大家介绍无密码世界。
密码是安全基础架构中众所周知的漏洞。
即使密码存储在安全的地方,也容易受到钓鱼式攻击、服务器数据泄露和撞库等自动化攻击的影响。
作为行业,我们试图通过强制提高密码复杂度来解决这个问题:特殊字符、数字、强制轮换。
事实证明,这种做法已经失败,部分原因在于,您越是增加难度,反而越会促使人们采取不安全的行为。
因为定期更改的复杂密码很可能会被写在便签上。
甚至基于短信的双重身份验证也容易受到拦截(例如 SIM 卡交换)。
不过,现在这个问题已基本解决。
通行密钥基于 FIDO 标准,并使用公钥-私钥加密技术。
通行密钥具有很强的防钓鱼式攻击能力,不会在服务器遭到入侵时泄露,并且通过屏幕锁定(在大多数情况下,基于我们的生物识别、指纹等)直接与用户的手机安全绑定。此外,通行密钥还可在不同平台上提供统一的体验,无需记住复杂的密码,从而有效提升用户体验。
我们今天的宣言很简单。
是时候迈向无密码世界了。
而实现这一目标的工具已经掌握在我们手中。
Jetpack Credential Manager 是在 Android 上实现这一飞跃的核心。
该 API 一直支持到 Android 4.4,并且自 Android 9 起支持通行密钥,它提供了一个单一、统一的 API,不仅支持通行密钥,还支持传统身份验证,例如联合登录(如“使用 Google 账号登录”),并且在需要时可以优雅地回退到密码。
您调用 Credential Manager API,它会处理界面,以选择用户希望看到的身份验证方法。
然后,它会执行繁重的工作,返回安全获取的正确凭据。
它支持登录、注册和备份凭据。
您还可以使用它来更新和删除凭据。
此外,用户只需点击几下,即可在凭证提供程序之间转移密码和通行密钥,而无需下载文件。
该法案旨在让用户能够完全控制自己的数据,并有权随时更换服务提供商。
Credential Manager 支持多种设备类型,包括手表、电视、XR、车载设备、手机、可折叠设备、平板电脑。
最重要的是,它有专门的通行密钥流程。
以下是用户在注册期间看到的界面。
我的应用会告知 Credential Manager,通行密钥身份验证可用,并向其提供从后端收集的详细信息。
您点击“注册”,Credential Manager 会显示一个底部动作条,其中包含屏幕锁定提示。
通常使用指纹或人脸扫描等生物识别信息解锁。
然后,Credential Manager 会处理复杂的加密工作,例如实际创建通行密钥。
并将通行密钥保存到我的 Credential Manager 中,在本例中是 Google 密码管理工具,但也可以是其他工具。
代码如下所示。
val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
requestJson = registerRequestJson
)
val createCredentialResponse = credentialManager.createCredential (
context = localActivity,
request = createPublicKeyCredentialRequest
)首先,我们使用后端提供的详细信息(即 registerRequestJson)创建此 createPublicKeyCredentialRequest 对象。
然后,我们调用魔术方法 credentialManager.createCredential。
我们传入该请求和 Local Context,以便其显示界面。
顺便提一下,我在这里无需编写任何界面代码。
Credential Manager 会为我们处理好这一切。
以上就是登录流程。
对用户来说,这看起来非常像注册流程。
只需一个解锁提示,即可完成解锁。
对于开发者来说,只有细微差别。
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
requestJson = authenticationRequestJson
)
val signInRequest =
GetCredentialRequest(listOf(getPublicKeyCredentialOption))
val result = credentialManager.getCredential (
context = localActivity,
request = signInRequest
)首先,我们从后端获取登录详情,以构建 GetPublicKeyCredential 实例。
我们告知 Credential Manager 我们支持哪些类型的凭据。
这里只有通行密钥。
不过,我们也可以根据需要添加其他类型的凭据。
然后,我们调用 credentialManager.getCredential,并传入我们刚刚请求的凭据列表。
同样,没有界面代码。
无需编写任何界面代码。
Credential Manager 会为我们处理好这一切。
它会引导用户选择最常用的选项,同时保留其他选项作为备用。
这会自动解决当今最令人头疼的身份验证问题之一。
您注意到以下问题了吗?
即记住我上次使用的登录方式。
看,答案就在底部动作条中清晰地呈现。
顺便说一下,这不仅仅是让用户感到烦恼。
这是一个真正的痛点。
这会导致不必要的账号恢复操作,并产生商业影响,例如购物车被弃置。
这是我的演示应用代码,您可以用来亲自试用 Credential Manager。
您可以在自己的设备上看到这些流程。
左侧是官方 Google 代码示例。
该方法用于处理通行密钥的创建。
如何将现有密码用户升级为通行密钥?
为此,条件式创建是一个强大的工具。
如果用户刚刚使用密码登录,并且密码是由受支持的凭据提供程序自动填充的,那么我们知道他们正在积极参与,并且我们刚刚验证了他们的身份。
在这种情况下,条件式创建功能允许我们在后台自动请求创建通行密钥。
它不会中断用户的工作流程。
通行密钥会在后台自动生成并存储。
Google 密码管理工具会显示一条通知,确认已完成此操作。
其他密码管理工具可能会有所不同。
条件式创建的代码与注册代码非常相似。
val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
requestJson = registerRequestJson,
preferImmediatelyAvailableCredentials = false,
isConditional = true
)
val createCredentialResponse = credentialManager.createCredential (
context = localActivity,
request = createPublicKeyCredentialRequest
)magic 标志为 isConditional true,这可确保用户不会看到该界面。
由于它会伺机运行,因此我们会将其放入 try-catch 中。
我们会静默处理所有错误,因此如果该过程失败,我们不会打扰用户。
然后,通行密钥会保存到用户的 Credential Manager 中,以便下次使用。
Credential Manager 还允许凭据提供程序安全地导入和导出其凭据。
只需点击几下。
无需下载。
这提供了更大的灵活性,同时确保了信息的安全。
流程如下所示。
用户在其新凭证提供程序中请求导入凭证。
然后,Credential Manager 会处理导入界面,让用户能够选择凭证数据的来源。
然后,导出凭据提供程序可以提供自己的界面,以便向用户提供相关说明和警告。
最后,用户的新凭证提供程序会通过安全信道接收数据。
因此,它无需任何用户互动即可导入。
但如果用户从应用或网站中撤消通行密钥,会发生什么情况?
凭据提供程序不一定知道发生了这种情况。
下次他们尝试登录时,Credential Manager 会提供该失效通行密钥,而他们的尝试会被拒绝。
同样,当用户更新其用户名或显示名称时,也需要进行同步。
现在,借助 Signal API,您的应用可以向凭证提供程序标记这些特定状态。
以使用已撤销的密码登录为例。
您的应用可以捕获该特定错误状态,并使用它来告知用户的凭据提供程序。
然后,凭证提供方可以自行更新,以便不再提供该特定通行密钥。
以上就是 Credential Manager 的一些功能。它非常有助于引导用户使用通行密钥注册,并在登录时优先使用通行密钥。
但请记住,我们的最终目标是完全实现无密码。
Credential Manager 如何帮助实现这一目标?
以下是我们建议的五个步骤。
在前两个步骤中,您需要将使用密码的用户升级为使用通行密钥。
首先,我们尝试使用条件创建。
然后,如果这样的机会没有出现,我们最终将需要用户的干预,因此应用内邀请会要求用户设置通行密钥。
我们可以逐渐开始更突出地展示这些内容。
毕竟,从用户的角度来看,他们通常只需要提供屏锁方式(例如生物识别信息)即可。
当分析结果显示绝大多数用户都拥有使用通行密钥的账号时,就可以强制要求所有新用户使用通行密钥。
但对于那些老用户,我们会在最后三个步骤中处理。
我们鼓励通行密钥用户移除其密码。
因此,在第 3 步中,我们允许用户手动执行此操作,并在其账号设置中提供了一个非常清晰的“删除密码”选项。
在第 4 步中,我们开始推动用户删除密码,例如,通过应用内通知并搭配宣传文案。
最后,您将密码删除功能从“选择启用”切换为“选择退出”。
最终,甚至连最后一种选择退出方式也可能消失。
Google Analytics 将让我们有信心从应用中完全移除密码流程。
因此,迈向无密码世界不再是未来某个时候的理论安全概念。
它得到了广泛支持。
API 非常强大,用户体验也非常好。
我建议您立即开始使用 Credential Manager 集成通行密钥流程。
开始为用户升级,让我们彻底告别密码。
谢谢。
现在,我想请 Chiping 来谈谈设备迁移和身份验证方面的最新进展。
很好
好的,谢谢您,Tom。
正如 Tom 所强调的,在用户设备上实现无缝身份验证至关重要。
但如果用户换了新手机,会发生什么情况?
如今,用户体验历程中最大的流失往往发生在用户切换设备时。
今天,我想先谈谈我们如何解决这个问题,并快速介绍一下即将实施的 Play 商店凭据恢复要求。
自 2027 年 4 月起,Google Play 将要求应用满足免点按登录要求,以便在 Play 商店中保持完整的发布功能和最佳曝光度。
什么是免点按登录?
实际上,这意味着用户在改用新设备时,无需在您的应用中手动重新进行身份验证。
如何实现这一点?
我们建议您通过 Credential Manager 使用 Android 的 Restore Credentials API 来实现此目的。
借助此 API,您可以生成所谓的恢复密钥,该密钥会根据用户的备份设置保存在用户的设备上或云端。
我们建议在用户登录后,在后台生成此密钥。
在此图表中,我们展示了用户登录应用的过程。
然后,在用户使用应用期间,系统会在某个时间点创建恢复密钥。
然后,此密钥会存储在本地,或者如果已启用,则会存储在云端。
当用户设置新设备时,恢复凭证会在设备设置过程中在后台静默传输。
用户可以选择从云端或通过设备间传输来恢复数据。
然后,当用户打开您的应用时,您可以调用 Credential Manager API,使用此恢复密钥在新设备上首次启动时让用户登录。
这样,用户就可以立即快速、安全地登录您的应用。
如果您希望在设置后立即恢复应用,而无需打开应用,则可以实现自定义备份代理来执行此操作。
这样,用户无需手动打开应用即可登录。
我们的一些合作伙伴已在其应用中实现了此功能。
Uber 是全球最大的拼车公司,为数百万人提供交通服务,同时还支持送餐、医疗保健运输和货运物流。
访问的简便性至关重要。
当用户更换新设备时,他们希望能够无缝过渡,而无需每次都重新登录 Uber 应用或通过短信进行一次性身份验证。
这种频繁的设备更换给用户留存率带来了挑战。
在评估用户需求并进行代码原型设计后,Uber 在 Uber Rider 应用中引入了 Restore Credentials 支持,并取得了非常积极的成效。
他们估计,此举每天可节省 400 万次平均每日登录,并且预计随着采用率的提高,这一数字还会增加。
请扫描此二维码并访问相应链接,详细了解这项即将实施的政策,以及您的应用如何满足零点击恢复要求。
同样,此变更将于 2027 年 4 月生效,因此请务必在日历中做好标记。
如果您想抢先在应用中实现 Restore Credentials,请扫描此二维码并点击链接,访问我们的 Restore Credentials 技能。
此技能可用于帮助您快速开发或测试所选智能体的功能。
现在,让我们展望数字身份的下一个前沿领域:通过 Credential Manager 使用 Digital Credentials API。
我们来了解一下什么是数字凭证以及其运作方式。
数字凭证是可通过加密方式验证的数字文档,用户可以使用这些文档提供自己的相关信息。
这些证件存储在用户设备上的数字证件夹中,可供设备上的其他应用请求,也可通过混合方式(例如通过二维码)向外界展示。
其用途广泛多样,从验证身份、访问服务到在线购买商品,不一而足。
数字凭证提供了一种安全便捷的信息分享方式,无需使用实物卡。
数字凭证涉及三方:颁发方、持有方和验证方。
数字凭证的生命周期通常从左到右流动。
我们来逐一了解这些实体及其扮演的角色。
第一种是颁发者,顾名思义,就是颁发凭证的一方,就像政府颁发护照一样。
不过,在数字世界中,为了防止欺骗,这些凭证还会由签发者使用其数字证书进行签名,因此任何收到凭证的人都可以确切知道凭证来自何处。
实际上,任何人都可以成为发行方,无论是健身房、酒店,还是您自己。
您只需创建凭证并为其签名。
好的,签发方创建并签发凭证,但这些凭证需要存储在某个地方,就像您需要将政府身份证件存储在自己的钱包中一样。
这就是下一个参与方,称为持有人,通常也称为数字钱包。
顾名思义,持有者主要持有由签发者签发的凭证。
这些凭证会以应用的形式存储在设备上,用户可以向请求方出示这些凭证。
与签发者类似,任何人都可以成为持有者。
事实上,发行方和持有方由同一实体管理的情况并不少见。
例如,某家健身房希望创建自己的数字会员凭证,并通过自己的应用进行管理。
这相当于将发行方和持有方合二为一。
好的,第三方是验证方,即请求数字凭证的一方。
验证方通常会发出凭证请求,以检查用户的属性,例如年龄要求或姓氏等。
这些商家或应用需要额外的用户数据才能提供服务,例如购买酒精饮料或验证最低年龄。
在 Android 上,这三方与用户之间的互动通过 Credential Manager 和 Digital Credentials API 进行协调。
签发方可以调用该 API 向持有方签发凭证,但如果签发方和持有方是同一实体,则无需这样做。
凭证签发后,验证方可以调用 API 来请求凭证。
Credential Manager 会将请求与所有可用的持有者进行匹配,并将其呈现给用户,以便用户选择凭证并提供同意。
选择持有者后,系统会调用该持有者,并可在需要时执行额外检查,然后再最终与验证者共享凭证。
验证方收到凭证后,还可以对凭证的合法性执行各种检查,包括检查原始签发方。
现在,您可能想知道,数字凭证究竟是如何保障安全的?
实际上,该流程的每个步骤都内置了安全机制,我们现在就来了解其中一些机制。
因此,在签发方一端,当签发方想要与持有者共享凭证时,需要验证持有者无法被篡改或复制,基本上就是可以信任。
如何验证这一点?
在签发者签发凭证之前,需要持有人证明其自身身份以及所操作设备的安全性。
持有者发送其钱包认证,通常采用可验证的证书链形式。
持有者还会生成一个与硬件绑定的密钥对。
它会通过 Android 密钥认证将此密钥对中的公钥发送给发卡机构,以便发卡机构验证硬件的合法性。
签发者会将此公钥签名到签发的凭证中,以便将凭证正式绑定到单个设备。
在持有者端,持有者获得新凭证后,只会向 Credential Manager 注册其凭证元数据。
Credential Manager 会使用此元数据来确定在验证方发出凭证请求时要显示哪些持有者。
任何真实用户数据都不会注册到 Credential Manager,也不会向 Google 泄露。
即使 Credential Manager 正在选择要向用户显示的钱包,每个钱包中的内容也不会在此时显示。
现在,当发出凭证请求且用户同意分享凭证时,系统会调用所选凭证持有方,并将工作流控制权移交给该凭证持有方。
您可能还记得,凭证持有方只是一个应用,因此该应用会被调用。
此时,持有者可以看到请求验证方和请求的凭证等信息,并可以决定是否要与该验证方共享该凭证,即使用户已经授予了权限。
Google 无法访问凭证数据本身。
系统只会注册路由元数据,而载荷会在持有人和验证者之间进行端到端加密。
这样,持有者就可以控制要与哪些方共享其凭证。
最后,在验证方,请求凭证且持有人分享凭证后,验证方会收到持有人提供的凭证。
现在,验证方可以检查响应是否由原始凭证持有者针对请求全新生成。
它可以确认凭证响应是使用持有者的设备绑定密钥签名的,包括请求一次性令牌和调用持有者观察到的来源。
验证方还可以通过验证签发方证书来检查凭证签发方的真实性,确保其来自可信来源。
简而言之,签发方可获得硬件保障,持有人可控制要分享哪些凭证,验证方可获得签发方和持有人的加密证明,而用户则可完全掌控要分享哪些数据。
在介绍了数字凭证的安全性之后,我们再来了解一下目前在 Android 上通过 Digital Credentials API 可实现的一些数字凭证用例。
我要介绍的第一种方法是邮箱验证。
此 API 会直接从用户的设备中获取其登录的 Gmail 账号,并将其作为 Google 验证的数字凭据提供给您的应用,从而无需发送动态密码或魔法链接。
这意味着用户无需离开您的应用,只需在熟悉的 Credential Manager 底部动作条中点按一下即可提供同意声明。
下面是该 API 的实际应用演示录制。
这是一个示例应用,通过 Digital Credentials API 实现邮箱验证。
当我点按“请求经过验证的邮箱”时,Credential Manager 会向设备上持有此类凭据的持有者请求经过验证的邮箱数字凭据。
在这种情况下,只有 Google 是此经验证电子邮件凭证的持有者,您会在底部动作条中看到该凭证。
这是用户在设备上登录的其中一个电子邮件地址。
事实上,在这种情况下,Google 既是数字凭证的签发者,也是持有者。
选择要分享的凭证后,该凭证会返回到应用,应用目前仅显示凭证中包含的字段,即经过验证的邮箱。
在应用中,您可以使用此信息进入下一个应用界面,例如创建通行密钥或其他账号创建步骤。
对用户来说,这其实非常简单,只需表示同意,然后进入下一个界面即可。
如果您想试用,可以前往我们的 GitHub 示例,并将其安装到您自己的设备上。
请尝试在实体设备上使用该应用,并注意您需要在设备上登录至少一个 Gmail 账号。
因此,如果大家感兴趣,我会多留一点时间。
好了
听起来很棒,对吧?
除了邮箱验证,您还会想到哪种用户验证方式?
没错,就是电话号码验证。
Digital Credentials API 还原生支持电话号码验证,从而消除了短信动态密码带来的不便和漏洞。
电话号码验证也是通过 Digital Credentials API 进行的,在这种情况下,系统会请求设备上的电话号码,并将其作为数字凭证呈现。
用户只需在熟悉的 Credential Manager 底部动作条中分享或验证电话号码,无需离开应用,只需点按几下即可完成验证。
在这里,您可以看到一个示例应用,客户只需点按一下即可分享其电话号码,而无需离开应用。
那么,这种设置是如何运作的呢?
如果我们将此映射到数字凭证各方,那么在本例中,签发方是设备 SIM 卡上电话号码的电话运营商,而持有方是 Android Telephony。
当验证方(例如您的应用)请求用户的电话号码时,系统会通过 Digital Credentials API 调用从 Google Telephony 中检索该号码。
您可能想知道结算机制。
此流程有一个特有的额外步骤,即凭证会传递给您的运营商或聚合器,以便进行结算。
此过程发生在从 Google Telephony 检索电话号码之后,电话号码在 Google Telephony 中以加密状态存储。
然后,此令牌会传递给运营商,运营商会解密该令牌,并将电话号码直接返回给您的应用后端,在此期间,系统会向您收取费用。
这种方式的一大优势在于,运营商只会针对成功的验证向您收费,这些验证通过移动流量或 Wi-Fi 传输,比短信可靠得多。
在这里,我们演示了如何使用 Firebase 作为运营商聚合器进行电话号码验证。
该应用会在登录过程中要求用户提供电话号码。
此用户有多张 SIM 卡,并选择其中一张与应用共享。
用户确认选择后,交换过程会在后台完成。
然后,您会看到下一个界面,其中已填充检索到的电话号码。
用户无需离开应用,只需选择并确认要分享的电话号码,即可顺利完成整个流程,只需点按几下即可。
只需等待其完成,以便您再次看到完成状态。
因此,您会在最后一个屏幕上看到电话号码。
就是这么简单
今天我们探讨了许多内容。
我们首先探讨了如何使用 Credential Manager 改进通行密钥实现,并迈向无密码世界。
请参阅此处有关 Credential Manager 和通行密钥的资源,了解详情并获取我们的示例应用。
接下来,我们介绍了即将推出的零点按登录要求,该要求可让用户在设置新设备时顺畅登录。
Play 商店将于 2027 年 4 月强制执行此要求,请务必记下这个时间。
最后,我们介绍了数字凭证的工作原理,以及它们在 Android 上的一些最实用的功能。
如需深入探究相关内容并获取我们的示例,请参阅此处有关数字凭证的资源。
我们今天分享的所有内容均已上线,随时可供您使用。
Android 身份验证的未来已来,比以往任何时候都更令人兴奋。
感谢大家抽出宝贵时间,现在是午餐时间,请享用外面的美食。
如果您对通行密钥或数字凭证有任何疑问,请随时向我和 Tom 提问。
谢谢!
val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
requestJson = registerRequestJson
)
val createCredentialResponse = credentialManager.createCredential (
context = localActivity,
request = createPublicKeyCredentialRequest
)val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
requestJson = authenticationRequestJson
)
val signInRequest =
GetCredentialRequest(listOf(getPublicKeyCredentialOption))
val result = credentialManager.getCredential (
context = localActivity,
request = signInRequest
)val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
requestJson = registerRequestJson,
preferImmediatelyAvailableCredentials = false,
isConditional = true
)
val createCredentialResponse = credentialManager.createCredential (
context = localActivity,
request = createPublicKeyCredentialRequest
)