@flun/webauthn-browser 2.1.9 → 2.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/LICENSE +35 -19
- package/README.md +46 -33
- package/helpers/base64urlAndBuffer.js +2 -2
- package/helpers/common.js +4 -4
- package/helpers/identifyAuthenticationError.js +5 -4
- package/helpers/identifyRegistrationError.js +12 -9
- package/helpers/index.d.ts +14 -8
- package/helpers/isValidDomain.js +5 -4
- package/helpers/platformAuthenticatorIsAvailable.js +1 -1
- package/helpers/toPublicKeyCredentialDescriptor.js +2 -2
- package/helpers/webAuthnError.js +2 -2
- package/index.d.ts +8 -6
- package/methods/index.d.ts +5 -5
- package/methods/startAuthentication.js +6 -4
- package/methods/startRegistration.js +5 -4
- package/package.json +3 -3
- package/types/dom.d.ts +16 -16
- package/types/index.d.ts +31 -27
package/CHANGELOG.md
CHANGED
package/LICENSE
CHANGED
|
@@ -1,19 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
ISC 许可证 (ISC License)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, flun <open@flun.top>
|
|
4
|
+
衍生开发 本项目基于 Matthew Miller 作者的原代码进行开发、修改和扩展;
|
|
5
|
+
|
|
6
|
+
特此免费授予任何获得本软件及相关文档文件("软件")副本的人,不受限制地处理本软件的权利,
|
|
7
|
+
包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售本软件副本的权利,
|
|
8
|
+
并允许向其提供本软件的人这样做,但须满足以下条件:
|
|
9
|
+
|
|
10
|
+
上述版权声明和本许可声明应包含在本软件的所有副本或重要部分中;
|
|
11
|
+
|
|
12
|
+
本软件按"原样"提供,不作任何形式的明示或暗示的保证,
|
|
13
|
+
包括但不限于适销性、特定用途的适用性和不侵权的保证;
|
|
14
|
+
在任何情况下,作者或版权持有人均不对因本软件或本软件的使用或其它交易而产生的
|
|
15
|
+
任何索赔、损害或其它责任负责,无论是合同之诉、侵权之诉还是其它行为之诉;
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
英文原文 (English Version)
|
|
20
|
+
|
|
21
|
+
ISC License
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2026, flun <open@flun.top>
|
|
24
|
+
|
|
25
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
26
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
27
|
+
copyright notice and this permission notice appear in all copies.
|
|
28
|
+
|
|
29
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
30
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
31
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
32
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
33
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
34
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
35
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|

