dsh-webui-oauth 0.7.9 → 0.8.1
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 +91 -0
- package/README.en.md +25 -0
- package/README.md +28 -1
- package/index.js +966 -20
- package/lib/client.js +214 -17
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,97 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录每个版本的**用户可见**变更。破坏性变更与升级动作单独标注。
|
|
4
4
|
|
|
5
|
+
## 0.8.1
|
|
6
|
+
|
|
7
|
+
设备批准登录回跳、浏览器会话列表、保存提示语义、DeepSeek 子窗口中转、绑定弹窗。
|
|
8
|
+
|
|
9
|
+
### 设备批准:未登录跳转登录并正确回跳
|
|
10
|
+
|
|
11
|
+
- `GET /dsh-webui-oauth/device` 加载时检查会话;未登录自动跳
|
|
12
|
+
`/login?next=/dsh-webui-oauth/device?user_code=...`;
|
|
13
|
+
- 登录页支持 `next`:密码登录与 SSO 都会在成功后回到设备批准页(保留 user_code);
|
|
14
|
+
- `safeNextPath()` 只允许同源相对路径,拒绝 `//host`、反斜杠与绝对 URL。
|
|
15
|
+
|
|
16
|
+
### 修正保存提示语义
|
|
17
|
+
|
|
18
|
+
- 只有**修改密码**时才吊销其他浏览器会话;只改 TTL/OIDC 配置不再踢人;
|
|
19
|
+
- 提示改为「密码已更新,已吊销 N 个其他浏览器会话(设备会话不受影响)」或
|
|
20
|
+
「已保存。当前会话保持有效。」;
|
|
21
|
+
- `configure` 返回 `revoked` / `passwordChanged` 供前端展示。
|
|
22
|
+
|
|
23
|
+
### 浏览器会话列表
|
|
24
|
+
|
|
25
|
+
- 会话记录新增:创建时间、最近活跃、IP、UA、系统、浏览器、设备形态;
|
|
26
|
+
- 设置页新增「浏览器会话」卡片:当前会话标记、系统/浏览器、设备指纹(UA+IP 短哈希)、
|
|
27
|
+
最近活跃时间,可**单独吊销**;
|
|
28
|
+
- 新增 `GET /dsh-webui-oauth/sessions` 与 `POST /dsh-webui-oauth/sessions/revoke-one`;
|
|
29
|
+
- 当前会话不允许从列表吊销(提示用「退出登录」)。
|
|
30
|
+
|
|
31
|
+
### DeepSeek 登录:子窗口中转
|
|
32
|
+
|
|
33
|
+
- 点击官方「登录」时,在同一用户手势内打开
|
|
34
|
+
`/dsh-webui-oauth/relay-window` 子窗口(类似 SSO 验证弹窗);
|
|
35
|
+
- 子窗口显示「等待验证」并接受回调 URL,成功后 `postMessage` 通知主窗口并自我关闭;
|
|
36
|
+
- 原「等待登录」对话框内的粘贴框保留为兜底,并新增「在新窗口打开」。
|
|
37
|
+
|
|
38
|
+
### 绑定 / 换绑 / 解绑
|
|
39
|
+
|
|
40
|
+
- 绑定与换绑改为子窗口:`/oidc/bind?popup=1`,回调返回自我关闭页面并
|
|
41
|
+
`postMessage({type:'dsh-wua-bound'})`;
|
|
42
|
+
- 解绑改为与保存一致的验证弹窗(当前密码或 SSO 复验),移除卡片里的密码输入框。
|
|
43
|
+
|
|
44
|
+
### 测试
|
|
45
|
+
|
|
46
|
+
- `lifecycle` 新增 16 项:登录 next、会话列表/单条吊销、保存不误吊销、
|
|
47
|
+
改密码吊销其他会话;
|
|
48
|
+
- `oidc-bind` 新增 5 项:绑定弹窗回调 HTML/通知/自我关闭/落库;
|
|
49
|
+
- 全量 24 个测试文件、677 项断言通过。
|
|
50
|
+
|
|
51
|
+
## 0.8.0
|
|
52
|
+
|
|
53
|
+
新增 **OAuth 2.1 设备授权(RFC 8628,无回调)**:App / CLI 可以获得与浏览器完全独立的设备会话,
|
|
54
|
+
只有主动吊销才失效。
|
|
55
|
+
|
|
56
|
+
### 新增:设备授权流程
|
|
57
|
+
|
|
58
|
+
- `POST /dsh-webui-oauth/device/authorize`(公开)→ `device_code`、`user_code`、
|
|
59
|
+
`verification_uri`、`verification_uri_complete`、`expires_in`、`interval`;
|
|
60
|
+
- `GET /dsh-webui-oauth/device`(公开)→ 批准页;输入代码后批准/拒绝;
|
|
61
|
+
- `POST /dsh-webui-oauth/device/approve`(**仅浏览器会话**)→ 批准或拒绝;
|
|
62
|
+
- `POST /dsh-webui-oauth/device/token`(公开)→ 轮询换 `access_token`;
|
|
63
|
+
支持 `authorization_pending` / `slow_down` / `access_denied` / `expired_token` / `invalid_grant`。
|
|
64
|
+
|
|
65
|
+
### 新增:独立设备会话
|
|
66
|
+
|
|
67
|
+
- 设备 token 存放在 `devices.jsonl`,**只存 SHA-256**,比对用 `timingSafeEqual`;
|
|
68
|
+
- 无过期时间,只有 `POST /dsh-webui-oauth/devices/revoke` 主动吊销才失效;
|
|
69
|
+
- `checkRequest` 接受 `Authorization: Bearer`,覆盖 `/api`、`/plugins`、
|
|
70
|
+
WebSocket upgrade、静态资源与 fallback;
|
|
71
|
+
- 设置页新增「设备会话(App / CLI)」卡片:名称、创建/最近使用时间、逐个吊销、刷新;
|
|
72
|
+
- 设备 Bearer **不能**做设备管理(列表 / 吊销 / 批准新设备只允许浏览器会话),
|
|
73
|
+
避免一个被窃取的设备 token 反向吊销其它设备或自我扩权。
|
|
74
|
+
|
|
75
|
+
### 新增:核心 launch Cookie 桥
|
|
76
|
+
|
|
77
|
+
- `POST /dsh-webui-oauth/device/core-session`(仅设备 Bearer):
|
|
78
|
+
服务端内部用 `connection.authenticatedUrl()` + `Host: <authority>` 调本机,
|
|
79
|
+
截获 `dsh-auth-*` Cookie,返回 `{cookie_name, cookie_value, authority}`;
|
|
80
|
+
- **不把 launch token 交给客户端**(它等价于该进程 owner 权限);
|
|
81
|
+
- 响应 `no-store`。
|
|
82
|
+
|
|
83
|
+
### 安全边界
|
|
84
|
+
|
|
85
|
+
- 设备授权是公开端点:待批准队列上限 1000、设备码 10 分钟过期、轮询最小间隔 5 秒;
|
|
86
|
+
- 设备 token = 该 dsh 的完整权限;建议只保存在可信设备,丢失后立即吊销;
|
|
87
|
+
- 设备管理只允许浏览器会话(Cookie),Bearer 会被 401。
|
|
88
|
+
|
|
89
|
+
### 测试
|
|
90
|
+
|
|
91
|
+
- 新增 `test/device-flow.test.mjs`(27 项):authorize → pending → 批准 → token →
|
|
92
|
+
Bearer 访问 → 设备列表 → 核心 Cookie 桥 → 主动吊销 → 401;磁盘只存哈希、
|
|
93
|
+
device_code 不可重放、设备 Bearer 不能做设备管理;
|
|
94
|
+
- 全量 24 个测试文件、656 项断言通过。
|
|
95
|
+
|
|
5
96
|
## 0.7.9
|
|
6
97
|
|
|
7
98
|
「概览」卡片新增**「吊销所有浏览器会话」**按钮。
|
package/README.en.md
CHANGED
|
@@ -418,6 +418,31 @@ credentials for no reason. The login path is the opposite and must send a new se
|
|
|
418
418
|
Reproduce with `test/oidc-redirect-matrix.test.mjs` (dependency-free unit regression,
|
|
419
419
|
wired into `npm test`).
|
|
420
420
|
|
|
421
|
+
## Device authorization (App / CLI, OAuth 2.1 device flow)
|
|
422
|
+
|
|
423
|
+
A second credential type for clients without browser cookies (dsh-Go, CLIs), following RFC 8628
|
|
424
|
+
with **no callback**:
|
|
425
|
+
|
|
426
|
+
1. `POST /dsh-webui-oauth/device/authorize` (public) → `device_code`, `user_code`, `verification_uri`;
|
|
427
|
+
2. the user opens `verification_uri` in a browser, signs in and approves (or denies) the code;
|
|
428
|
+
3. `POST /dsh-webui-oauth/device/token` (public) polls → `access_token` (Bearer).
|
|
429
|
+
|
|
430
|
+
Device sessions are **completely independent** of browser sessions:
|
|
431
|
+
|
|
432
|
+
- stored in `devices.jsonl` as SHA-256 hashes only;
|
|
433
|
+
- **no expiry** — only an explicit revoke (`POST /dsh-webui-oauth/devices/revoke`) invalidates them;
|
|
434
|
+
- listed separately on the settings page, revocable one by one;
|
|
435
|
+
- the Bearer goes through `checkRequest`, so it covers `/api`, `/plugins`, WebSockets and static assets;
|
|
436
|
+
- a device Bearer **cannot** manage devices (list / revoke / approve are browser-session only).
|
|
437
|
+
|
|
438
|
+
Core launch cookie bridge: `POST /dsh-webui-oauth/device/core-session` (device Bearer only) calls
|
|
439
|
+
`connection.authenticatedUrl()` internally and returns `{cookie_name, cookie_value, authority}`;
|
|
440
|
+
the launch token is **never** handed to the client.
|
|
441
|
+
|
|
442
|
+
Security: a device token equals full control of this dsh. Keep it on trusted devices and revoke it
|
|
443
|
+
immediately if lost. Public endpoints are bounded: 1000 pending codes, 10-minute expiry, 5-second poll
|
|
444
|
+
interval (`slow_down`).
|
|
445
|
+
|
|
421
446
|
## Known limits
|
|
422
447
|
|
|
423
448
|
- **DeepSeek official account login (0.2.1+, remote callback relay)**: `/oauth/callback` is **not**
|
package/README.md
CHANGED
|
@@ -418,12 +418,39 @@ IdP 侧日志确认完整往返:`discovery → jwks → authorize(issued code)
|
|
|
418
418
|
|
|
419
419
|
复现方式见 `test/oidc-redirect-matrix.test.mjs`(无需外部依赖的单元回归,已接入 `npm test`)。
|
|
420
420
|
|
|
421
|
+
## 设备授权(App / CLI,OAuth 2.1 device flow)
|
|
422
|
+
|
|
423
|
+
给没有浏览器 Cookie 的客户端(dsh-Go 等 App、CLI)使用的第二套凭据。流程遵循 RFC 8628,**无回调**:
|
|
424
|
+
|
|
425
|
+
1. `POST /dsh-webui-oauth/device/authorize`(公开)→ `device_code`、`user_code`、`verification_uri`;
|
|
426
|
+
2. 用户在浏览器打开 `verification_uri`;未登录会自动跳转登录页(密码或 SSO),
|
|
427
|
+
登录成功后回到批准页并保留 `user_code`,再批准(或拒绝);
|
|
428
|
+
3. `POST /dsh-webui-oauth/device/token`(公开)轮询 → `access_token`(Bearer)。
|
|
429
|
+
|
|
430
|
+
设备会话与浏览器会话**完全独立**:
|
|
431
|
+
|
|
432
|
+
- 单独存放于 `devices.jsonl`,只存 token 的 SHA-256;
|
|
433
|
+
- **没有过期时间**,只有显式吊销(`POST /dsh-webui-oauth/devices/revoke`)才失效;
|
|
434
|
+
- 设置页「设备会话(App / CLI)」单独列表,可逐个吊销;
|
|
435
|
+
- 浏览器会话也有独立列表(系统 / 浏览器 / 设备指纹 / 最近活跃),可单独吊销;
|
|
436
|
+
- 设备 Bearer 直接通过 `checkRequest`,可访问 `/api`、`/plugins`、WebSocket 与静态资源;
|
|
437
|
+
- 设备 Bearer **不能**做设备管理(列表 / 吊销 / 批准新设备只允许浏览器会话)。
|
|
438
|
+
|
|
439
|
+
核心 launch Cookie 桥:
|
|
440
|
+
|
|
441
|
+
`POST /dsh-webui-oauth/device/core-session`(仅设备 Bearer)在服务端内部用
|
|
442
|
+
`connection.authenticatedUrl()` 换取 `dsh-auth-*` Cookie,返回
|
|
443
|
+
`{cookie_name, cookie_value, authority}`;**不会把 launch token 交给客户端**。
|
|
444
|
+
|
|
445
|
+
安全边界:设备 token 等价于该 dsh 的完整权限。只保存在可信设备上;丢失后立即在设置页吊销。
|
|
446
|
+
公开端点有限流:待批准队列上限 1000、过期 10 分钟、轮询间隔 5 秒(`slow_down`)。
|
|
447
|
+
|
|
421
448
|
## 已知边界
|
|
422
449
|
|
|
423
450
|
- **DeepSeek 官方账号登录(0.2.1+,远程回调中转)**:`/oauth/callback` **不是公开路径**,仍受会话闸门保护。上游 `@deepseek-ai/dsh-deepseek-account-platform` 的 `loginOrigin()` 只接受 `http://localhost:<port>`、`http://127.0.0.1:<port>`、`http://[::1]:<port>`(显式非零端口),不支持反代域名。插件的处理方式:
|
|
424
451
|
- 劫持官方登录的 `callbackOrigin`:本地回环访问保留原 origin(cookie 同 host,回调直接命中);域名访问改写成 `http://127.0.0.1:<本机端口>`;
|
|
425
452
|
- 浏览器授权后会被跳到这个回环回调地址——远程机器上通常打不开,但地址栏里有完整 URL;
|
|
426
|
-
-
|
|
453
|
+
- 点击官方「登录」时会打开 `/dsh-webui-oauth/relay-window` 子窗口(类似 SSO 验证弹窗);授权后把地址栏 URL 粘贴进去,插件在服务端**内部重放**给账号平台注册的 `/oauth/callback` handler(state + PKCE 仍由平台校验),完成换 token;「等待登录」对话框内的粘贴框保留为兜底;
|
|
427
454
|
- 中转端点要求插件会话,且只接受本机端口的 `/oauth/callback` + 单个 `code`/`state`(防 SSRF / 开放重定向);
|
|
428
455
|
- 本机 `http://127.0.0.1:3080` 访问时官方登录原本就能直接走完,无需中转。
|
|
429
456
|
- **DSH Desktop**:支持 `dsh-desktop-*` 标记透传(登录 / 退出后重登不再丢失标记),但**不提供**上游 0.3.6 的「宿主载体桥」。该桥只用 socket 是否回环来判定,而本部署是 nginx 同机反代——所有请求的 socket 都是 `127.0.0.1`,启用它等于"持有核心 Cookie 即可绕过插件会话 / SSO",与「仅允许 SSO」冲突。若必须使用 DSH Desktop 的 shell 直连模式,请改用上游原版插件,并自行评估该旁路风险。
|