Streamline sign-in with passkey upgrades and credential managers
Privacy & Security 进阶 20m

用 Passkey 自动升级和凭证管理器简化登录

Streamline sign-in with passkey upgrades and credential managers

2024年6月10日

在 Apple 官方观看视频

一句话判断

Automatic passkey upgrades 让你在零用户感知的情况下把现有密码账号升级为 passkey,这是推动行业消灭密码的最关键一步。

这场 Session 讲了什么

这场 Session 聚焦三个话题:automatic passkey upgrades(自动 passkey 升级)、credential manager(凭证管理器)的新功能、以及如何让你的 App 在新的 Passwords app 中表现更好。

核心亮点是 automatic passkey upgrades。传统流程是:用户登录后,App 弹一个 upsell 界面问”要不要创建 passkey?“。新流程完全跳过这一步——用户正常用密码登录,系统在后台自动创建 passkey 并弹一个通知。下次登录就是一次点击。整个过程没有中断用户的操作流程。

Session 还讨论了 passkey 迁移的终极目标:不是给账号加一个 passkey 作为备选,而是最终消灭所有可被钓鱼的认证因素(密码、SMS、邮件验证码、push 通知、TOTP)。

值得深挖的点

Automatic Passkey Upgrades 的工作机制

这个功能的精妙之处在于它被设计为一个”progressive enhancement”(渐进增强),而不是一个必须成功的操作。系统会做一系列内部检查:设备是否设置了 credential manager、是否开启了 passcode(使用 passkey 的前提)、Web 场景下是否是非隐私浏览标签。即使系统检查通过,credential manager 还会做自己的条件判断——最重要的是确认刚刚是否用同一个账号的用户名和密码完成登录。只有全部条件满足,才会自动创建 passkey。

如果任何条件不满足,你只会收到一个 error,不会显示任何 UI。你的 App 可以选择在这个时候 fallback 到传统的 upsell 对话框,也可以选择什么都不做,下次再试。这个设计非常务实:不追求 100% 转化率,而是追求在可以转化的场景下做到零摩擦。

从密码到无密码的战略路径

Session 提出了一个清晰的阶段性策略。第一步:给现有账号添加 passkey 作为替代登录方式(这就是 automatic passkey upgrades 解决的问题)。第二步:观察用户行为,如果某个账号已经不再使用密码,考虑移除密码。第三步:最终目标——消除所有可被钓鱼的认证因素。

Session 特别强调了一个容易被忽略的点:SMS、邮件验证码、push 通知、TOTP 这些所谓的”多因素认证”本质上都是可被钓鱼的。它们组合在一起也解决不了根本问题。Passkey 是目前唯一真正抗钓鱼的认证方案。这意味着”密码 + 短信验证码”在安全性上远远不如单独一个 passkey。

代码片段

App 端发起 automatic passkey upgrade

场景:用户用密码登录成功后,尝试在后台自动创建 passkey。

// 注册请求使用 .conditional 样式
let registrationRequest = ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest(
    challenge: challenge,
    relyingPartyIdentifier: "your-app.com",
    name: username,
    userID: userID
)
// 关键:设置为 conditional 模式
registrationRequest.style = .conditional

let controller = ASAuthorizationController([registrationRequest])
controller.delegate = self
controller.performRequests()

坑:如果系统条件不满足,你只会收到 error,不会显示任何 UI——这是预期行为,不要 panic。

Web 端发起 automatic passkey upgrade

场景:在网站上实现自动 passkey 升级。

// 先检查浏览器是否支持
const capabilities = PublicKeyCredential.getClientCapabilities();
if (capabilities?.conditionalCreate) {
    // 发起 conditional 注册
    const credential = await navigator.credentials.create({
        publicKey: {
            challenge: challenge,
            rp: { name: "Your App", id: "yourapp.com" },
            user: { ... },
            pubKeyCredParams: [...],
            // 关键参数
            mediation: "conditional"
        }
    });
}

坑:Web 端必须先用 getClientCapabilities 检查浏览器是否支持 conditionalCreate,否则会直接报错。

Credential Manager 填充验证码

场景:让 credential manager 自动填充基于时间的验证码。

<!-- Info.plist 新增 key -->
<key>ASCredentialProviderExtensionCapabilities</key>
<dict>
    <key>ProvidesOneTimeCodes</key>
    <true/>
</dict>

坑:这个功能是给第三方 credential manager(如 1Password)用的,不是给你的 App 用的。你的 App 只需要正确使用 ASAuthorizationController 即可。

最佳实践

已有项目:如果你已经实现了 passkey 注册,改动极小——把注册请求的 style 设为 .conditional(App)或 mediation 设为 "conditional"(Web),然后在 error 回调中保留你原有的 upsell 逻辑作为 fallback。如果你还没实现 passkey,现在是个好时机,因为 automatic upgrades 让迁移成本几乎为零。

新项目:直接让用户用 passkey 注册。Session 明确说”一个只有 passkey 的账号不会被钓鱼,不会被遗忘,不需要重置”。密码应该只在兼容性需要时才提供。同时,确保你的 App 在新的 Passwords app 中正确展示——添加 webCredentials associated domain,让 Passwords app 能正确关联你的 App 和网站。

还有什么值得关注

  • 新的 Passwords app 在 iOS 18 中独立出来,你的 App 图标和名称会在其中展示,确保 associated domain 配置正确。
  • Credential manager 现在可以填充用户名、密码、一次性验证码到任何文本框(不仅仅是系统标准的 credential 填充场景)。
  • Session 反复强调”消灭密码”是终局——如果你的系统检测到用户不再使用密码,是时候考虑移除它了。
WWDC 2024