题干与适用场景
产品已有用户名、密码和条件式获取的 passkey 登录。现在希望在用户完成一次高信任登录后,利用 WebAuthn Level 3 的 conditional create 在不打断流程的情况下建议创建 passkey。请说明浏览器能力检测、创建条件、服务端校验、隐私边界和回滚策略。
题目聚焦 Web 平台与认证交互。不要把“浏览器支持 API”当成“用户设备一定有可用验证器”;创建仍需经过用户同意和服务端验证。
面试官考察点
- 能否区分 conditional create、conditional get 和显式按钮创建。
- 能否在能力、策略、用户状态和跨设备同步之间建立清晰的门槛。
- 能否避免重复创建、误绑定账号、泄露凭据存在性或让失败阻塞密码登录。
- 能否设计可观测的灰度和撤回,而不是把新 API 一次性打开。
强回答会把条件式创建当作增强路径:能力检测失败或用户拒绝时主流程不变,服务端仍验证 challenge、origin、RP ID 和用户归属。
回答前需要澄清的问题
- 目标是所有已登录用户,还是只针对已验证邮箱、完成 MFA 或高风险操作后的用户?
- 是否允许创建多台设备同步的 passkey?账户恢复和撤销策略会因此改变。
- 当前前端是否已经有一个未完成的
credentials.create()请求?并发请求会产生难以解释的提示。 - 浏览器不支持 conditional create 时,是否显示显式“创建 passkey”按钮,还是完全隐藏?
30 秒回答框架
“我先用 WebAuthn 能力检测确认浏览器支持 conditional create,再结合账号状态、最近认证强度和服务端 feature flag 决定是否尝试。前端只在用户明确允许的时机发起一次创建,并用 AbortController 取消重复请求;任何不支持、取消或超时都回到原登录流程。服务端为一次性 challenge 绑定用户、origin、RP ID 和过期时间,验证成功后才保存 credential。灰度观察创建成功率、取消率、重复凭据、登录回退率和恢复工单;异常时关闭 flag,已创建的凭据仍可单独撤销。”
分步骤深入解答
1. 区分两种条件式交互
conditional get 让用户在输入账号时选择已有 passkey;conditional create 则是在表单上下文中建议创建新凭据。两者都依赖浏览器与验证器能力,不能只检查 PublicKeyCredential 对象就断言可用。
能力检测结果只决定是否尝试,不决定是否授权。服务端仍应把注册挑战绑定到当前已认证用户,并记录创建来源和策略版本。
2. 设计创建门槛
建议同时满足:浏览器报告 conditional create 能力;用户已完成足够强度的认证;账户未处于恢复、合并或高风险变更;服务端 feature flag 对该租户和流量窗口开放。对新设备可先显示解释文案,再由用户继续,而不是在页面加载时静默创建。
3. 防止重复与竞态
每个页面会话最多有一个创建请求。开始新请求前取消旧请求,组件卸载时也取消。服务端 challenge 使用一次即失效,credential ID 设唯一约束;重复提交返回幂等结果,不创建第二条记录。
capability -> policy gate -> one challenge -> user consent
| | |
fallback feature flag server verify -> persist4. 完整验证注册结果
服务端验证 challenge、origin、RP ID、签名、用户句柄、attestation 策略和 credential ID。不要只相信前端返回的“创建成功”。如果不需要设备证明,可采用更简单的 attestation 策略,但必须说明这是隐私与风控权衡。
5. 处理同步与恢复
多设备同步的 passkey 让用户换设备后仍可能看到凭据,但同步不等于账户恢复。提供查看设备、撤销单个 credential、失去所有设备后的恢复流程;不要把是否存在 passkey 暴露给未认证请求。
6. 灰度和可观测性
按浏览器、平台、租户和风险等级分批打开。记录能力检测通过率、创建提示出现率、用户同意率、验证失败原因、取消/超时、重复 credential、密码回退率和支持工单。指标要区分“浏览器不支持”和“用户拒绝”,否则无法决定改文案还是关功能。
7. 回滚与兼容
用远程 flag 关闭新创建尝试,保留已有 passkey 的登录能力。撤回时不删除凭据,避免把发布回滚误做成账户破坏。服务端保留旧 challenge 版本的验证窗口,并设置过期时间;规范或浏览器行为改变后重新进行兼容测试。
高质量示范回答
我会把 conditional create 当作可撤回的增强路径。前端先检查浏览器能力,再由账号状态、最近认证强度和 feature flag 决定是否尝试;每个页面会话只允许一个请求,重复请求用 AbortController 取消。服务端为当前用户生成一次性 challenge,并校验 challenge、origin、RP ID、签名和 credential ID 后才保存。用户取消、不支持或超时都回到密码登录,不把失败当成登录失败。灰度按平台和风险分批,观察能力通过率、同意率、验证失败、重复凭据、回退率和恢复工单。关闭 flag 只停止新创建,保留已有 passkey 和撤销能力。
常见错误
- 错误表现 → 只检查
PublicKeyCredential是否存在 → 对象存在不代表 conditional create 和验证器都可用;修正方法:使用能力检测并保留回退路径。 - 错误表现 → 页面加载就静默调用创建 → 用户未同意且容易产生重复提示;修正方法:在明确的高信任时机触发,并让用户控制继续。
- 错误表现 → 只在前端判断成功 → challenge、origin 或账户归属可能未验证;修正方法:服务端完成完整注册验证。
- 错误表现 → 回滚时删除已创建凭据 → 发布回退变成账户破坏;修正方法:关闭新创建 flag,凭据撤销独立处理。
追问及应对
用户拒绝创建后是否每天再提示?
不要无限提示。记录本地冷却期和服务端策略版本,只有账户风险、设备或产品条件显著变化时再询问;拒绝本身不应影响密码登录。
同一账户出现两个相同 credential ID 怎么办?
把 credential ID 设为唯一键,注册接口做幂等处理并记录来源。若数据库已有重复数据,先冻结新增写入,再按用户与 credential ID 合并或人工审核,不能静默覆盖。
浏览器支持能力但创建成功率突然下降,先查什么?
先按浏览器版本、平台、认证器类型和错误码切片,区分用户取消、超时、策略拒绝和服务端验证失败。暂停受影响分组的 flag,保留密码与已有 passkey 登录,修复后用小流量重新验证。