dsh-webui-oauth 0.6.2 → 0.6.3

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 CHANGED
@@ -2,6 +2,35 @@
2
2
 
3
3
  本文件记录每个版本的**用户可见**变更。破坏性变更与升级动作单独标注。
4
4
 
5
+ ## 0.6.3
6
+
7
+ 修复部分 IdP 下**绑定 OIDC 立即失败**的问题(点「绑定 OIDC 身份」后立刻收到
8
+ `oidc-authorize-failed / invalid_request`)。
9
+
10
+ ### 修复:授权请求携带了 IdP 不支持的 prompt 参数
11
+
12
+ - **现象**:点绑定后浏览器被 IdP 打回,回调形如
13
+ `.../oidc/callback?error=invalid_request&error_description=unsupported+prompt+value+requested`,
14
+ 界面显示 `{"ok":false,"error":"oidc-authorize-failed","detail":"invalid_request"}`。
15
+ - **原因**:插件在授权请求里**硬编码**了 `prompt=select_account`。该参数是 OIDC 的
16
+ 可选扩展,不同 IdP 支持度差异极大;不支持的 IdP 会直接拒绝整个授权请求,
17
+ 于是授权根本发不出去。
18
+ - **修复**:默认**不发送** `prompt`。需要"强制选择账号"行为的部署可在设置页
19
+ **Prompt(可选)** 里显式填写(如 `select_account`),留空即不携带该参数。
20
+
21
+ ### 建议
22
+
23
+ - 若你此前被此问题挡住,升级后**重新点一次绑定**即可,无需改动其它配置。
24
+ - 多数 IdP 的登录页本来就会让用户选择账号,因此该参数通常不必填。
25
+
26
+ ### 测试
27
+
28
+ - `test/oidc-integration.test.mjs` 增加两条断言:默认不携带 `prompt`、
29
+ 显式配置时如实携带。已确认能抓出修复前的行为(回退硬编码时 2 项 FAIL)。
30
+ - 另在隔离实例上用**会拒绝 prompt 的 IdP**(复刻同类行为)做了端到端对照:
31
+ 带 `prompt` 复现出完全相同的错误;去掉后绑定成功、随后 SSO 登录成功。
32
+ - 全量 337 项断言通过。
33
+
5
34
  ## 0.6.2
6
35
 
7
36
  修复设置页在**切走再切回**时不回显已有配置的问题。
