WWDC Quick Look 💓 By SwiftGGTeam
Streamline sign-in with passkey upgrades and credential managers

Streamline sign-in with passkey upgrades and credential managers

观看原视频

Highlight

Automatic passkey upgrades 让用户在用密码登录后,系统自动在后台创建 passkey 并通过通知告知,无需中断用户操作流程。


核心内容

今天的登录体验往往很繁琐:输入用户名、输入密码、等待短信验证码、再输入验证码——多步流程虽提升了安全性,但也很慢。很多 App 会在登录后弹出一个 upsell 界面问”要不要创建 passkey?“,但用户往往忽略或拒绝。

WWDC 2024 引入了 automatic passkey upgrades,完全改变了这个流程。用户正常用密码登录后,系统在后台自动创建 passkey 并弹出一个通知:“已为您创建 passkey”。下次登录时只需一次点击。整个过程中没有任何中断式的 upsell 界面。

Session 指出,预防钓鱼的最好方式是让账号没有任何可被钓鱼的认证因素。密码、SMS、邮件验证码、push 通知、TOTP——这些都可以被钓鱼。真正的目标是最终消灭所有可被钓鱼的因素,而 passkey 是通往这一目标的第一步。


详细内容

Automatic passkey upgrades 的实现原理

系统在处理自动升级请求时会进行一系列检查(04:18):

  1. 系统内部检查:设备是否设置了凭证管理器并支持自动升级?设备是否设置了密码?
  2. Web 特定检查:是否是非隐私浏览标签页?
  3. 凭证管理器条件:最重要的条件是——该管理器刚才是否刚刚填入了该账号的用户名和密码?

如果所有条件都满足,系统返回新创建的 passkey,设备显示通知。如果条件不满足,返回错误,但不显示任何 UI。

iOS/macOS 实现

传统的 upsell 方式是这样的(01:19):

// Offering a passkey upsell

func signIn() async throws {
    let userInfo = try await signInWithPassword()
    guard !userInfo.hasPasskey else { return }
    let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
        relyingPartyIdentifier: "example.com")

    guard try offerPasskeyUpsell() else { return }
    let request = provider.createCredentialRegistrationRequest(
        challenge: try await fetchChallenge(),
        name: userInfo.user,
        userID: userInfo.accountID)

    do {
        let passkey = try await authorizationController.performRequest(request)
        // Save new passkey to the backend
    } catch {  }
}

使用 automatic passkey upgrades 后,只需将 requestStyle 设为 .conditional02:18):

// Automatic passkey upgrade

func signIn() async throws {
    let userInfo = try await signInWithPassword()
    guard !userInfo.hasPasskey else { return }
    let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
        relyingPartyIdentifier: "example.com")

    let request = provider.createCredentialRegistrationRequest(
        challenge: try await fetchChallenge(),
        name: userInfo.user,
        userID: userInfo.accountID,
        requestStyle: .conditional)

    do {
        let passkey = try await authorizationController.performRequest(request)
        // Save new passkey to the backend
    } catch {  }
}

关键点

  • requestStyle: .conditional 将请求从弹窗式创建改为条件式自动创建
  • 如果系统条件不满足,会返回错误,此时可以选择显示原有的 upsell 对话框或下次再试
  • 整个过程不会阻塞用户的正常 App 使用流程

Web 实现

标准的 Web passkey 注册是模态弹窗(03:15):

// Modal passkey creation

const options = {
    "publicKey": {
        "rp": { … },
        "user": {
            "name": userInfo.user,

        },
        "challenge": …,
        "pubKeyCredParams": [ … ]
    },
};

await navigator.credentials.create(options);

添加 mediation: "conditional" 参数后即可实现自动升级(04:03):

// Automatic passkey creation

let capabilities = await PublicKeyCredential.getClientCapabilities();
if (!capabilities.conditionalCreate) { return; }

const options = {
    "publicKey": {
        "rp": { … },
        "user": {
            "name": userInfo.user,

        },
        "challenge": …,
        "pubKeyCredParams": [ … ]
    },
    "mediation": "conditional"
};

await navigator.credentials.create(options);

关键点

  • 使用 getClientCapabilities() 检查浏览器是否支持条件式创建
  • mediation: "conditional" 将请求从用户显式确认改为系统条件判断
  • 如果不支持,可以回退到传统的模态注册方式

Credential Manager 的新功能

凭证管理器扩展获得了更多能力(09:22):

  • 支持 time-based verification codes(基于时间的验证码)
  • 可以填充任意文本字段的用户名、密码或一次性代码
  • 最多可以选择 3 个 App 用于 AutoFill

在 Info.plist 中声明这些能力(05:04):

<dict>
	<key>NSExtensionAttributes</key>
	<dict>
		<key>ASCredentialProviderExtensionCapabilities</key>
		<dict>
			<key>ProvidesPasswords</key>
			<true/>
			<key>ProvidesPasskeys</key>
			<true/>
			<key>SupportsConditionalPasskeyRegistration</key>
			<true/>
			<key>ProvidesOneTimeCodes</key>
			<true/>
			<key>ProvidesTextToInsert</key>
			<true/>
		</dict>
	</dict>
</dict>

关键点

  • ProvidesPasswordsProvidesPasskeys 声明支持的凭证类型
  • SupportsConditionalPasskeyRegistration 表明支持自动 passkey 升级
  • ProvidesOneTimeCodes 支持一次性验证码填充
  • ProvidesTextToInsert 允许填充任意文本字段

在 Passwords App 中展示

iOS 18 和 macOS Sequoia 引入了新的 Passwords app10:17)。为了让你的网站和 App 在其中更好地展示:

  1. 使用 OpenGraph 元数据:Passwords app 会读取 og:site_name 来显示网站名称
  2. 提供高清图标:确保网站有合适的图标文件
  3. 支持 otpauth:// 链接:让验证码设置成为一键体验

当同一个账号同时有 passkey 和密码时,Passwords app 会将它们合并为单个条目(11:34),用户可以在一个地方看到所有登录方式。


核心启发

1. 为现有账号启用自动 passkey 升级

  • 为什么值得做:让用户无需改变任何习惯,就能在登录后自动获得更安全、更快捷的登录方式。Apple 的系统级判断确保只在合适时机创建 passkey,避免打扰。
  • 怎么开始:在 iOS/macOS 上将 requestStyle 设为 .conditional,在 Web 上设置 mediation: "conditional"。在 error 回调中保留原有的 upsell 逻辑作为 fallback。

2. 新项目直接使用 passkey 作为唯一登录方式

  • 为什么值得做:只有 passkey 的账号不会被钓鱼、不会被遗忘、几乎不需要重置。Session 明确指出这是最终目标——消灭所有可被钓鱼的认证因素。
  • 怎么开始:新用户注册时直接创建 passkey,不再提供密码选项。只在确实有兼容性需求时才提供密码作为备选。

3. 优化网站在 Passwords app 中的展示

  • 为什么值得做:Passwords app 是 iOS 18/macOS Sequoia 用户的密码管理中心,良好的展示能提升品牌认知度,方便用户识别和管理账号。
  • 怎么开始:确保网站所有页面和子域都有正确的 OpenGraph 元数据(og:site_name),提供高分辨率的图标文件。对于提供验证码的服务,支持 otpauth:// 链接以实现一键设置。

关联 Session

评论

GitHub Issues · utterances