|
|
4
4
|
[](https://www.npmjs.com/package/@flun/webauthn-browser)
|
|
5
5
|
|
|
6
|
-
**@flun/webauthn-browser** 是一个专为浏览器环境设计的 WebAuthn
|
|
6
|
+
**@flun/webauthn-browser** 是一个专为浏览器环境设计的 WebAuthn(含 Passkeys)前端工具库,
|
|
7
|
+
基于 [@simplewebauthn/browser](https://simplewebauthn.dev/) 核心逻辑重构并增强,它封装了 `navigator.credentials` API
|
|
8
|
+
的复杂细节,提供简洁的 Promise 风格接口,并内置了丰富的错误处理与 Base64URL 编解码工具,
|
|
9
|
+
并且新增在Windows下优先调用底层Windows hello 注册/认证(避免环境干扰与安全);
|
|
7
10
|
|
|
8
11
|
本包以 **ESM** 编写,同时提供 **UMD** 打包产物,可通过 npm 模块方式或 `<script>` 全局方式引入,兼容现代浏览器及部分旧版环境;
|
|
9
12
|
|
|
@@ -15,7 +18,7 @@
|
|
|
15
18
|
- [安装](#安装)
|
|
16
19
|
- [NPM 模块方式](#npm-模块方式)
|
|
17
20
|
- [UMD 全局方式](#umd-全局方式)
|
|
18
|
-
- [ES2021
|
|
21
|
+
- [ES2021(推荐用于现代浏览器)](#es2021推荐用于现代浏览器)
|
|
19
22
|
- [主要功能](#主要功能)
|
|
20
23
|
- [Windows下优先调用底层Windows hello 注册/认证(避免环境干扰);](#windows下优先调用底层windows-hello-注册认证避免环境干扰)
|
|
21
24
|
- [快速开始](#快速开始)
|
|
@@ -45,7 +48,8 @@
|
|
|
45
48
|
|
|
46
49
|
### 允许安装脚本执行
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了
|
|
52
|
+
`ignore-scripts=true`),可能会导致安装不完整或运行时异常;
|
|
49
53
|
|
|
50
54
|
推荐在项目根目录的 `package.json` 中添加 `allowScripts` 字段,显式放行本包及其依赖的脚本:
|
|
51
55
|
|
|
@@ -53,12 +57,13 @@
|
|
|
53
57
|
{
|
|
54
58
|
"allowScripts": {
|
|
55
59
|
"@flun/webauthn-browser": true
|
|
56
|
-
//
|
|
60
|
+
// 如果依赖的其它包(如 bcrypt、electron-winstaller 等)也有脚本,请按需添加,格式相同
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
```
|
|
60
64
|
|
|
61
|
-
> 如果你信任所有安装包,也可以直接在项目 `.npmrc` 中设置 `allow-scripts = false
|
|
65
|
+
> 如果你信任所有安装包,也可以直接在项目 `.npmrc` 中设置 `allow-scripts = false`(表示关闭脚本拦截,所有脚本均允许执行),
|
|
66
|
+
或删除`ignore-scripts` 字段;
|
|
62
67
|
|
|
63
68
|
---
|
|
64
69
|
|
|
@@ -66,13 +71,13 @@
|
|
|
66
71
|
|
|
67
72
|
### NPM 模块方式
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
适用于使用打包工具(Webpack、Vite、Rollup 等)或 Node.js ≥ 22.12.0 的环境;
|
|
70
75
|
|
|
71
76
|
```sh
|
|
72
77
|
npm i @flun/webauthn-browser
|
|
73
78
|
```
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
然后在代码中按需导入:
|
|
76
81
|
|
|
77
82
|
```js
|
|
78
83
|
// ES Module
|
|
@@ -86,12 +91,12 @@ const { startRegistration, startAuthentication } = require('@flun/webauthn-brows
|
|
|
86
91
|
|
|
87
92
|
本包通过 **unpkg** 提供 UMD 版本,引入后所有方法将挂载在全局对象 **`flunWebAuthnBrowser`** 上;
|
|
88
93
|
|
|
89
|
-
#### ES2021
|
|
94
|
+
#### ES2021(推荐用于现代浏览器)
|
|
90
95
|
|
|
91
96
|
```html
|
|
92
97
|
<script src="https://unpkg.com/@flun/webauthn-browser/dist/index.js"></script>
|
|
93
98
|
```
|
|
94
|
-
*** 注意 ES5
|
|
99
|
+
*** 注意 ES5(不再支持)***
|
|
95
100
|
|
|
96
101
|
---
|
|
97
102
|
## 主要功能
|
|
@@ -102,8 +107,8 @@ const { startRegistration, startAuthentication } = require('@flun/webauthn-brows
|
|
|
102
107
|
- ✅ **内置错误标准化**
|
|
103
108
|
将浏览器原生错误转换为包含 `code` 和 `message` 的 `WebAuthnError` 对象,便于业务层处理;
|
|
104
109
|
|
|
105
|
-
- ✅
|
|
106
|
-
支持 `useBrowserAutofill`
|
|
110
|
+
- ✅ **条件媒介支持(Conditional UI / 自动填充)**
|
|
111
|
+
支持 `useBrowserAutofill` 实现无感登录体验(需页面存在合适的 `<input>` 元素);
|
|
107
112
|
|
|
108
113
|
- ✅ **完整的环境检测**
|
|
109
114
|
提供 `browserSupportsWebAuthn()`、`platformAuthenticatorIsAvailable()` 等方法,精确判断设备能力;
|
|
@@ -194,7 +199,7 @@ async function login(username) {
|
|
|
194
199
|
console.log('注册结果:', result);
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
// ----------
|
|
202
|
+
// ---------- 登录(认证)示例 ----------
|
|
198
203
|
async function handleLogin(username) {
|
|
199
204
|
const optionsRes = await fetch('/api/login/begin', {
|
|
200
205
|
method: 'POST',
|
|
@@ -219,8 +224,8 @@ async function login(username) {
|
|
|
219
224
|
}
|
|
220
225
|
}
|
|
221
226
|
|
|
222
|
-
// ----------
|
|
223
|
-
//
|
|
227
|
+
// ---------- 带自动填充(Conditional UI)的登录示例 ----------
|
|
228
|
+
// 注意:使用自动填充时,页面必须包含类似如下的 input 元素:
|
|
224
229
|
// <input type="text" name="username" autocomplete="username webauthn" />
|
|
225
230
|
async function handleAutofillLogin() {
|
|
226
231
|
if (!(await flunWebAuthnBrowser.browserSupportsWebAuthnAutofill())) {
|
|
@@ -285,11 +290,11 @@ async function login(username) {
|
|
|
285
290
|
| 字段名 | 类型 | 默认值 | 描述 |
|
|
286
291
|
| ----------------- | --------- | ------- | -------------------------------------------------------------- |
|
|
287
292
|
| `optionsJSON` | `object` | 必填 | 由服务端调用 `generateRegistrationOptions()` 生成的 JSON 对象 |
|
|
288
|
-
| `useAutoRegister` | `boolean` | `false` |
|
|
293
|
+
| `useAutoRegister` | `boolean` | `false` | 是否启用条件媒介自动注册(实验性,需浏览器支持 Conditional UI) |
|
|
289
294
|
|
|
290
295
|
**返回值** `Promise<RegistrationResponseJSON>`
|
|
291
296
|
|
|
292
|
-
返回一个经过标准化处理的 JSON 对象,可直接发送给后端 `verifyRegistrationResponse()`
|
|
297
|
+
返回一个经过标准化处理的 JSON 对象,可直接发送给后端 `verifyRegistrationResponse()` 验证;响应格式如下:
|
|
293
298
|
|
|
294
299
|
```ts
|
|
295
300
|
interface RegistrationResponseJSON {
|
|
@@ -318,15 +323,22 @@ const response = await startRegistration({ optionsJSON });
|
|
|
318
323
|
|
|
319
324
|
#### startAuthentication(options)
|
|
320
325
|
|
|
321
|
-
发起 WebAuthn
|
|
326
|
+
发起 WebAuthn 认证(登录)流程;
|
|
322
327
|
|
|
323
328
|
**参数**
|
|
324
329
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
+
- `optionsJSON`
|
|
331
|
+
- 类型:`object`
|
|
332
|
+
- 默认值:必填
|
|
333
|
+
- 描述:由服务端调用 `generateAuthenticationOptions()` 生成的 JSON 对象
|
|
334
|
+
- `useBrowserAutofill`
|
|
335
|
+
- 类型:`boolean`
|
|
336
|
+
- 默认值:`false`
|
|
337
|
+
- 描述:是否启用条件媒介(自动填充),实现无感登录
|
|
338
|
+
- `verifyBrowserAutofillInput`
|
|
339
|
+
- 类型:`boolean`
|
|
340
|
+
- 默认值:`true`
|
|
341
|
+
- 描述:启用自动填充时是否检查页面是否存在 `autocomplete="... webauthn"` 的 input 元素
|
|
330
342
|
|
|
331
343
|
**返回值** `Promise<AuthenticationResponseJSON>`
|
|
332
344
|
|
|
@@ -352,7 +364,7 @@ interface AuthenticationResponseJSON {
|
|
|
352
364
|
// 普通登录
|
|
353
365
|
const response = await startAuthentication({ optionsJSON });
|
|
354
366
|
|
|
355
|
-
//
|
|
367
|
+
// 自动填充登录(需页面存在 <input autocomplete="webauthn">)
|
|
356
368
|
const response = await startAuthentication({
|
|
357
369
|
optionsJSON,
|
|
358
370
|
useBrowserAutofill: true
|
|
@@ -375,7 +387,7 @@ if (flunWebAuthnBrowser.browserSupportsWebAuthn()) {
|
|
|
375
387
|
|
|
376
388
|
#### platformAuthenticatorIsAvailable()
|
|
377
389
|
|
|
378
|
-
|
|
390
|
+
检查平台认证器(如 Touch ID、Face ID、Windows Hello)是否可用;
|
|
379
391
|
|
|
380
392
|
**返回值** `Promise<boolean>`
|
|
381
393
|
|
|
@@ -385,7 +397,7 @@ const available = await flunWebAuthnBrowser.platformAuthenticatorIsAvailable();
|
|
|
385
397
|
|
|
386
398
|
#### browserSupportsWebAuthnAutofill()
|
|
387
399
|
|
|
388
|
-
检查当前浏览器是否支持 WebAuthn
|
|
400
|
+
检查当前浏览器是否支持 WebAuthn 条件媒介(自动填充);
|
|
389
401
|
|
|
390
402
|
**返回值** `Promise<boolean>`
|
|
391
403
|
|
|
@@ -422,19 +434,19 @@ const { bufferToBase64URLString, base64URLStringToBuffer } = flunWebAuthnBrowser
|
|
|
422
434
|
|
|
423
435
|
| 属性 | 类型 | 描述 |
|
|
424
436
|
| ------- | -------- | ---------------------------- |
|
|
425
|
-
| `name` | `string` |
|
|
426
|
-
| `code` | `string` |
|
|
437
|
+
| `name` | `string` | 错误名称(通常为原始错误名) |
|
|
438
|
+
| `code` | `string` | 标准化的错误码(见下方表格) |
|
|
427
439
|
| `cause` | `Error` | 浏览器抛出的原始错误对象 |
|
|
428
440
|
|
|
429
441
|
**常见错误码**
|
|
430
442
|
|
|
431
443
|
| 错误码 | 描述 |
|
|
432
444
|
| ------------------------------------------------------------- | ------------------------------------------------ |
|
|
433
|
-
| `ERROR_CEREMONY_ABORTED` |
|
|
445
|
+
| `ERROR_CEREMONY_ABORTED` | 认证/注册流程被中止(如调用 `cancelCeremony()`) |
|
|
434
446
|
| `ERROR_INVALID_DOMAIN` | 当前域名无效 |
|
|
435
447
|
| `ERROR_INVALID_RP_ID` | RP ID 与当前域名不匹配 |
|
|
436
448
|
| `ERROR_AUTHENTICATOR_GENERAL_ERROR` | 认证器通用错误 |
|
|
437
|
-
| `ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED` |
|
|
449
|
+
| `ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED` | 认证器已被注册(注册时) |
|
|
438
450
|
| `ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT` | 需要可发现凭证但认证器不支持 |
|
|
439
451
|
| `ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY` | 透传错误,需查看 `cause` 属性 |
|
|
440
452
|
|
|
@@ -482,7 +494,7 @@ try {
|
|
|
482
494
|
console.log('用户取消了操作');
|
|
483
495
|
break;
|
|
484
496
|
case 'ERROR_INVALID_RP_ID':
|
|
485
|
-
alert('
|
|
497
|
+
alert('配置错误:RP ID 无效');
|
|
486
498
|
break;
|
|
487
499
|
default:
|
|
488
500
|
console.error('认证失败', err.message);
|
|
@@ -499,7 +511,8 @@ try {
|
|
|
499
511
|
|
|
500
512
|
### 配合 @flun/webauthn-server
|
|
501
513
|
|
|
502
|
-
|
|
514
|
+
以下是一个完整的最小化集成示例,前端使用本库,
|
|
515
|
+
后端使用 [`@flun/webauthn-server`](https://www.npmjs.com/package/@flun/webauthn-server);
|
|
503
516
|
|
|
504
517
|
**前端代码**
|
|
505
518
|
|
|
@@ -531,7 +544,7 @@ async function login(username) {
|
|
|
531
544
|
headers: { 'Content-Type': 'application/json' },
|
|
532
545
|
body: JSON.stringify({ username })
|
|
533
546
|
}), options = await optionsRes.json(),
|
|
534
|
-
// 2.
|
|
547
|
+
// 2. 启动浏览器认证流程(可开启自动填充)
|
|
535
548
|
response = await startAuthentication({ optionsJSON: options }),
|
|
536
549
|
// 3. 发送响应验证
|
|
537
550
|
verifyRes = await fetch('/api/login/complete', {
|
|
@@ -543,7 +556,7 @@ async function login(username) {
|
|
|
543
556
|
}
|
|
544
557
|
```
|
|
545
558
|
|
|
546
|
-
|
|
559
|
+
**后端代码片段**(基于 Express + `@flun/webauthn-server`)
|
|
547
560
|
|
|
548
561
|
```js
|
|
549
562
|
import {
|
|
@@ -10,7 +10,7 @@ const base64URLStringToBuffer = base64URLString => {
|
|
|
10
10
|
// 从 Base64URL 转换为标准 Base64
|
|
11
11
|
const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/'),
|
|
12
12
|
/**
|
|
13
|
-
* 用 '='
|
|
13
|
+
* 用 '=' 填充,直到长度是 4 的倍数
|
|
14
14
|
* (4 - (85 % 4 = 1) = 3) % 4 = 3 个填充
|
|
15
15
|
* (4 - (86 % 4 = 2) = 2) % 4 = 2 个填充
|
|
16
16
|
* (4 - (87 % 4 = 3) = 1) % 4 = 1 个填充
|
|
@@ -29,7 +29,7 @@ const base64URLStringToBuffer = base64URLString => {
|
|
|
29
29
|
* 与 `base64URLStringToBuffer` 搭配使用
|
|
30
30
|
* - 查看定义:@see {@link bufferToBase64URLString}
|
|
31
31
|
* @param {BufferSource} buffer - 要转换的 ArrayBuffer 或 ArrayBufferView
|
|
32
|
-
* @returns {string} Base64URL
|
|
32
|
+
* @returns {string} Base64URL 编码的字符串(不含填充符)
|
|
33
33
|
*/
|
|
34
34
|
const bufferToBase64URLString = buffer => {
|
|
35
35
|
const bytes = new Uint8Array(buffer);
|
package/helpers/common.js
CHANGED
|
@@ -11,10 +11,10 @@ const isWindows = (() => {
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* 从包含凭证 ID 的对象中提取 base64url 格式的 ID 字符串
|
|
14
|
-
* 支持多种 ID
|
|
14
|
+
* 支持多种 ID 表示形式(字符串、Uint8Array、Buffer 序列化对象等)
|
|
15
15
|
* > 查看定义:@see {@link extractBase64Id}
|
|
16
16
|
* @param {Object} obj - 包含 `id` 属性的对象
|
|
17
|
-
* @returns {string|undefined} base64url 编码的 ID
|
|
17
|
+
* @returns {string|undefined} base64url 编码的 ID 字符串,或 undefined
|
|
18
18
|
*/
|
|
19
19
|
const extractBase64Id = obj => {
|
|
20
20
|
if (!obj) return undefined;
|
|
@@ -33,8 +33,8 @@ const extractBase64Id = obj => {
|
|
|
33
33
|
* - 若传入 { challenge, ... } 则包装为 { optionsJSON: input }
|
|
34
34
|
* - 否则返回 null
|
|
35
35
|
* > 查看定义:@see {@link normalizeOptions}
|
|
36
|
-
* @param {Object} [input] - 用户传入的 options
|
|
37
|
-
* @returns {Object|null}
|
|
36
|
+
* @param {Object} [input] - 用户传入的 options,应包含 optionsJSON 或 challenge 属性
|
|
37
|
+
* @returns {Object|null} 规范化后的对象(至少包含 optionsJSON 属性)或 null
|
|
38
38
|
*/
|
|
39
39
|
const normalizeOptions = input => {
|
|
40
40
|
if (!input) return null;
|
|
@@ -8,7 +8,7 @@ import { WebAuthnError } from './webAuthnError.js';
|
|
|
8
8
|
* @param {Object} params - 参数对象
|
|
9
9
|
* @param {Error|DOMException} params.error - 原始错误对象
|
|
10
10
|
* @param {CredentialRequestOptions} params.options - navigator.credentials.get() 传入的选项
|
|
11
|
-
* @returns {Error|WebAuthnError}
|
|
11
|
+
* @returns {Error|WebAuthnError} 解析后的错误对象(WebAuthnError 或原始错误)
|
|
12
12
|
*/
|
|
13
13
|
const identifyAuthenticationError = ({ error, options }) => {
|
|
14
14
|
const { publicKey } = options;
|
|
@@ -21,7 +21,7 @@ const identifyAuthenticationError = ({ error, options }) => {
|
|
|
21
21
|
}
|
|
22
22
|
else if (error.name === 'NotAllowedError')
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* 直接传递错误,平台对该错误的定义超出了规范范围,我们不想覆盖可能更有用的错误消息;
|
|
25
25
|
*/
|
|
26
26
|
return new WebAuthnError({ message: error.message, code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY', cause: error });
|
|
27
27
|
else if (error.name === 'SecurityError') {
|
|
@@ -34,14 +34,15 @@ const identifyAuthenticationError = ({ error, options }) => {
|
|
|
34
34
|
else if (publicKey.rpId !== effectiveDomain)
|
|
35
35
|
// https://www.w3.org/TR/webauthn-2/#sctn-discover-from-external-source (第 6 步)
|
|
36
36
|
return new WebAuthnError({
|
|
37
|
-
message: `信赖方 ID
|
|
37
|
+
message: `信赖方 ID"${publicKey.rpId}"对此域名无效`, code: 'ERROR_INVALID_RP_ID', cause: error
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
else if (error.name === 'UnknownError')
|
|
41
41
|
// https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (第 1 步)
|
|
42
42
|
// https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (第 12 步)
|
|
43
43
|
return new WebAuthnError({
|
|
44
|
-
message: '身份验证器无法处理指定的选项或创建新的断言签名',
|
|
44
|
+
message: '身份验证器无法处理指定的选项或创建新的断言签名',
|
|
45
|
+
code: 'ERROR_AUTHENTICATOR_GENERAL_ERROR', cause: error
|
|
45
46
|
});
|
|
46
47
|
|
|
47
48
|
return error;
|
|
@@ -8,7 +8,7 @@ import { WebAuthnError } from './webAuthnError.js';
|
|
|
8
8
|
* @param {Object} params - 参数对象
|
|
9
9
|
* @param {Error} params.error - 原始错误对象
|
|
10
10
|
* @param {CredentialCreationOptions} params.options - 传递给 `navigator.credentials.create()` 的选项
|
|
11
|
-
* @returns {WebAuthnError|Error} 如果错误能被识别则返回包含更详细信息的 WebAuthnError
|
|
11
|
+
* @returns {WebAuthnError|Error} 如果错误能被识别则返回包含更详细信息的 WebAuthnError,否则返回原始错误
|
|
12
12
|
*/
|
|
13
13
|
const identifyRegistrationError = ({ error, options }) => {
|
|
14
14
|
const { publicKey } = options;
|
|
@@ -25,20 +25,21 @@ const identifyRegistrationError = ({ error, options }) => {
|
|
|
25
25
|
if (publicKey.authenticatorSelection?.requireResidentKey === true)
|
|
26
26
|
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (第 4 步)
|
|
27
27
|
return new WebAuthnError({
|
|
28
|
-
message: '
|
|
28
|
+
message: '需要可发现凭证,但没有可用的身份验证器支持该功能',
|
|
29
29
|
code: 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT', cause: error,
|
|
30
30
|
});
|
|
31
|
-
// `mediation` 在 CredentialCreationOptions
|
|
31
|
+
// `mediation` 在 CredentialCreationOptions 中尚不存在,但截至 2024 年 9 月已有可能
|
|
32
32
|
else if (options.mediation === 'conditional' && publicKey.authenticatorSelection?.userVerification === 'required')
|
|
33
33
|
// https://w3c.github.io/webauthn/#sctn-createCredential (第 22.4 步)
|
|
34
34
|
return new WebAuthnError({
|
|
35
|
-
message: '
|
|
35
|
+
message: '自动注册期间要求用户验证,但无法执行',
|
|
36
|
+
code: 'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE', cause: error
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
else if (publicKey.authenticatorSelection?.userVerification === 'required')
|
|
39
40
|
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (第 5 步)
|
|
40
41
|
return new WebAuthnError({
|
|
41
|
-
message: '
|
|
42
|
+
message: '需要用户验证,但没有可用的身份验证器支持该功能',
|
|
42
43
|
code: 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT', cause: error
|
|
43
44
|
});
|
|
44
45
|
}
|
|
@@ -50,7 +51,7 @@ const identifyRegistrationError = ({ error, options }) => {
|
|
|
50
51
|
});
|
|
51
52
|
else if (error.name === 'NotAllowedError')
|
|
52
53
|
/**
|
|
53
|
-
*
|
|
54
|
+
* 直接传递错误;平台对该错误的定义超出了规范范围,保留原始错误消息;
|
|
54
55
|
*/
|
|
55
56
|
return new WebAuthnError({ message: error.message, code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY', cause: error });
|
|
56
57
|
else if (error.name === 'NotSupportedError') {
|
|
@@ -58,7 +59,8 @@ const identifyRegistrationError = ({ error, options }) => {
|
|
|
58
59
|
if (validPubKeyCredParams.length === 0)
|
|
59
60
|
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (第 10 步)
|
|
60
61
|
return new WebAuthnError({
|
|
61
|
-
message: 'pubKeyCredParams 中没有类型为 "public-key" 的条目',
|
|
62
|
+
message: 'pubKeyCredParams 中没有类型为 "public-key" 的条目',
|
|
63
|
+
code: 'ERROR_MALFORMED_PUBKEYCREDPARAMS', cause: error
|
|
62
64
|
});
|
|
63
65
|
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (第 2 步)
|
|
64
66
|
return new WebAuthnError({
|
|
@@ -76,7 +78,7 @@ const identifyRegistrationError = ({ error, options }) => {
|
|
|
76
78
|
else if (publicKey.rp.id !== effectiveDomain)
|
|
77
79
|
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (第 8 步)
|
|
78
80
|
return new WebAuthnError({
|
|
79
|
-
message: `信赖方 ID
|
|
81
|
+
message: `信赖方 ID"${publicKey.rp.id}"对此域名无效`, code: 'ERROR_INVALID_RP_ID', cause: error
|
|
80
82
|
});
|
|
81
83
|
}
|
|
82
84
|
else if (error.name === 'TypeError') {
|
|
@@ -90,7 +92,8 @@ const identifyRegistrationError = ({ error, options }) => {
|
|
|
90
92
|
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (第 1 步)
|
|
91
93
|
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (第 8 步)
|
|
92
94
|
return new WebAuthnError({
|
|
93
|
-
message: '
|
|
95
|
+
message: '身份验证器无法处理指定的选项,或无法创建新的凭证',
|
|
96
|
+
code: 'ERROR_AUTHENTICATOR_GENERAL_ERROR', cause: error
|
|
94
97
|
});
|
|
95
98
|
|
|
96
99
|
return error;
|
package/helpers/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { base64URLStringToBuffer, bufferToBase64URLString } from './base64urlAndBuffer.js';
|
|
2
2
|
import { _browserSupportsWebAuthnInternals, browserSupportsWebAuthn } from './browserSupportsWebAuthn.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
_browserSupportsWebAuthnAutofillInternals, browserSupportsWebAuthnAutofill
|
|
5
|
+
} from './browserSupportsWebAuthnAutofill.js';
|
|
4
6
|
import { isWindows, extractBase64Id, normalizeOptions } from './common.js';
|
|
5
7
|
import { identifyAuthenticationError } from './identifyAuthenticationError.js';
|
|
6
8
|
import { identifyRegistrationError } from './identifyRegistrationError.js';
|
|
@@ -28,7 +30,7 @@ declare module './base64urlAndBuffer.js' {
|
|
|
28
30
|
/**
|
|
29
31
|
* ```js
|
|
30
32
|
* // 文件导出内容
|
|
31
|
-
* const _browserSupportsWebAuthnInternals={}; //
|
|
33
|
+
* const _browserSupportsWebAuthnInternals={}; // 测试期间对返回值进行桩(stub)处理
|
|
32
34
|
* browserSupportsWebAuthn(); // 判断当前浏览器是否支持 WebAuthn
|
|
33
35
|
* ```
|
|
34
36
|
* >查看定义:@see {@link _browserSupportsWebAuthnInternals}、{@link browserSupportsWebAuthn}
|
|
@@ -140,7 +142,8 @@ declare module './toPublicKeyCredentialDescriptor.js' {
|
|
|
140
142
|
/**
|
|
141
143
|
* ```js
|
|
142
144
|
* // 文件导出内容
|
|
143
|
-
* const WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
145
|
+
* const WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
146
|
+
* // 服务单例,用于确保同一时间只有一个 WebAuthn 仪式处于活动状态;
|
|
144
147
|
* ```
|
|
145
148
|
* >查看定义:@see {@link WebAuthnAbortService}
|
|
146
149
|
*/
|
|
@@ -169,16 +172,17 @@ declare module './webAuthnError.js' {
|
|
|
169
172
|
|
|
170
173
|
// ================================ 导出入口 ================================
|
|
171
174
|
/**
|
|
172
|
-
*
|
|
175
|
+
* 模块导出内容:
|
|
173
176
|
* ```js
|
|
174
177
|
* // 类型
|
|
175
178
|
* type WebAuthnErrorCode;
|
|
176
179
|
*
|
|
177
180
|
* // 常量
|
|
178
|
-
* const _browserSupportsWebAuthnInternals={}; //
|
|
181
|
+
* const _browserSupportsWebAuthnInternals={}; // 测试期间对返回值进行桩(stub)处理
|
|
179
182
|
* const _browserSupportsWebAuthnAutofillInternals={}; // 测试期间模拟返回值;
|
|
180
183
|
* const isWindows=(); // 当前操作系统是否为 Windows
|
|
181
|
-
* const WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
184
|
+
* const WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
185
|
+
* // 服务单例,用于确保同一时间只有一个 WebAuthn 仪式处于活动状态;
|
|
182
186
|
*
|
|
183
187
|
* // 函数
|
|
184
188
|
* base64URLStringToBuffer(); // 将 Base64URL 编码的字符串转换为 Array Buffer;
|
|
@@ -186,14 +190,16 @@ declare module './webAuthnError.js' {
|
|
|
186
190
|
* browserSupportsWebAuthn(); // 判断当前浏览器是否支持 WebAuthn
|
|
187
191
|
* browserSupportsWebAuthnAutofill(); // 判断浏览器是否支持条件式UI;
|
|
188
192
|
* isWindows(); // 检测当前操作系统是否为 Windows
|
|
189
|
-
* extractBase64Id();
|
|
193
|
+
* extractBase64Id();
|
|
194
|
+
* // 从包含凭证 ID 的对象中提取 base64url 格式的 ID 字符串 支持多种 ID 表示形式
|
|
190
195
|
* normalizeOptions(); // 规范化传入的 options 对象,兼容旧调用方式
|
|
191
196
|
* identifyAuthenticationError(); // 尝试推断调用 `navigator.credentials.get()` 后引发错误的原因;
|
|
192
197
|
* identifyRegistrationError(); // 尝试推断调用 `navigator.credentials.create()` 后引发错误的原因;
|
|
193
198
|
* isValidDomain(); // 判断主机名是否符合验证规范;
|
|
194
199
|
* platformAuthenticatorIsAvailable(); // 判断浏览器是否能够与内置身份验证器通信;
|
|
195
200
|
* toAuthenticatorAttachment(); // 尝试将 `string` 值强制转换为已知的 `AuthenticatorAttachment` 类型
|
|
196
|
-
* toPublicKeyCredentialDescriptor();
|
|
201
|
+
* toPublicKeyCredentialDescriptor();
|
|
202
|
+
* // 将描述符中的Base64URL字符串`id`转换为ArrayBuffer,以适配 WebAuthn API;
|
|
197
203
|
*
|
|
198
204
|
* // 类
|
|
199
205
|
* class WebAuthnError{}; // 自定义错误,用于更细致地说明规范中八种错误被抛出的原因;
|
package/helpers/isValidDomain.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 一个简单的测试,用于判断主机名是否为格式正确的域名
|
|
3
3
|
* - 查看定义:@see {@link isValidDomain}
|
|
4
|
-
* -
|
|
4
|
+
* - "有效域名"的定义参见:https://url.spec.whatwg.org/#valid-domain
|
|
5
5
|
*
|
|
6
|
-
* - 正则表达式最初来源于此处,后经改编以增加 punycode
|
|
6
|
+
* - 正则表达式最初来源于此处,后经改编以增加 punycode 支持:
|
|
7
7
|
* https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch08s15.html
|
|
8
8
|
*
|
|
9
9
|
* @param {string} hostname - 待验证的主机名
|
|
10
|
-
* @returns {boolean} 如果主机名是 localhost 或符合有效域名格式则返回 true
|
|
10
|
+
* @returns {boolean} 如果主机名是 localhost 或符合有效域名格式则返回 true,否则 false
|
|
11
11
|
*/
|
|
12
12
|
const isValidDomain = hostname => {
|
|
13
|
+
const domainRe = /^((xn--[a-z0-9-]+|[a-z0-9]+(-[a-z0-9]+)*)\.)+([a-z]{2,}|xn--[a-z0-9-]+)$/i;
|
|
13
14
|
return (
|
|
14
15
|
// 将 localhost 视为有效,因为它在安全上下文方面是没问题的,支持 punycode (ACE) 或 ASCII 标签和域名
|
|
15
|
-
hostname === 'localhost' ||
|
|
16
|
+
hostname === 'localhost' || domainRe.test(hostname));
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export { isValidDomain };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { browserSupportsWebAuthn } from './browserSupportsWebAuthn.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* 判断浏览器是否能够与内置身份验证器(如 Touch ID、Android 指纹扫描器或 Windows Hello)通信;
|
|
5
5
|
* 该方法无法告知平台身份验证器的具体名称;
|
|
6
6
|
* - 查看定义:@see {@link platformAuthenticatorIsAvailable}
|
|
7
7
|
* @returns {Promise<boolean>} 如果平台身份验证器可用,则解析为 true,否则为 false;
|
|
@@ -4,8 +4,8 @@ import { base64URLStringToBuffer } from './base64urlAndBuffer.js';
|
|
|
4
4
|
* 将描述符中的 Base64URL 字符串 `id` 转换为 ArrayBuffer,以适配 WebAuthn API;
|
|
5
5
|
* - 查看定义:@see {@link toPublicKeyCredentialDescriptor}
|
|
6
6
|
* @param {Object} descriptor - 包含 `id` (Base64URL 字符串) 的凭证描述符;
|
|
7
|
-
* @param {string} descriptor.id - 凭证 ID
|
|
8
|
-
* @param {string[]} [descriptor.transports] -
|
|
7
|
+
* @param {string} descriptor.id - 凭证 ID(Base64URL 编码)
|
|
8
|
+
* @param {string[]} [descriptor.transports] - 可选的传输方式列表(如 ['usb', 'nfc'])
|
|
9
9
|
* @returns {PublicKeyCredentialDescriptor} 转换后的描述符,可直接用于 WebAuthn;
|
|
10
10
|
*/
|
|
11
11
|
const toPublicKeyCredentialDescriptor = descriptor => {
|
package/helpers/webAuthnError.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 自定义错误,用于在调用 `navigator.credentials.create()` 或 `navigator.credentials.get()` 后,
|
|
3
|
-
*
|
|
3
|
+
* 更细致地说明规范中描述的八种错误之一被抛出的原因:
|
|
4
4
|
*
|
|
5
5
|
* - `AbortError`
|
|
6
6
|
* - `ConstraintError`
|
|
@@ -18,7 +18,7 @@ class WebAuthnError extends Error {
|
|
|
18
18
|
/**
|
|
19
19
|
* @param {Object} params - 错误配置参数
|
|
20
20
|
* @param {string} params.message - 错误消息
|
|
21
|
-
* @param {string} params.code -
|
|
21
|
+
* @param {string} params.code - 错误码(规范中定义的错误名称之一)
|
|
22
22
|
* @param {Error} [params.cause] - 导致此错误的原始错误对象
|
|
23
23
|
* @param {string} [params.name] - 自定义错误名称,默认使用 cause 的 name
|
|
24
24
|
*/
|
package/index.d.ts
CHANGED
|
@@ -6,16 +6,17 @@ import {
|
|
|
6
6
|
import { startAuthentication, startRegistration } from './methods/index.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* 模块导出内容:
|
|
10
10
|
* ```js
|
|
11
11
|
* // 登录和注册处理函数
|
|
12
|
-
* startAuthentication(); // 通过 WebAuthn
|
|
13
|
-
* startRegistration(); // 通过 WebAuthn
|
|
12
|
+
* startAuthentication(); // 通过 WebAuthn 断言开始身份验证器"登录"
|
|
13
|
+
* startRegistration(); // 通过 WebAuthn 证明开始认证器"注册"
|
|
14
14
|
*
|
|
15
15
|
* // 工具
|
|
16
|
-
* const _browserSupportsWebAuthnInternals={}; //
|
|
16
|
+
* const _browserSupportsWebAuthnInternals={}; // 测试期间对返回值进行桩(stub)处理
|
|
17
17
|
* const _browserSupportsWebAuthnAutofillInternals={}; // 测试期间模拟返回值;
|
|
18
|
-
* const WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
18
|
+
* const WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
19
|
+
* // 服务单例,用于确保同一时间只有一个 WebAuthn 仪式处于活动状态;
|
|
19
20
|
*
|
|
20
21
|
* base64URLStringToBuffer(); // 将 Base64URL 编码的字符串转换为 Array Buffer;
|
|
21
22
|
* bufferToBase64URLString(); // 将给定的数组缓冲区转换为 Base64URL 编码的字符串;
|
|
@@ -27,7 +28,8 @@ import { startAuthentication, startRegistration } from './methods/index.js';
|
|
|
27
28
|
* ```
|
|
28
29
|
* >查看定义:@see
|
|
29
30
|
*- 登录和注册处理: {@link startAuthentication}、{@link startRegistration}
|
|
30
|
-
*- 工具: {@link _browserSupportsWebAuthnInternals}、{@link _browserSupportsWebAuthnAutofillInternals}、
|
|
31
|
+
*- 工具: {@link _browserSupportsWebAuthnInternals}、{@link _browserSupportsWebAuthnAutofillInternals}、
|
|
32
|
+
* {@link WebAuthnAbortService}、
|
|
31
33
|
* {@link base64URLStringToBuffer}、{@link bufferToBase64URLString}、 {@link browserSupportsWebAuthn}、
|
|
32
34
|
* {@link browserSupportsWebAuthnAutofill}、{@link platformAuthenticatorIsAvailable}
|
|
33
35
|
* >{@link WebAuthnError}
|
package/methods/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { startRegistration } from './startRegistration.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* ```js
|
|
7
7
|
* // 文件导出内容
|
|
8
|
-
* startAuthentication(); // 通过 WebAuthn
|
|
8
|
+
* startAuthentication(); // 通过 WebAuthn 断言开始身份验证器"登录"
|
|
9
9
|
* ```
|
|
10
10
|
* >查看定义:@see {@link startAuthentication}
|
|
11
11
|
*/
|
|
@@ -17,7 +17,7 @@ declare module './startAuthentication.js' {
|
|
|
17
17
|
/**
|
|
18
18
|
* ```js
|
|
19
19
|
* // 文件导出内容
|
|
20
|
-
* startRegistration(); // 通过 WebAuthn
|
|
20
|
+
* startRegistration(); // 通过 WebAuthn 证明开始认证器"注册"
|
|
21
21
|
* ```
|
|
22
22
|
* >查看定义:@see {@link startRegistration}
|
|
23
23
|
*/
|
|
@@ -27,10 +27,10 @@ declare module './startRegistration.js' {
|
|
|
27
27
|
|
|
28
28
|
// ================================ 导出入口 ================================
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* 模块导出内容:
|
|
31
31
|
* ```js
|
|
32
|
-
* startAuthentication(); // 通过 WebAuthn
|
|
33
|
-
* startRegistration(); // 通过 WebAuthn
|
|
32
|
+
* startAuthentication(); // 通过 WebAuthn 断言开始身份验证器"登录"
|
|
33
|
+
* startRegistration(); // 通过 WebAuthn 证明开始认证器"注册"
|
|
34
34
|
* ```
|
|
35
35
|
* >查看定义:@see {@link startAuthentication}、{@link startRegistration}
|
|
36
36
|
*/
|
|
@@ -5,13 +5,15 @@ import {
|
|
|
5
5
|
} from '../helpers/index.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* 通过 WebAuthn
|
|
8
|
+
* 通过 WebAuthn 断言开始身份验证器"登录"
|
|
9
9
|
* > 查看定义:@see {@link startAuthentication}
|
|
10
10
|
* @param {Object} options - 配置选项
|
|
11
11
|
* @param {Object} options.optionsJSON - 来自 **@flun/webauthn-server** 的 `generateAuthenticationOptions()` 的输出
|
|
12
12
|
* @param {boolean} [options.useBrowserAutofill=false] - 初始化条件式 UI,以支持通过浏览器自动填充提示进行登录
|
|
13
|
-
* @param {boolean} [options.verifyBrowserAutofillInput=true] - 当 `useBrowserAutofill` 为 `true`
|
|
14
|
-
*
|
|
13
|
+
* @param {boolean} [options.verifyBrowserAutofillInput=true] - 当 `useBrowserAutofill` 为 `true` 时,
|
|
14
|
+
* 确保存在合适的 `<input>` 元素
|
|
15
|
+
* @param {string} [options.accountId] - (可选)手动指定账号 ID,用于 Windows 原生认证时作为凭证标识,
|
|
16
|
+
* 若未提供则从 `optionsJSON.allowCredentials` 中提取
|
|
15
17
|
* @returns {Promise<{
|
|
16
18
|
* id: string,
|
|
17
19
|
* rawId: string,
|
|
@@ -61,7 +63,7 @@ const startAuthentication = async (options) => {
|
|
|
61
63
|
|
|
62
64
|
const eligibleInputs = document.querySelectorAll("input[autocomplete$='webauthn']");
|
|
63
65
|
if (eligibleInputs.length < 1 && verifyBrowserAutofillInput)
|
|
64
|
-
throw Error('未检测到任何 `autocomplete` 属性中包含 "webauthn"
|
|
66
|
+
throw Error('未检测到任何 `autocomplete` 属性中包含 "webauthn"(作为唯一值或最后一个值)的 <input> 元素');
|
|
65
67
|
|
|
66
68
|
getOptions.mediation = 'conditional', publicKey.allowCredentials = [];
|
|
67
69
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
bufferToBase64URLString, base64URLStringToBuffer, browserSupportsWebAuthn, isWindows, extractBase64Id,
|
|
2
|
+
bufferToBase64URLString, base64URLStringToBuffer, browserSupportsWebAuthn, isWindows, extractBase64Id,
|
|
3
|
+
normalizeOptions,
|
|
3
4
|
identifyRegistrationError, toAuthenticatorAttachment, toPublicKeyCredentialDescriptor, WebAuthnAbortService
|
|
4
5
|
} from '../helpers/index.js';
|
|
5
6
|
|
|
@@ -15,7 +16,7 @@ const warnOnBrokenImplementation = (methodName, cause) => {
|
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* 通过 WebAuthn
|
|
19
|
+
* 通过 WebAuthn 证明开始认证器"注册"
|
|
19
20
|
* - 查看定义:@see {@link startRegistration}
|
|
20
21
|
*
|
|
21
22
|
* @param {Object} options - 配置选项
|
|
@@ -74,9 +75,9 @@ const startRegistration = async (options) => {
|
|
|
74
75
|
}, createOptions = {};
|
|
75
76
|
|
|
76
77
|
/**
|
|
77
|
-
*
|
|
78
|
+
* 尝试使用条件创建(conditional create)为用户注册一个通行密钥,
|
|
78
79
|
* 使用用户刚刚用于认证的密码管理器;浏览器不会向用户显示任何突出的 UI;
|
|
79
|
-
*
|
|
80
|
+
* 注意:`mediation` 在 CredentialCreationOptions 中尚不存在,但自 2024 年 9 月起已可用
|
|
80
81
|
*/
|
|
81
82
|
if (useAutoRegister) createOptions.mediation = 'conditional';
|
|
82
83
|
createOptions.publicKey = publicKey, createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flun/webauthn-browser",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.10",
|
|
4
4
|
"description": "WebAuthn 前端库,提供简单易用的接口来处理 WebAuthn 认证流程,支持 Passkeys 和 FIDO2 标准;",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"type": "git",
|
|
37
37
|
"url": "git+https://github.com/OpenFlun/webauthn-browser.git"
|
|
38
38
|
},
|
|
39
|
-
"homepage": "https://
|
|
39
|
+
"homepage": "https://github.com/OpenFlun/webauthn-browser#readme",
|
|
40
40
|
"bugs": {
|
|
41
41
|
"url": "https://github.com/OpenFlun/webauthn-browser/issues"
|
|
42
42
|
},
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
"npm": ">=10.0.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@types/node": "^26.6.
|
|
50
|
+
"@types/node": "^26.6.5"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/types/dom.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 请勿修改这些文件!
|
|
3
3
|
*
|
|
4
|
-
* 这些文件是从 **types**
|
|
5
|
-
* 然后从 monorepo
|
|
4
|
+
* 这些文件是从 **types** 包复制而来的;要更新此文件,请修改相应包中的文件,
|
|
5
|
+
* 然后从 monorepo 根目录运行命令: deno task codegen:types
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
8
|
* 由 typescript@5.6.3 生成
|
|
9
|
-
*
|
|
9
|
+
* 要重新生成,请从包根目录运行命令: deno task extract-dom-types
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
13
13
|
*
|
|
14
14
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/AuthenticatorAssertionResponse)
|
|
15
15
|
*/
|
|
@@ -22,7 +22,7 @@ export interface AuthenticatorAssertionResponse extends AuthenticatorResponse {
|
|
|
22
22
|
readonly userHandle: ArrayBuffer | null;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
26
26
|
*
|
|
27
27
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/AuthenticatorAttestationResponse)
|
|
28
28
|
*/
|
|
@@ -50,13 +50,13 @@ export interface AuthenticatorSelectionCriteria {
|
|
|
50
50
|
userVerification?: UserVerificationRequirement;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* 当前上下文中可用的基本加密功能;它允许访问密码学强随机数生成器和加密原语;
|
|
54
54
|
*
|
|
55
55
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/Crypto)
|
|
56
56
|
*/
|
|
57
57
|
export interface Crypto {
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
60
60
|
*
|
|
61
61
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/Crypto/subtle)
|
|
62
62
|
*/
|
|
@@ -64,14 +64,14 @@ export interface Crypto {
|
|
|
64
64
|
/** [MDN 参考](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */
|
|
65
65
|
getRandomValues<T extends ArrayBufferView | null>(array: T): T;
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
67
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
68
68
|
*
|
|
69
69
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID)
|
|
70
70
|
*/
|
|
71
71
|
randomUUID(): `${string}-${string}-${string}-${string}-${string}`;
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
74
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
75
75
|
*
|
|
76
76
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential)
|
|
77
77
|
*/
|
|
@@ -111,7 +111,7 @@ export interface PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity
|
|
|
111
111
|
displayName: string, id: BufferSource;
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
115
115
|
*
|
|
116
116
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/AuthenticatorResponse)
|
|
117
117
|
*/
|
|
@@ -123,8 +123,8 @@ export interface CredentialPropertiesOutput {
|
|
|
123
123
|
rk?: boolean;
|
|
124
124
|
}
|
|
125
125
|
/**
|
|
126
|
-
* 此 Web Crypto API 接口提供了一系列低级加密函数;可通过窗口上下文中的 Crypto.subtle
|
|
127
|
-
*
|
|
126
|
+
* 此 Web Crypto API 接口提供了一系列低级加密函数;可通过窗口上下文中的 Crypto.subtle 属性访问(通过 Window.crypto);
|
|
127
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
128
128
|
*
|
|
129
129
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/SubtleCrypto)
|
|
130
130
|
*/
|
|
@@ -180,7 +180,7 @@ export interface SubtleCrypto {
|
|
|
180
180
|
RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams): Promise<ArrayBuffer>;
|
|
181
181
|
}
|
|
182
182
|
/**
|
|
183
|
-
*
|
|
183
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
184
184
|
*
|
|
185
185
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/Credential)
|
|
186
186
|
*/
|
|
@@ -209,8 +209,8 @@ export interface AesGcmParams extends Algorithm {
|
|
|
209
209
|
additionalData?: BufferSource, iv: BufferSource, tagLength?: number;
|
|
210
210
|
}
|
|
211
211
|
/**
|
|
212
|
-
* Web Crypto API 的 CryptoKey
|
|
213
|
-
*
|
|
212
|
+
* Web Crypto API 的 CryptoKey 字典表示一个加密密钥;
|
|
213
|
+
* 仅在安全上下文(secure contexts)中可用;
|
|
214
214
|
*
|
|
215
215
|
* [MDN 参考](https://developer.mozilla.org/docs/Web/API/CryptoKey)
|
|
216
216
|
*/
|
package/types/index.d.ts
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 请勿修改这些文件!
|
|
3
3
|
*
|
|
4
|
-
* 这些文件是从 **types**
|
|
5
|
-
* 然后从 monorepo
|
|
4
|
+
* 这些文件是从 **types** 包复制而来的;要更新此文件,请修改相应包中的文件,
|
|
5
|
+
* 然后从 monorepo 根目录运行命令: deno task codegen:types
|
|
6
6
|
*/
|
|
7
7
|
import type {
|
|
8
8
|
AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs,
|
|
9
|
-
AuthenticatorAssertionResponse, AuthenticatorAttachment, AuthenticatorAttestationResponse,
|
|
9
|
+
AuthenticatorAssertionResponse, AuthenticatorAttachment, AuthenticatorAttestationResponse,
|
|
10
|
+
AuthenticatorSelectionCriteria,
|
|
10
11
|
COSEAlgorithmIdentifier, PublicKeyCredential, PublicKeyCredentialCreationOptions, PublicKeyCredentialDescriptor,
|
|
11
12
|
PublicKeyCredentialParameters, PublicKeyCredentialRequestOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
|
|
12
13
|
UserVerificationRequirement
|
|
13
14
|
} from './dom.js';
|
|
14
15
|
export type {
|
|
15
16
|
AttestationConveyancePreference, AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs,
|
|
16
|
-
AuthenticatorAssertionResponse, AuthenticatorAttachment, AuthenticatorAttestationResponse,
|
|
17
|
+
AuthenticatorAssertionResponse, AuthenticatorAttachment, AuthenticatorAttestationResponse,
|
|
18
|
+
AuthenticatorSelectionCriteria,
|
|
17
19
|
AuthenticatorTransport, COSEAlgorithmIdentifier, Crypto, PublicKeyCredential, PublicKeyCredentialCreationOptions,
|
|
18
|
-
PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptions,
|
|
20
|
+
PublicKeyCredentialDescriptor, PublicKeyCredentialParameters, PublicKeyCredentialRequestOptions,
|
|
21
|
+
PublicKeyCredentialRpEntity,
|
|
19
22
|
PublicKeyCredentialType, PublicKeyCredentialUserEntity, ResidentKeyRequirement, UserVerificationRequirement,
|
|
20
23
|
} from './dom.js';
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
|
-
* 适用于 JSON 传输到浏览器的 PublicKeyCredentialCreationOptions
|
|
24
|
-
*
|
|
26
|
+
* 适用于 JSON 传输到浏览器的 PublicKeyCredentialCreationOptions 变体,
|
|
27
|
+
* (最终)将传递给浏览器中的 navigator.credentials.create(...);
|
|
25
28
|
*
|
|
26
|
-
* 当 WebAuthn L3 类型最终进入语言标准时,应被官方的 TypeScript DOM
|
|
29
|
+
* 当 WebAuthn L3 类型最终进入语言标准时,应被官方的 TypeScript DOM 类型取代:
|
|
27
30
|
*
|
|
28
31
|
* https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson
|
|
29
32
|
*/
|
|
@@ -42,8 +45,8 @@ export interface PublicKeyCredentialCreationOptionsJSON {
|
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/**
|
|
45
|
-
* 适用于 JSON 传输到浏览器的 PublicKeyCredentialRequestOptions
|
|
46
|
-
*
|
|
48
|
+
* 适用于 JSON 传输到浏览器的 PublicKeyCredentialRequestOptions 变体,
|
|
49
|
+
* (最终)将传递给浏览器中的 navigator.credentials.get(...);
|
|
47
50
|
*/
|
|
48
51
|
export interface PublicKeyCredentialRequestOptionsJSON {
|
|
49
52
|
challenge: Base64URLString;
|
|
@@ -78,7 +81,7 @@ export interface RegistrationCredential extends PublicKeyCredentialFuture {
|
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
/**
|
|
81
|
-
* 略微修改的 RegistrationCredential,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer
|
|
84
|
+
* 略微修改的 RegistrationCredential,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer,
|
|
82
85
|
* 以便它们可以作为 JSON 发送到服务器;
|
|
83
86
|
*
|
|
84
87
|
* https://w3c.github.io/webauthn/#dictdef-registrationresponsejson
|
|
@@ -100,8 +103,8 @@ export interface AuthenticationCredential extends PublicKeyCredentialFuture {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
/**
|
|
103
|
-
* 略微修改的 AuthenticationCredential,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer
|
|
104
|
-
* 以便它们可以作为 JSON
|
|
106
|
+
* 略微修改的 AuthenticationCredential,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer,
|
|
107
|
+
* 以便它们可以作为 JSON 发送到服务器;
|
|
105
108
|
*
|
|
106
109
|
* https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson
|
|
107
110
|
*/
|
|
@@ -115,8 +118,8 @@ export interface AuthenticationResponseJSON {
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
/**
|
|
118
|
-
* 略微修改的 AuthenticatorAttestationResponse,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer
|
|
119
|
-
* 以便它们可以作为 JSON
|
|
121
|
+
* 略微修改的 AuthenticatorAttestationResponse,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer,
|
|
122
|
+
* 以便它们可以作为 JSON 发送到服务器;
|
|
120
123
|
*
|
|
121
124
|
* https://w3c.github.io/webauthn/#dictdef-authenticatorattestationresponsejson
|
|
122
125
|
*/
|
|
@@ -130,8 +133,8 @@ export interface AuthenticatorAttestationResponseJSON {
|
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
/**
|
|
133
|
-
* 略微修改的 AuthenticatorAssertionResponse,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer
|
|
134
|
-
* 以便它们可以作为 JSON
|
|
136
|
+
* 略微修改的 AuthenticatorAssertionResponse,用于简化处理浏览器中经过 Base64URL 编码的 ArrayBuffer,
|
|
137
|
+
* 以便它们可以作为 JSON 发送到服务器;
|
|
135
138
|
*
|
|
136
139
|
* https://w3c.github.io/webauthn/#dictdef-authenticatorassertionresponsejson
|
|
137
140
|
*/
|
|
@@ -156,7 +159,7 @@ export type WebAuthnCredential = {
|
|
|
156
159
|
export type Base64URLString = string;
|
|
157
160
|
|
|
158
161
|
/**
|
|
159
|
-
* TypeScript DOM 库中的 AuthenticatorAttestationResponse
|
|
162
|
+
* TypeScript DOM 库中的 AuthenticatorAttestationResponse 已过时(最高到 v3.9.7);
|
|
160
163
|
* 此处维护一个增强版本,以便随着 WebAuthn 规范的演进实现额外的属性;
|
|
161
164
|
*
|
|
162
165
|
* 参见 https://www.w3.org/TR/webauthn-2/#iface-authenticatorattestationresponse
|
|
@@ -169,13 +172,13 @@ export interface AuthenticatorAttestationResponseFuture extends AuthenticatorAtt
|
|
|
169
172
|
|
|
170
173
|
/**
|
|
171
174
|
* TypeScript 的 `AuthenticatorTransport` 的超集,包含对最新传输方式的支持;
|
|
172
|
-
* 当 TypeScript
|
|
175
|
+
* 当 TypeScript 更新到知晓这些传输方式后(大约在 4.6.3 之后),应最终被 TypeScript 的类型取代;
|
|
173
176
|
*/
|
|
174
177
|
export type AuthenticatorTransportFuture = 'ble' | 'cable' | 'hybrid' | 'internal' | 'nfc' | 'smart-card' | 'usb';
|
|
175
178
|
|
|
176
179
|
/**
|
|
177
180
|
* TypeScript 的 `PublicKeyCredentialDescriptor` 的超集,知晓最新的传输方式;
|
|
178
|
-
* 当 TypeScript
|
|
181
|
+
* 当 TypeScript 更新到知晓这些传输方式后(大约在 4.6.3 之后),应最终被 TypeScript 的类型取代;
|
|
179
182
|
*/
|
|
180
183
|
export interface PublicKeyCredentialDescriptorFuture extends Omit<PublicKeyCredentialDescriptor, 'transports'> {
|
|
181
184
|
transports?: AuthenticatorTransportFuture[];
|
|
@@ -196,15 +199,15 @@ export interface PublicKeyCredentialFuture extends PublicKeyCredential {
|
|
|
196
199
|
}
|
|
197
200
|
|
|
198
201
|
/**
|
|
199
|
-
* 由认证器数据中的第 3
|
|
202
|
+
* 由认证器数据中的第 3 位("备份资格")定义的两种凭证类型:
|
|
200
203
|
* - `"singleDevice"` 凭证永远不会被备份
|
|
201
204
|
* - `"multiDevice"` 凭证可以被备份
|
|
202
205
|
*/
|
|
203
206
|
export type CredentialDeviceType = 'singleDevice' | 'multiDevice';
|
|
204
207
|
|
|
205
208
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
209
|
+
* 依赖方可以在注册时传递给浏览器的认证器类别,能够理解这些值的浏览器可以优化其模态体验,
|
|
210
|
+
* 将用户引导至特定的注册流程:
|
|
208
211
|
*
|
|
209
212
|
* - `hybrid`: 移动设备上的平台认证器
|
|
210
213
|
* - `security-key`: 可通过 USB 或 NFC 连接在多个设备上使用的便携式 FIDO2 认证器
|
|
@@ -228,10 +231,11 @@ export type AttestationFormat = 'fido-u2f' | 'packed' | 'android-safetynet' | 'a
|
|
|
228
231
|
*
|
|
229
232
|
* **背景**
|
|
230
233
|
*
|
|
231
|
-
* `Uint8Array` 在TypeScript 5.7中成为泛型类型,要求从Deno 2.2开始将简单定义为 `Uint8Array`
|
|
232
|
-
*
|
|
234
|
+
* `Uint8Array` 在TypeScript 5.7中成为泛型类型,要求从Deno 2.2开始将简单定义为 `Uint8Array` 的类型
|
|
235
|
+
* 重构为 `Uint8Array<ArrayBuffer>`;
|
|
236
|
+
* 然而,在 Deno 2.1.x 及更早版本中 `Uint8Array` 不是泛型,因此此类型有助于弥合这一差距;
|
|
233
237
|
*
|
|
234
|
-
* 灵感来自 Deno
|
|
238
|
+
* 灵感来自 Deno 的标准库:
|
|
235
239
|
*
|
|
236
240
|
* https://github.com/denoland/std/blob/b5a5fe4f96b91c1fe8dba5cc0270092dd11d3287/bytes/_types.ts#L11
|
|
237
241
|
*/
|