package/index.js CHANGED
@@ -2267,6 +2267,8 @@ export async function apply(ctx) {
2267
2267
  trustBrowserOrigin: oidcCfg.trustBrowserOrigin !== false,
2268
2268
  bound: isOidcBound(creds),
2269
2269
  boundSubHint: isOidcBound(creds) ? maskSubForDisplay(boundSubOf(creds)) : null,
2270
+ // prompt 是可选参数,回传当前值供设置页回显(缺省为空 = 不发送)。
2271
+ prompt: typeof oidcCfg.prompt === 'string' ? oidcCfg.prompt : '',
2270
2272
  // 提供给 IdP 登记用的确切回调地址。原生 OIDC 要求 redirect_uri 精确匹配,
2271
2273
  // 部署者必须把它抄进 IdP 的允许列表,因此这里直接算好给他。
2272
2274
  // 不能只给路径:IdP 侧需要完整的 scheme + host + port。
@@ -2415,6 +2417,9 @@ export async function apply(ctx) {
2415
2417
  scope: typeof oidcIn.scope === 'string' ? oidcIn.scope : undefined,
2416
2418
  redirectBase: typeof oidcIn.redirectBase === 'string' && oidcIn.redirectBase.trim() ? oidcIn.redirectBase.trim() : undefined,
2417
2419
  trustBrowserOrigin: oidcIn.trustBrowserOrigin !== undefined ? oidcIn.trustBrowserOrigin : true,
2420
+ // prompt 默认不发(留空即 undefined)。仅在部署者显式填写时才带给 IdP,
2421
+ // 因为不少 IdP 会因不认识的 prompt 值直接拒绝授权。
2422
+ prompt: typeof oidcIn.prompt === 'string' && oidcIn.prompt.trim() ? oidcIn.prompt.trim() : undefined,
2418
2423
  }
2419
2424
  } else if (oidcIn && oidcIn.enabled === false) {
2420
2425
  oidcOut = { enabled: false }
@@ -2543,7 +2548,15 @@ export async function apply(ctx) {
2543
2548
  authUrl.searchParams.set('nonce', nonce)
2544
2549
  authUrl.searchParams.set('code_challenge', oidcChallenge(verifier))
2545
2550
  authUrl.searchParams.set('code_challenge_method', 'S256')
2546
- authUrl.searchParams.set('prompt', 'select_account')
2551
+ // prompt 是 OIDC 的【可选】参数,且不同 IdP 支持度差异极大:
2552
+ // 部分 IdP(如某些企业 SSO)对不认识的 prompt 值直接回
2553
+ // error=invalid_request&error_description=unsupported prompt value requested,
2554
+ // 导致授权根本发不出去——表现就是"点绑定立刻失败"。
2555
+ // 因此这里【默认不发送】:多数 IdP 的登录页本来就会让用户选账号,
2556
+ // 显式 select_account 只对"已登录且想强制切换账号"的场景有意义。
2557
+ // 需要该行为的部署可通过 oidc.prompt 显式声明(见 README)。
2558
+ const promptValue = typeof o.prompt === 'string' ? o.prompt.trim() : ''
2559
+ if (promptValue) authUrl.searchParams.set('prompt', promptValue)
2547
2560
  // 登录 CSRF 绑定:把 state 同时写进一个 HttpOnly 短时效 Cookie。回调时两者必须
2548
2561
  // 同时匹配——否则攻击者可先在自己的浏览器发起授权拿到 state,再诱导受害者带着
2549
2562
  // 该 state 完成回调,把受害者的浏览器登录进攻击者的账号(会话固定/登录 CSRF)。
package/lib/client.js CHANGED
@@ -91,6 +91,9 @@ window.__ModuleLoader__.load({
91
91
  const [oidcScope, setOidcScope] = react.useState('openid profile email');
92
92
  const [oidcRedirectBase, setOidcRedirectBase] = react.useState('');
93
93
  const [oidcTrustBrowser, setOidcTrustBrowser] = react.useState(true);
94
+ // prompt(可选)。默认留空 = 不发送该参数——不少 IdP 会对不认识的
95
+ // prompt 值直接回 unsupported prompt value requested,导致授权发不出去。
96
+ const [oidcPrompt, setOidcPrompt] = react.useState('');
94
97
  // 绑定状态:bound 表示已绑定某个 IdP 身份;boundSubHint 是掩码后的展示值
95
98
  // (服务端只回传掩码,前端拿不到 sub 明文,也不应该拿)。
96
99
  const [oidcBound, setOidcBound] = react.useState(false);
@@ -127,6 +130,7 @@ window.__ModuleLoader__.load({
127
130
  setOidcClientId((o && o.clientId) || '');
128
131
  if (o && o.scope) setOidcScope(o.scope);
129
132
  if (o && o.redirectBase) setOidcRedirectBase(o.redirectBase);
133
+ if (o && typeof o.prompt === 'string') setOidcPrompt(o.prompt);
130
134
  setOidcBound(!!(o && o.bound));
131
135
  setOidcBoundHint((o && o.boundSubHint) || '');
132
136
  setOidcRedirectUri((o && o.redirectUri) || '');
@@ -186,6 +190,8 @@ window.__ModuleLoader__.load({
186
190
  scope: oidcScope.trim(),
187
191
  redirectBase: oidcRedirectBase.trim() || null,
188
192
  trustBrowserOrigin: oidcTrustBrowser,
193
+ // 留空即不发送 prompt(服务端会存成 undefined)
194
+ prompt: oidcPrompt.trim(),
189
195
  };
190
196
  api('/dsh-webui-oauth/configure', { username: name, password, current, ttl: Number(ttl), oidc: oidcPayload }).then((r) => {
191
197
  setBusy(false);
@@ -439,6 +445,12 @@ window.__ModuleLoader__.load({
439
445
  react.createElement('input', { type: 'checkbox', checked: oidcTrustBrowser, onChange: (e) => setOidcTrustBrowser(e.target.checked) }),
440
446
  ' 信任浏览器 origin(默认开启;关闭时仅用上方 Redirect Base)'),
441
447
  ),
448
+ react.createElement('div', { className: 'wua-row' },
449
+ react.createElement('label', { className: 'wua-label' }, 'Prompt(可选,建议留空)'),
450
+ react.createElement('input', { className: 'wua-input', type: 'text', value: oidcPrompt, spellCheck: false, placeholder: '留空则不发送该参数', onChange: (e) => setOidcPrompt(e.target.value) }),
451
+ react.createElement('p', { className: 'wua-hint', style: { borderTop: 'none', paddingTop: 0 } },
452
+ '多数 IdP 无需该参数。部分 IdP 会对不认识的值直接拒绝授权(提示 unsupported prompt value requested),此时请保持留空。'),
453
+ ),
442
454
  ) : null,
443
455
  // ---- OIDC 身份绑定 ----
444
456
  // 绑定是访问控制前提:未绑定时任何能通过该 IdP 的人都会被拒,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-webui-oauth",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "scripts": {