# Passkey 中文学习实验

这是可在本机运行的 WebAuthn 注册与无用户名登录示例，配套中文学习文档。服务器使用 `@simplewebauthn/server 14.0.3` 与 `Express 5.3.0`；浏览器使用原生 `navigator.credentials.create()` / `get()`，无需前端构建。

## 启动

需要 Node.js 22 或更高版本。进入本目录后运行：

```sh
npm ci
npm test
npm start
```

打开 **http://localhost:3000**。不要改用 `127.0.0.1`、局域网 IP 或其他端口：示例固定验证 Host、RP ID 与 origin。服务仅监听本机，不应部署为公开认证服务。

1. 点击“创建实验账号与通行密钥”，完成系统提示。
2. 点击退出。
3. 点击“用通行密钥登录”，选择刚才的凭据。
4. 确认回到同一个随机实验账号。

浏览器、操作系统和认证器需要支持可发现凭据与本地用户验证；提示方式因设备而异。取消、超时或设备不满足策略时，不会建立登录会话。

## 阅读代码

- `server.js`：四个协议接口、一次性 challenge、账号归属、真实验签、会话轮换与计数更新。
- `public/browser.js`：JSON base64url 与 WebAuthn 字节字段的转换。
- `public/app.js`：浏览器调用、提交响应与错误反馈。
- `server.test.js`：软件生成 CBOR 注册数据与 ES256 签名响应，测试有效流程及安全拒绝路径。

POST 接口为 `/api/registration/options`、`/api/registration/verify`、`/api/authentication/options`、`/api/authentication/verify`。选项响应直接返回 JSON；验证请求直接提交转换后的凭据对象。`GET /api/session` 查询会话，`POST /api/logout` 退出。

## 已实现的保护与明确边界

challenge 使用 32 字节安全随机数，绑定 HttpOnly / SameSite=Strict Cookie 对应的服务端会话与操作，五分钟过期，在异步验签前原子消费，成功失败均不可重用。新操作覆盖当前会话上次待验证挑战。

注册创建独立匿名账号，不接受客户端指定的用户名或账号 ID；没有邮箱身份验证或已有账号添加凭据功能。认证通过 credential ID 查服务端公钥，并要求 user handle 与凭据所有者一致。注册和认证均要求 UP / UV；固定 origin / RP ID；登录后轮换会话。请求有来源、格式、大小、频率与存储上限。

单进程锁保护计数器的读、验、写；沿用验证库的计数策略：双方均为零时允许，非零回退时拒绝。生产环境应结合设备与风险政策评估计数异常。

**全部数据仅保存在进程内存**，最多 500 个凭据、1,000 个一小时会话；每个 IP 每分钟最多 120 个 API 请求。重启后账号与公钥记录清空；设备上的实验凭据可能仍保留，可到设备通行密钥管理器删除。

没有实现数据库、跨实例事务、线上 HTTPS / Secure Cookie 配置、真实身份验证、账号恢复、撤销与多凭据管理。自动测试验证软件生成响应的实际密码学路径，不代替硬件、平台 UI 和同步测试。仅修改域名不能把本示例变成完整生产系统。

## 英文参考

- WebAuthn L2 §7.1：https://www.w3.org/TR/webauthn-2/#sctn-registering-a-new-credential
- WebAuthn L2 §7.2：https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion
- SimpleWebAuthn Server：https://simplewebauthn.dev/docs/packages/server
- SimpleWebAuthn Browser：https://simplewebauthn.dev/docs/packages/browser

本示例为原创教学实现。规范内容归属及文档许可见配套站点“来源、版本与译注”。
