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/lib/client.js
CHANGED
|
@@ -68,6 +68,33 @@ window.__ModuleLoader__.load({
|
|
|
68
68
|
if (styleEl.parentNode !== null) styleEl.parentNode.removeChild(styleEl);
|
|
69
69
|
}, 'dsh-webui-oauth: styles');
|
|
70
70
|
|
|
71
|
+
// ---------------- DeepSeek 登录:打开回调中转子窗口 ----------------
|
|
72
|
+
// 官方登录会新开窗口去 IdP;远程部署回调落在 127.0.0.1,打不开。
|
|
73
|
+
// 在用户点击「登录」的同一手势里打开中转窗口(避免被弹窗拦截),
|
|
74
|
+
// 用户授权后把回调 URL 粘进去即可。命中失败时仍有内联粘贴框兜底。
|
|
75
|
+
(function hookDeepSeekLogin() {
|
|
76
|
+
if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') return;
|
|
77
|
+
let last = 0;
|
|
78
|
+
const handler = (ev) => {
|
|
79
|
+
try {
|
|
80
|
+
const target = ev.target;
|
|
81
|
+
const btn = target && target.closest ? target.closest('button,[role="button"]') : null;
|
|
82
|
+
if (!btn) return;
|
|
83
|
+
const label = (btn.textContent || '').trim();
|
|
84
|
+
if (!/^(登录|Sign in|继续登录|重新登录)$/i.test(label)) return;
|
|
85
|
+
const scope = btn.closest('[role="dialog"],section,form,div');
|
|
86
|
+
const text = scope ? (scope.textContent || '') : '';
|
|
87
|
+
if (!/DeepSeek|账号|Account/i.test(text)) return;
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
if (now - last < 1500) return;
|
|
90
|
+
last = now;
|
|
91
|
+
window.open('/dsh-webui-oauth/relay-window', 'dsh-wua-relay', 'width=480,height=620');
|
|
92
|
+
} catch (e) { /* best effort */ }
|
|
93
|
+
};
|
|
94
|
+
document.addEventListener('click', handler, true);
|
|
95
|
+
ctx.effect(() => () => document.removeEventListener('click', handler, true), 'dsh-webui-oauth: deepseek relay hook');
|
|
96
|
+
})();
|
|
97
|
+
|
|
71
98
|
// ---------------- DeepSeek 登录回调:合并进官方「等待登录」对话框 ----------------
|
|
72
99
|
//
|
|
73
100
|
// 官方账号登录是 SignInDialog(另一个插件)渲染的,没有开放插槽;但它等待
|
|
@@ -105,8 +132,16 @@ window.__ModuleLoader__.load({
|
|
|
105
132
|
submit.type = 'button';
|
|
106
133
|
submit.className = 'wua-btn';
|
|
107
134
|
submit.textContent = '完成登录';
|
|
135
|
+
const pop = document.createElement('button');
|
|
136
|
+
pop.type = 'button';
|
|
137
|
+
pop.className = 'wua-btn ghost';
|
|
138
|
+
pop.textContent = '在新窗口打开';
|
|
139
|
+
pop.addEventListener('click', () => {
|
|
140
|
+
window.open('/dsh-webui-oauth/relay-window', 'dsh-wua-relay', 'width=480,height=620');
|
|
141
|
+
});
|
|
108
142
|
row.appendChild(input);
|
|
109
143
|
row.appendChild(submit);
|
|
144
|
+
row.appendChild(pop);
|
|
110
145
|
|
|
111
146
|
const msg = document.createElement('p');
|
|
112
147
|
msg.style.display = 'none';
|
|
@@ -276,8 +311,8 @@ window.__ModuleLoader__.load({
|
|
|
276
311
|
// (服务端只回传掩码,前端拿不到 sub 明文,也不应该拿)。
|
|
277
312
|
const [oidcBound, setOidcBound] = react.useState(false);
|
|
278
313
|
const [oidcBoundHint, setOidcBoundHint] = react.useState('');
|
|
279
|
-
const [unbindPw, setUnbindPw] = react.useState('');
|
|
280
314
|
const [bindMsg, setBindMsg] = react.useState(null);
|
|
315
|
+
const [bindWaiting, setBindWaiting] = react.useState(false);
|
|
281
316
|
// 敏感操作复验(保存配置 / 禁用 / 解绑):密码或 SSO 一次性令牌。
|
|
282
317
|
const [saveOpen, setSaveOpen] = react.useState(false);
|
|
283
318
|
const [savePw, setSavePw] = react.useState('');
|
|
@@ -288,6 +323,13 @@ window.__ModuleLoader__.load({
|
|
|
288
323
|
// 远程访问信任开关。
|
|
289
324
|
const [remoteTrustBusy, setRemoteTrustBusy] = react.useState(false);
|
|
290
325
|
const [remoteTrustMsg, setRemoteTrustMsg] = react.useState(null);
|
|
326
|
+
// 设备会话(App / CLI):独立列表,只有主动吊销才失效。
|
|
327
|
+
const [sessionList, setSessionList] = react.useState(null);
|
|
328
|
+
const [sessionBusy, setSessionBusy] = react.useState(false);
|
|
329
|
+
const [sessionMsg, setSessionMsg] = react.useState(null);
|
|
330
|
+
const [deviceList, setDeviceList] = react.useState(null);
|
|
331
|
+
const [deviceBusy, setDeviceBusy] = react.useState(false);
|
|
332
|
+
const [deviceMsg, setDeviceMsg] = react.useState(null);
|
|
291
333
|
// 服务端算好的完整回调地址(含 scheme/host/port)。原生 OIDC 要求
|
|
292
334
|
// redirect_uri 与 IdP 登记值精确匹配,少一个字符都会被拒,所以要显示全。
|
|
293
335
|
const [oidcRedirectUri, setOidcRedirectUri] = react.useState('');
|
|
@@ -330,6 +372,14 @@ window.__ModuleLoader__.load({
|
|
|
330
372
|
if (!alive) return;
|
|
331
373
|
if (r && r.ok && Array.isArray(r.entries)) setAudit(r.entries);
|
|
332
374
|
}).catch(() => { /* 审计不可用时静默 */ });
|
|
375
|
+
api('/dsh-webui-oauth/devices').then((r) => {
|
|
376
|
+
if (!alive) return;
|
|
377
|
+
if (r && r.ok && Array.isArray(r.devices)) setDeviceList(r.devices);
|
|
378
|
+
}).catch(() => { /* 设备列表不可用时静默 */ });
|
|
379
|
+
api('/dsh-webui-oauth/sessions').then((r) => {
|
|
380
|
+
if (!alive) return;
|
|
381
|
+
if (r && r.ok && Array.isArray(r.sessions)) setSessionList(r.sessions);
|
|
382
|
+
}).catch(() => { /* 会话列表不可用时静默 */ });
|
|
333
383
|
return () => {
|
|
334
384
|
alive = false;
|
|
335
385
|
};
|
|
@@ -349,6 +399,18 @@ window.__ModuleLoader__.load({
|
|
|
349
399
|
if (s && !s.error) setStatus(s);
|
|
350
400
|
}).catch(() => {});
|
|
351
401
|
}
|
|
402
|
+
if (e.data && e.data.type === 'dsh-wua-bound' && e.data.ok) {
|
|
403
|
+
setBindWaiting(false);
|
|
404
|
+
api('/dsh-webui-oauth/status').then((s) => {
|
|
405
|
+
if (s && !s.error) {
|
|
406
|
+
setStatus(s);
|
|
407
|
+
const o = s.oidc || {};
|
|
408
|
+
setOidcBound(!!o.bound);
|
|
409
|
+
setOidcBoundHint(o.boundSubHint || '');
|
|
410
|
+
}
|
|
411
|
+
}).catch(() => {});
|
|
412
|
+
setBindMsg({ kind: 'ok', text: 'OIDC 身份绑定成功。此后只有该身份可通过 SSO 登录。' });
|
|
413
|
+
}
|
|
352
414
|
};
|
|
353
415
|
window.addEventListener('message', onMessage);
|
|
354
416
|
return () => window.removeEventListener('message', onMessage);
|
|
@@ -455,7 +517,10 @@ window.__ModuleLoader__.load({
|
|
|
455
517
|
setBusy(false);
|
|
456
518
|
if (r && r.ok) {
|
|
457
519
|
if (enabled) {
|
|
458
|
-
|
|
520
|
+
const text = r.passwordChanged
|
|
521
|
+
? '已保存。密码已更新,已吊销 ' + (r.revoked || 0) + ' 个其他浏览器会话(设备会话不受影响)。'
|
|
522
|
+
: '已保存。当前会话保持有效。';
|
|
523
|
+
setMsg({ kind: 'ok', text });
|
|
459
524
|
} else {
|
|
460
525
|
setMsg({ kind: 'ok', text: '已保存并启用认证,当前浏览器已登录。' });
|
|
461
526
|
}
|
|
@@ -564,28 +629,39 @@ window.__ModuleLoader__.load({
|
|
|
564
629
|
setBindMsg(null);
|
|
565
630
|
// base 用当前页面的 origin:反代部署下浏览器看到的才是对外地址。
|
|
566
631
|
const base = location.origin || '';
|
|
567
|
-
|
|
632
|
+
const q = base ? '?base=' + encodeURIComponent(base) + '&popup=1' : '?popup=1';
|
|
633
|
+
const win = window.open('/dsh-webui-oauth/oidc/bind' + q, 'dsh-wua-bind', 'width=520,height=720');
|
|
634
|
+
if (!win) {
|
|
635
|
+
// 弹窗被拦截:回退整页跳转(回调会走 oidcbound=1 回执)
|
|
636
|
+
location.href = '/dsh-webui-oauth/oidc/bind' + (base ? '?base=' + encodeURIComponent(base) : '');
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
setBindWaiting(true);
|
|
568
640
|
};
|
|
569
641
|
|
|
570
|
-
const
|
|
642
|
+
const doUnbind = (auth) => {
|
|
571
643
|
if (busy) return;
|
|
572
|
-
|
|
573
|
-
|
|
644
|
+
const credentials = auth || {};
|
|
645
|
+
if (!credentials.current && !reauthValid) {
|
|
646
|
+
setSaveErr('解除绑定需要验证:请输入当前密码,或使用 SSO 验证。');
|
|
574
647
|
return;
|
|
575
648
|
}
|
|
576
649
|
setBusy(true);
|
|
577
650
|
setBindMsg(null);
|
|
578
|
-
api('/dsh-webui-oauth/oidc/unbind', { current:
|
|
651
|
+
api('/dsh-webui-oauth/oidc/unbind', { current: credentials.current || '' }).then((r) => {
|
|
579
652
|
setBusy(false);
|
|
580
653
|
if (r && r.ok) {
|
|
581
|
-
setUnbindPw('');
|
|
582
654
|
setOidcBound(false);
|
|
583
655
|
setOidcBoundHint('');
|
|
656
|
+
setSaveOpen(false);
|
|
657
|
+
setSavePw('');
|
|
658
|
+
setSaveErr('');
|
|
584
659
|
setBindMsg({ kind: 'ok', text: '已解除绑定。此后 OIDC 登录将被拒绝,直到重新绑定。' });
|
|
585
660
|
api('/dsh-webui-oauth/status').then((s) => {
|
|
586
661
|
if (s && !s.error) setStatus(s);
|
|
587
662
|
}).catch(() => {});
|
|
588
663
|
} else if (r && r.error === 'current-invalid') {
|
|
664
|
+
setSaveErr('当前密码不正确');
|
|
589
665
|
setBindMsg({ kind: 'err', text: '当前密码不正确' });
|
|
590
666
|
} else if (r && r.error === 'sso-only-active') {
|
|
591
667
|
setBindMsg({ kind: 'err', text: (r && r.hint) || '请先关闭「仅允许 SSO」再解绑' });
|
|
@@ -598,6 +674,14 @@ window.__ModuleLoader__.load({
|
|
|
598
674
|
});
|
|
599
675
|
};
|
|
600
676
|
|
|
677
|
+
const onUnbindClick = () => {
|
|
678
|
+
if (busy) return;
|
|
679
|
+
if (reauthValid) { doUnbind({}); return; }
|
|
680
|
+
setVerifyAction('unbind');
|
|
681
|
+
setSaveErr('');
|
|
682
|
+
setSaveOpen(true);
|
|
683
|
+
};
|
|
684
|
+
|
|
601
685
|
const onLogout = () => {
|
|
602
686
|
if (busy) return;
|
|
603
687
|
setBusy(true);
|
|
@@ -675,6 +759,54 @@ window.__ModuleLoader__.load({
|
|
|
675
759
|
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
|
|
676
760
|
}
|
|
677
761
|
|
|
762
|
+
const onRevokeSession = (id) => {
|
|
763
|
+
if (sessionBusy) return;
|
|
764
|
+
setSessionBusy(true);
|
|
765
|
+
setSessionMsg(null);
|
|
766
|
+
api('/dsh-webui-oauth/sessions/revoke-one', { id }).then((r) => {
|
|
767
|
+
setSessionBusy(false);
|
|
768
|
+
if (r && r.ok) {
|
|
769
|
+
setSessionList((list) => (list || []).filter((s) => s.id !== id));
|
|
770
|
+
setSessionMsg({ kind: 'ok', text: '已吊销该浏览器会话。' });
|
|
771
|
+
} else {
|
|
772
|
+
setSessionMsg({ kind: 'err', text: (r && (r.reason || r.error)) || '吊销失败' });
|
|
773
|
+
}
|
|
774
|
+
}).catch(() => {
|
|
775
|
+
setSessionBusy(false);
|
|
776
|
+
setSessionMsg({ kind: 'err', text: '无法连接认证服务' });
|
|
777
|
+
});
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
const refreshSessions = () => {
|
|
781
|
+
api('/dsh-webui-oauth/sessions').then((r) => {
|
|
782
|
+
if (r && r.ok && Array.isArray(r.sessions)) setSessionList(r.sessions);
|
|
783
|
+
}).catch(() => {});
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
const onRevokeDevice = (id) => {
|
|
787
|
+
if (deviceBusy) return;
|
|
788
|
+
setDeviceBusy(true);
|
|
789
|
+
setDeviceMsg(null);
|
|
790
|
+
api('/dsh-webui-oauth/devices/revoke', { id }).then((r) => {
|
|
791
|
+
setDeviceBusy(false);
|
|
792
|
+
if (r && r.ok) {
|
|
793
|
+
setDeviceList((list) => (list || []).filter((d) => d.id !== id));
|
|
794
|
+
setDeviceMsg({ kind: 'ok', text: '已吊销该设备会话,对应 token 立即失效。' });
|
|
795
|
+
} else {
|
|
796
|
+
setDeviceMsg({ kind: 'err', text: (r && (r.reason || r.error)) || '吊销失败' });
|
|
797
|
+
}
|
|
798
|
+
}).catch(() => {
|
|
799
|
+
setDeviceBusy(false);
|
|
800
|
+
setDeviceMsg({ kind: 'err', text: '无法连接认证服务' });
|
|
801
|
+
});
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
const refreshDevices = () => {
|
|
805
|
+
api('/dsh-webui-oauth/devices').then((r) => {
|
|
806
|
+
if (r && r.ok && Array.isArray(r.devices)) setDeviceList(r.devices);
|
|
807
|
+
}).catch(() => {});
|
|
808
|
+
};
|
|
809
|
+
|
|
678
810
|
const onToggleRemoteTrust = () => {
|
|
679
811
|
if (remoteTrustBusy) return;
|
|
680
812
|
const next = !remoteTrust;
|
|
@@ -825,20 +957,15 @@ window.__ModuleLoader__.load({
|
|
|
825
957
|
'。只有该身份可通过 SSO 登录。')
|
|
826
958
|
: react.createElement('p', { className: 'wua-warn' },
|
|
827
959
|
'尚未绑定任何 OIDC 身份。此时 OIDC 登录一律被拒绝,登录页也不会显示 SSO 按钮。请先保存上方配置,再点击「绑定 OIDC 身份」并完成一次 IdP 登录。'),
|
|
828
|
-
oidcBound
|
|
829
|
-
? react.createElement('div', { className: 'wua-row' },
|
|
830
|
-
react.createElement('label', { className: 'wua-label' }, '当前密码(解除绑定需验证)'),
|
|
831
|
-
react.createElement('input', { className: 'wua-input', type: 'password', value: unbindPw, autoComplete: 'current-password', onChange: (e) => setUnbindPw(e.target.value) }),
|
|
832
|
-
)
|
|
833
|
-
: null,
|
|
834
960
|
react.createElement('div', { className: 'wua-actions' },
|
|
835
961
|
oidcBound
|
|
836
|
-
? react.createElement('button', { className: 'wua-btn ghost', disabled: busy || status === null, onClick:
|
|
962
|
+
? react.createElement('button', { className: 'wua-btn ghost', disabled: busy || status === null, onClick: onUnbindClick }, '解除绑定')
|
|
837
963
|
: react.createElement('button', { className: 'wua-btn', disabled: busy || status === null, onClick: onBind }, '绑定 OIDC 身份'),
|
|
838
964
|
oidcBound
|
|
839
965
|
? react.createElement('button', { className: 'wua-btn ghost', disabled: busy || status === null, onClick: onBind }, '重新绑定其他身份')
|
|
840
966
|
: null,
|
|
841
967
|
),
|
|
968
|
+
bindWaiting ? react.createElement('p', { className: 'wua-msg' }, '等待绑定…请在打开的窗口中完成 IdP 授权。') : null,
|
|
842
969
|
bindMsg ? react.createElement('p', { className: bindMsg.kind === 'ok' ? 'wua-msg' : 'wua-err' }, bindMsg.text) : null,
|
|
843
970
|
]) : null,
|
|
844
971
|
card('远程访问', [
|
|
@@ -861,6 +988,68 @@ window.__ModuleLoader__.load({
|
|
|
861
988
|
react.createElement('p', { className: 'wua-hint', style: { borderTop: 'none', paddingTop: 0 } },
|
|
862
989
|
'在「设置 → 账号」点击 DeepSeek 登录后,浏览器会跳到 127.0.0.1 的回调地址(远程机器上通常打不开)。官方「等待登录」窗口里会直接出现一个粘贴框:把地址栏里的完整 URL 粘进去即可。'),
|
|
863
990
|
]),
|
|
991
|
+
card('浏览器会话', [
|
|
992
|
+
sessionList === null ? null : badge(String(sessionList.length) + ' 个', sessionList.length ? 'ok' : 'muted'),
|
|
993
|
+
], [
|
|
994
|
+
react.createElement('p', { className: 'wua-intro' },
|
|
995
|
+
'当前已登录的浏览器。可单独吊销某个会话;设备会话在下一张卡片单独管理。'),
|
|
996
|
+
sessionList === null
|
|
997
|
+
? react.createElement('p', { className: 'wua-intro' }, '正在读取…')
|
|
998
|
+
: (sessionList.length === 0
|
|
999
|
+
? react.createElement('p', { className: 'wua-intro' }, '暂无活动的浏览器会话')
|
|
1000
|
+
: react.createElement('div', { className: 'wua-audit' },
|
|
1001
|
+
sessionList.map((s) => react.createElement('div', {
|
|
1002
|
+
key: s.id,
|
|
1003
|
+
className: 'wua-audit-row',
|
|
1004
|
+
style: { alignItems: 'center' },
|
|
1005
|
+
},
|
|
1006
|
+
react.createElement('span', { className: 'wua-audit-event' }, s.current ? '当前会话' : (s.device || '浏览器')),
|
|
1007
|
+
react.createElement('span', { className: 'wua-audit-user' }, [s.os, s.browser].filter(Boolean).join(' · ') || '未知设备'),
|
|
1008
|
+
react.createElement('span', { className: 'wua-audit-time' }, '指纹 ' + (s.fingerprint || '—')),
|
|
1009
|
+
react.createElement('span', { className: 'wua-audit-time' }, '最近 ' + fmtTime(s.lastSeenAt)),
|
|
1010
|
+
react.createElement('span', { style: { flex: 1 } }),
|
|
1011
|
+
react.createElement('button', {
|
|
1012
|
+
className: 'wua-btn ghost',
|
|
1013
|
+
disabled: sessionBusy || s.current,
|
|
1014
|
+
title: s.current ? '当前会话请用「退出登录」' : '吊销该会话',
|
|
1015
|
+
onClick: () => onRevokeSession(s.id),
|
|
1016
|
+
}, s.current ? '当前' : '吊销'),
|
|
1017
|
+
)))),
|
|
1018
|
+
react.createElement('div', { className: 'wua-actions' },
|
|
1019
|
+
react.createElement('button', { className: 'wua-btn ghost', onClick: refreshSessions }, '刷新列表'),
|
|
1020
|
+
),
|
|
1021
|
+
sessionMsg ? react.createElement('p', { className: sessionMsg.kind === 'ok' ? 'wua-msg' : 'wua-err' }, sessionMsg.text) : null,
|
|
1022
|
+
]),
|
|
1023
|
+
card('设备会话(App / CLI)', [
|
|
1024
|
+
deviceList === null ? null : badge(String(deviceList.length) + ' 个', deviceList.length ? 'ok' : 'muted'),
|
|
1025
|
+
], [
|
|
1026
|
+
react.createElement('p', { className: 'wua-intro' },
|
|
1027
|
+
'设备通过 OAuth 2.1 设备授权(无回调)登录。它们与浏览器会话完全独立,不会过期,只有主动吊销才会失效。'),
|
|
1028
|
+
deviceList === null
|
|
1029
|
+
? react.createElement('p', { className: 'wua-intro' }, '正在读取…')
|
|
1030
|
+
: (deviceList.length === 0
|
|
1031
|
+
? react.createElement('p', { className: 'wua-intro' }, '暂无设备会话')
|
|
1032
|
+
: react.createElement('div', { className: 'wua-audit' },
|
|
1033
|
+
deviceList.map((d) => react.createElement('div', {
|
|
1034
|
+
key: d.id,
|
|
1035
|
+
className: 'wua-audit-row',
|
|
1036
|
+
style: { alignItems: 'center' },
|
|
1037
|
+
},
|
|
1038
|
+
react.createElement('span', { className: 'wua-audit-event' }, d.name || '设备'),
|
|
1039
|
+
react.createElement('span', { className: 'wua-audit-time' }, '创建 ' + fmtTime(d.createdAt)),
|
|
1040
|
+
d.lastUsedAt ? react.createElement('span', { className: 'wua-audit-time' }, '最近 ' + fmtTime(d.lastUsedAt)) : null,
|
|
1041
|
+
react.createElement('span', { style: { flex: 1 } }),
|
|
1042
|
+
react.createElement('button', {
|
|
1043
|
+
className: 'wua-btn ghost',
|
|
1044
|
+
disabled: deviceBusy,
|
|
1045
|
+
onClick: () => onRevokeDevice(d.id),
|
|
1046
|
+
}, '吊销'),
|
|
1047
|
+
)))),
|
|
1048
|
+
react.createElement('div', { className: 'wua-actions' },
|
|
1049
|
+
react.createElement('button', { className: 'wua-btn ghost', onClick: refreshDevices }, '刷新列表'),
|
|
1050
|
+
),
|
|
1051
|
+
deviceMsg ? react.createElement('p', { className: deviceMsg.kind === 'ok' ? 'wua-msg' : 'wua-err' }, deviceMsg.text) : null,
|
|
1052
|
+
]),
|
|
864
1053
|
card('最近登录记录', [], [
|
|
865
1054
|
react.createElement('p', { className: 'wua-hint', style: { borderTop: 'none', paddingTop: 0 } },
|
|
866
1055
|
'完整审计日志可用 node index.js audit 查看。'),
|
|
@@ -880,7 +1069,10 @@ window.__ModuleLoader__.load({
|
|
|
880
1069
|
saveOpen ? react.createElement('div', { className: 'wua-modal-mask' },
|
|
881
1070
|
react.createElement('div', { className: 'wua-modal' },
|
|
882
1071
|
react.createElement('h4', { className: 'wua-card-title' },
|
|
883
|
-
verifyAction === 'disable' ? '验证后禁用'
|
|
1072
|
+
verifyAction === 'disable' ? '验证后禁用'
|
|
1073
|
+
: verifyAction === 'revoke' ? '验证后吊销会话'
|
|
1074
|
+
: verifyAction === 'unbind' ? '验证后解绑'
|
|
1075
|
+
: '验证后保存'),
|
|
884
1076
|
react.createElement('p', { className: 'wua-hint', style: { borderTop: 'none', paddingTop: 0 } },
|
|
885
1077
|
reauthValid
|
|
886
1078
|
? '已通过 SSO 验证(2 分钟内有效),可直接继续。'
|
|
@@ -898,8 +1090,13 @@ window.__ModuleLoader__.load({
|
|
|
898
1090
|
const auth = reauthValid ? {} : { current: savePw };
|
|
899
1091
|
if (verifyAction === 'disable') doDisable(auth);
|
|
900
1092
|
else if (verifyAction === 'revoke') doRevoke(auth);
|
|
1093
|
+
else if (verifyAction === 'unbind') doUnbind(auth);
|
|
901
1094
|
else onSave(auth);
|
|
902
|
-
} }, busy ? '处理中…' : (
|
|
1095
|
+
} }, busy ? '处理中…' : (
|
|
1096
|
+
verifyAction === 'disable' ? '验证并禁用'
|
|
1097
|
+
: verifyAction === 'revoke' ? '验证并吊销'
|
|
1098
|
+
: verifyAction === 'unbind' ? '验证并解绑'
|
|
1099
|
+
: '验证并保存')),
|
|
903
1100
|
react.createElement('button', { className: 'wua-btn ghost', disabled: busy, onClick: () => setSaveOpen(false) }, '取消'),
|
|
904
1101
|
),
|
|
905
1102
|
(status && status.oidc && status.oidc.enabled && status.oidc.bound)
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-webui-oauth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "node test/redirect-scheme.test.mjs && node test/proxy-host.test.mjs && node test/trusted-origin.test.mjs && node test/oidc.test.mjs && node test/oidc-state-ttl.test.mjs && node test/set-cookie-multi.test.mjs && node test/oidc-integration.test.mjs && node test/oidc-config-store.test.mjs && node test/adopt-data-dir.test.mjs && node test/oidc-ec.test.mjs && node test/jwks-refresh.test.mjs && node test/oidc-bind.test.mjs && node test/client-bundle-id.test.mjs && node test/client-prefill.test.mjs && node test/displace-original.test.mjs && node test/oidc-redirect-matrix.test.mjs && node test/gate-default-deny.test.mjs && node test/fallback-shape.test.mjs && node test/desktop-marker.test.mjs && node test/audit-null.test.mjs && node test/remote-trust.test.mjs && node test/oauth-relay.test.mjs && node test/lifecycle.test.mjs"
|
|
7
|
+
"test": "node test/redirect-scheme.test.mjs && node test/proxy-host.test.mjs && node test/trusted-origin.test.mjs && node test/oidc.test.mjs && node test/oidc-state-ttl.test.mjs && node test/set-cookie-multi.test.mjs && node test/oidc-integration.test.mjs && node test/oidc-config-store.test.mjs && node test/adopt-data-dir.test.mjs && node test/oidc-ec.test.mjs && node test/jwks-refresh.test.mjs && node test/oidc-bind.test.mjs && node test/client-bundle-id.test.mjs && node test/client-prefill.test.mjs && node test/displace-original.test.mjs && node test/oidc-redirect-matrix.test.mjs && node test/gate-default-deny.test.mjs && node test/fallback-shape.test.mjs && node test/desktop-marker.test.mjs && node test/audit-null.test.mjs && node test/remote-trust.test.mjs && node test/oauth-relay.test.mjs && node test/lifecycle.test.mjs && node test/device-flow.test.mjs"
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./index.js",
|