macOS Passkeys原生集成解析:Cate如何用Apple AuthenticationServices打通网页通行密钥
【免费下载链接】cateAn infinite zoomable canvas for coding. Editor, terminal, and browser panels in a spatial workspace.项目地址: https://gitcode.com/gh_mirrors/cate5/cate
Cate是一款支持无限缩放画布的编码工作台,把编辑器、终端和浏览器面板放在同一个空间化的工作区里。而它藏在 macOS 里的一项硬核能力,是让网页通行密钥(Passkeys)不再依赖 Chromium 的默认路径,而是直接调用 Apple 的AuthenticationServices框架,原生打通 Face ID、iCloud 钥匙串和 iPhone 扫码登录。本文将带你完整看懂 Cate 的 macOS Passkeys 原生集成方案:为什么需要它、四层架构各自做什么、以及它如何守住安全边界。
为什么网页通行密钥需要一层"原生桥"?
Passkeys 是基于 WebAuthn 标准的无密码登录方式:注册和登录时,私钥保存在操作系统、云钥匙串或安全密钥硬件中,网页拿到的只是一次性签名,密码泄露问题被从根上解决。
但 Cate 基于 Electron(Chromium 内核)构建,Chromium 内置的 WebAuthn 实现走的是它自己的一整套认证器通道。这意味着在 macOS 上,网页想使用系统钥匙串里的通行密钥、或者用 Face ID 解锁时,体验和能力都受限于 Chromium 的支持范围。
Apple 为此专门开放了macOS Browsers Passkeys能力:任何获得 Apple 审核授权的浏览器应用,都可以用AuthenticationServices的公开 API,代表任意网页发起通行密钥注册与登录——Cate 正是这样做的。🔐
它的选择非常克制:
- 使用 Apple 的浏览器 API,携带网页真实的请求来源(origin),而不是假装网站与 Cate 建立了"关联域名";
- 没有 Apple 授权或签名不完整的开发构建,保持 Chromium 原有 WebAuthn 路径原封不动,用户无感知地回退。
这个策略和边界在 docs/browser-passkeys.md 中有完整说明。
整体架构:一次登录请求的四层旅程
在 Cate 里点一次"用通行密钥登录",请求会穿过四层。每一层只做一件事、守一道关:
网页 JS (navigator.credentials.get) ↓ ① 页面桥:序列化请求、接管 WebAuthn 调用 主进程 (browserPasskeys.ts) ↓ ② 策略校验:安全来源、RP ID、参数限额 原生插件 (passkeys.node,Node-API) ↓ ③ ASAuthorizationController:系统级 UI + 认证器 Apple 系统 (Face ID / iCloud 钥匙串 / 安全密钥 / 手机)第①层 页面桥:接管 WebAuthn 而网页无感知
浏览器面板的预加载脚本会把一个自包含的桥接函数注入页面主世界,源码见 passkeyPageBridge.ts。它替换了navigator.credentials.create()和get()两个方法:
- 把二进制参数(challenge、credential ID 等)转成 Base64URL 字符串,走 IPC 传给主进程;
- 监听
AbortSignal:网页一旦取消、或用户切走页面,立即通知原生层撤销弹窗; - 把原生层返回的签名结果重新包装成标准的
PublicKeyCredential对象——toJSON、getAuthenticatorData()等方法一应俱全,网页 JS 完全感觉不到背后换了一条通道。
第②层 主进程:不信任任何请求
主进程入口是 browserPasskeys.ts,它先检查 macOS 版本 ≥ 14.4、原生插件可用、请求确实来自已注册浏览器面板的顶层主框架,且面板处于聚焦状态,才放行。
真正的参数审查在 passkeyPolicy.ts:
- 安全来源:仅允许 HTTPS(及本地 localhost),拒绝其他协议;
- RP ID 校验:依赖方 ID 必须是当前域名本身或其父域,用
tldts解析公共后缀(含私有后缀),防止小站冒用大域名的通行密钥; - 尺寸与结构限额:challenge、凭据 ID 有字节上限,凭据列表不超过 256 条;
- 不支持就明说:企业级证明(enterprise attestation)、PRF 等尚未实现的扩展,会直接抛出
NotSupportedError,而不是假装"已评估通过"——这一点在安全上至关重要。
第③层 原生插件:Objective-C++ 直连 AuthenticationServices
核心实现是 native/passkeys/passkeys.mm,一个编译为passkeys.node的 Node-API 插件(Apple Silicon + Intel 通用二进制),对外只暴露isAvailable/request/cancel三个函数。
它的工作流程:
- 能力自检:用
SecTaskCopyValueForEntitlement检查进程是否持有com.apple.developer.web-browser.public-key-credential权限——没有授权,一律返回不可用(见 passkeys.mm); - 组装请求:把 WebAuthn 参数翻译成
ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest/AssertionRequest(系统认证器,即 Face ID + 钥匙串),以及ASAuthorizationSecurityKeyPublicKeyCredentialProvider(USB/蓝牙安全密钥)两种原生请求,按网页的authenticatorAttachment偏好筛选; - 系统级弹窗:交给
ASAuthorizationController.performRequests()呈现模态授权面板。特意保留 Apple 默认的展示方式——它会在本地没有匹配通行密钥时自动提供 iPhone 扫码登录的降级路径; - 结果映射:把 Apple 返回的
rawClientDataJSON、attestationObject、authenticatorData、signature等字段编码回 WebAuthn 标准格式,并依据attachment属性如实标注这是"本机平台认证器"还是"跨平台(手机)"凭据。
注册成功后,主进程还会用 passkeyAttestation.ts 解析证明对象中的 CBOR 结构,安全地提取publicKey/publicKeyAlgorithm等 WebAuthn 标准字段——解析器有深度和长度限制,只读取系统认证器返回的数据。
第④层 签名与授权:Apple 审核是硬门槛
这套集成只对通过 Apple 审核的签名构建生效,这也是整个方案最难的部分:
- 需要组织向 Apple 申请 macOS Browsers Passkeys 能力,并生成 Developer ID 配置描述文件;
- 打包脚本 passkey-packaging.cjs 在
beforePack钩子里解析描述文件,校验有效期、App ID(com.cate.app)和权限位,然后把权限写入主程序的 entitlements;afterSign钩子再复核一次签名; - 没有提供描述文件的普通构建,插件照样加载但
isAvailable()返回 false,Chromium 原路径接管——功能静默降级,应用照常可用。
用户体验:Face ID、安全密钥与 iPhone 扫码
对最终用户而言,体验是"无感"的:
- 本机登录:网页发起登录后,系统弹出原生面板,Touch ID / Face ID 一按即完成;
- USB 安全密钥:同一面板里插入或点亮安全密钥,走 FIDO 断言流程;
- 手机接力:本地没有对应通行密钥时,面板自动出现"其他选项 → 附近设备的 Passkey",用 iPhone 摄像头扫码并在手机上确认(📱)——配对、邻近检查、加密交换全部由 Apple 负责,Cate 只是把断言结果交回给请求它的网页。
细节与验收标准(包括注册、排除凭据、多账号、取消、弹窗期间导航/关窗等场景)都记录在 docs/browser-passkeys.md 中。
能力边界:诚实声明支持范围
Cate 对这一特性的态度是"不宣称完整对齐 Chrome":
| 已支持 | 未实现 |
|---|---|
| 显式(explicit)注册与登录 | 条件式通行密钥(conditional UI) |
| 系统平台认证器 + 安全密钥 | 跨域 iframe、Related Origins |
| 安全来源与 RP ID 严格校验 | 企业证明、PRF、largeBlob 等扩展 |
| iPhone 扫码降级路径 | Google 密码管理器同步 |
不支持的扩展会被明确拒绝而非静默吞掉;Windows / Linux 上插件根本不加载,完整沿用 Chromium 原生 WebAuthn。
如何自行验证
如果你拿到了带授权配置的构建,可按官方文档的验证清单跑一遍:
npm run build:passkeys npx vitest run src/main/browser/passkeyPolicy.test.ts src/main/browser/browserPasskeys.test.ts对应测试覆盖了策略校验、请求转发与错误映射;E2E 层面则用 e2e/browser-session-persistence.spec.ts 验证面板会话在真实浏览器流程中的表现。
小结
Cate 的 macOS Passkeys 集成是一次教科书式的"能力受限环境下的原生桥接"实践:
- 克制——只为显式
create()/get()建桥,不越权、不冒名; - 分层——页面桥、主进程策略、原生插件、系统框架各司其职,每层独立可测试;
- 诚实——不支持的能力明确报错,未授权的构建安全回退。
正是这些取舍,让一个 Electron 应用既能站在 Apple 系统级身份体系之上,又不给安全和维护性留下隐患。
【免费下载链接】cateAn infinite zoomable canvas for coding. Editor, terminal, and browser panels in a spatial workspace.项目地址: https://gitcode.com/gh_mirrors/cate5/cate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考