@nihplod/sso-sdk 1.6.0 → 1.7.0
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/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +69 -78
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +69 -78
- package/dist/index.mjs.map +1 -1
- package/dist/next/index.d.mts +50 -1
- package/dist/next/index.d.ts +50 -1
- package/dist/next/index.js +260 -56
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +259 -56
- package/dist/next/index.mjs.map +1 -1
- package/dist/react/index.d.mts +7 -2
- package/dist/react/index.d.ts +7 -2
- package/dist/react/index.js +106 -96
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +106 -96
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.mts
CHANGED
|
@@ -326,7 +326,7 @@ declare class SsoClient {
|
|
|
326
326
|
* SSO SDK 错误类型
|
|
327
327
|
*/
|
|
328
328
|
/** SSO 错误码 */
|
|
329
|
-
type SsoErrorCode = "invalid_config" | "state_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
329
|
+
type SsoErrorCode = "invalid_config" | "state_mismatch" | "issuer_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
330
330
|
/**
|
|
331
331
|
* SSO SDK 自定义错误
|
|
332
332
|
*/
|
|
@@ -456,6 +456,11 @@ interface RequireAuthProps {
|
|
|
456
456
|
*/
|
|
457
457
|
renderLoginError?: (error: unknown, retry: () => void) => React.ReactNode;
|
|
458
458
|
}
|
|
459
|
+
interface LoginErrorContentProps {
|
|
460
|
+
loginError: unknown;
|
|
461
|
+
retry: () => void;
|
|
462
|
+
renderLoginError?: RequireAuthProps["renderLoginError"];
|
|
463
|
+
}
|
|
459
464
|
/**
|
|
460
465
|
* 路由保护组件
|
|
461
466
|
*
|
|
@@ -470,7 +475,7 @@ interface RequireAuthProps {
|
|
|
470
475
|
* </RequireAuth>
|
|
471
476
|
* ```
|
|
472
477
|
*/
|
|
473
|
-
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps>;
|
|
478
|
+
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps> | React.FunctionComponentElement<LoginErrorContentProps>;
|
|
474
479
|
/**
|
|
475
480
|
* 高阶组件:包装页面组件,要求认证后才能访问
|
|
476
481
|
*
|
package/dist/react/index.d.ts
CHANGED
|
@@ -326,7 +326,7 @@ declare class SsoClient {
|
|
|
326
326
|
* SSO SDK 错误类型
|
|
327
327
|
*/
|
|
328
328
|
/** SSO 错误码 */
|
|
329
|
-
type SsoErrorCode = "invalid_config" | "state_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
329
|
+
type SsoErrorCode = "invalid_config" | "state_mismatch" | "issuer_mismatch" | "pkce_required" | "token_request_failed" | "session_expired" | "no_refresh_token" | "userinfo_failed" | "not_authenticated" | "authorization_code_expired" | "authorization_code_used" | "client_disabled" | "user_denied_authorization" | "account_disabled" | "sso_server_error" | "rate_limited" | "network_error" | "popup_blocked" | "popup_closed" | "id_token_invalid" | "id_token_unsupported_alg" | "id_token_hs256_unsupported" | "id_token_missing_secret" | "id_token_invalid_signature" | "id_token_issuer_mismatch" | "id_token_audience_mismatch" | "id_token_expired" | "id_token_missing_sub" | "id_token_at_hash_mismatch" | "id_token_nonce_mismatch" | "logout_token_invalid" | "logout_token_unsupported_alg" | "logout_token_invalid_signature" | "logout_token_issuer_mismatch" | "logout_token_audience_mismatch" | "logout_token_expired" | "logout_token_replay";
|
|
330
330
|
/**
|
|
331
331
|
* SSO SDK 自定义错误
|
|
332
332
|
*/
|
|
@@ -456,6 +456,11 @@ interface RequireAuthProps {
|
|
|
456
456
|
*/
|
|
457
457
|
renderLoginError?: (error: unknown, retry: () => void) => React.ReactNode;
|
|
458
458
|
}
|
|
459
|
+
interface LoginErrorContentProps {
|
|
460
|
+
loginError: unknown;
|
|
461
|
+
retry: () => void;
|
|
462
|
+
renderLoginError?: RequireAuthProps["renderLoginError"];
|
|
463
|
+
}
|
|
459
464
|
/**
|
|
460
465
|
* 路由保护组件
|
|
461
466
|
*
|
|
@@ -470,7 +475,7 @@ interface RequireAuthProps {
|
|
|
470
475
|
* </RequireAuth>
|
|
471
476
|
* ```
|
|
472
477
|
*/
|
|
473
|
-
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps>;
|
|
478
|
+
declare function RequireAuth({ children, fallback, autoLogin, usePopup, onError, renderLoginError, }: RequireAuthProps): string | number | bigint | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.FunctionComponentElement<React.FragmentProps> | React.FunctionComponentElement<LoginErrorContentProps>;
|
|
474
479
|
/**
|
|
475
480
|
* 高阶组件:包装页面组件,要求认证后才能访问
|
|
476
481
|
*
|
package/dist/react/index.js
CHANGED
|
@@ -124,24 +124,6 @@ var SILENT_PROBE_KEY = "silent_probe";
|
|
|
124
124
|
function buildKey(base, clientId) {
|
|
125
125
|
return clientId ? `${base}:${clientId}` : base;
|
|
126
126
|
}
|
|
127
|
-
function createMemoryStorageAdapter() {
|
|
128
|
-
const store = /* @__PURE__ */ new Map();
|
|
129
|
-
return {
|
|
130
|
-
get(key) {
|
|
131
|
-
return store.get(key) ?? null;
|
|
132
|
-
},
|
|
133
|
-
set(key, value) {
|
|
134
|
-
store.set(key, value);
|
|
135
|
-
},
|
|
136
|
-
remove(key) {
|
|
137
|
-
store.delete(key);
|
|
138
|
-
},
|
|
139
|
-
keys() {
|
|
140
|
-
return [...store.keys()];
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
var memoryStorageAdapter = createMemoryStorageAdapter();
|
|
145
127
|
function createSessionStorageAdapter() {
|
|
146
128
|
const fallback = /* @__PURE__ */ new Map();
|
|
147
129
|
return {
|
|
@@ -409,7 +391,19 @@ async function fetchJwks(baseUrl, options = {}) {
|
|
|
409
391
|
return cachedJwks.jwks;
|
|
410
392
|
}
|
|
411
393
|
const discovery = await fetchDiscoveryDoc(baseUrl);
|
|
412
|
-
|
|
394
|
+
let jwksUri = `${baseUrl}/api/oauth/jwks`;
|
|
395
|
+
if (discovery?.jwks_uri) {
|
|
396
|
+
try {
|
|
397
|
+
if (new URL(discovery.jwks_uri).origin === new URL(baseUrl).origin) {
|
|
398
|
+
jwksUri = discovery.jwks_uri;
|
|
399
|
+
} else {
|
|
400
|
+
console.warn(
|
|
401
|
+
"[SSO SDK] Discovery \u7684 jwks_uri \u4E0E ssoBaseUrl \u4E0D\u540C\u6E90\uFF0C\u5DF2\u5FFD\u7565\u5E76\u4F7F\u7528\u9ED8\u8BA4 JWKS \u7AEF\u70B9"
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
}
|
|
413
407
|
try {
|
|
414
408
|
const res = await fetch(jwksUri, {
|
|
415
409
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
@@ -600,11 +594,7 @@ function assertValidTokenResponse(data, requireRefreshToken) {
|
|
|
600
594
|
);
|
|
601
595
|
}
|
|
602
596
|
}
|
|
603
|
-
var SILENT_PROBE_ERRORS = /* @__PURE__ */ new Set([
|
|
604
|
-
"login_required",
|
|
605
|
-
"consent_required",
|
|
606
|
-
"interaction_required"
|
|
607
|
-
]);
|
|
597
|
+
var SILENT_PROBE_ERRORS = /* @__PURE__ */ new Set(["login_required", "consent_required", "interaction_required"]);
|
|
608
598
|
var _SsoClient = class _SsoClient {
|
|
609
599
|
constructor(config) {
|
|
610
600
|
this._discovery = null;
|
|
@@ -670,6 +660,22 @@ var _SsoClient = class _SsoClient {
|
|
|
670
660
|
if (!authorizationOk) bad.push("authorization_endpoint");
|
|
671
661
|
if (!sameOrigin(d.token_endpoint)) bad.push("token_endpoint");
|
|
672
662
|
if (!sameOrigin(d.userinfo_endpoint)) bad.push("userinfo_endpoint");
|
|
663
|
+
if (d.jwks_uri && !sameOrigin(d.jwks_uri)) bad.push("jwks_uri");
|
|
664
|
+
if (d.revocation_endpoint && !sameOrigin(d.revocation_endpoint)) {
|
|
665
|
+
bad.push("revocation_endpoint");
|
|
666
|
+
}
|
|
667
|
+
if (d.introspection_endpoint && !sameOrigin(d.introspection_endpoint)) {
|
|
668
|
+
bad.push("introspection_endpoint");
|
|
669
|
+
}
|
|
670
|
+
if (d.end_session_endpoint) {
|
|
671
|
+
try {
|
|
672
|
+
if (new URL(d.end_session_endpoint).origin !== publicOrigin) {
|
|
673
|
+
bad.push("end_session_endpoint");
|
|
674
|
+
}
|
|
675
|
+
} catch {
|
|
676
|
+
bad.push("end_session_endpoint");
|
|
677
|
+
}
|
|
678
|
+
}
|
|
673
679
|
if (bad.length > 0) {
|
|
674
680
|
if (!this._discoveryOriginWarned) {
|
|
675
681
|
this._discoveryOriginWarned = true;
|
|
@@ -695,16 +701,11 @@ var _SsoClient = class _SsoClient {
|
|
|
695
701
|
const url = `${this._serverBase}/api/oauth/.well-known/openid-configuration`;
|
|
696
702
|
try {
|
|
697
703
|
const controller = new AbortController();
|
|
698
|
-
const timeoutId = setTimeout(
|
|
699
|
-
() => controller.abort(),
|
|
700
|
-
_SsoClient.DISCOVERY_TIMEOUT_MS
|
|
701
|
-
);
|
|
704
|
+
const timeoutId = setTimeout(() => controller.abort(), _SsoClient.DISCOVERY_TIMEOUT_MS);
|
|
702
705
|
const res = await fetch(url, { signal: controller.signal });
|
|
703
706
|
clearTimeout(timeoutId);
|
|
704
707
|
if (!res.ok) {
|
|
705
|
-
console.warn(
|
|
706
|
-
`[SSO SDK] OIDC Discovery \u8BF7\u6C42\u5931\u8D25: HTTP ${res.status}, \u56DE\u9000\u5230\u9ED8\u8BA4\u7AEF\u70B9`
|
|
707
|
-
);
|
|
708
|
+
console.warn(`[SSO SDK] OIDC Discovery \u8BF7\u6C42\u5931\u8D25: HTTP ${res.status}, \u56DE\u9000\u5230\u9ED8\u8BA4\u7AEF\u70B9`);
|
|
708
709
|
return null;
|
|
709
710
|
}
|
|
710
711
|
const doc = await res.json();
|
|
@@ -919,10 +920,7 @@ var _SsoClient = class _SsoClient {
|
|
|
919
920
|
removeOAuthState(this.config.clientId);
|
|
920
921
|
removeOAuthNonce(this.config.clientId);
|
|
921
922
|
removePkceVerifier(`${this.config.clientId}_popup_nonce`);
|
|
922
|
-
throw new SsoError(
|
|
923
|
-
"popup_blocked",
|
|
924
|
-
"\u5F39\u7A97\u88AB\u6D4F\u89C8\u5668\u62E6\u622A\uFF0C\u8BF7\u5141\u8BB8\u5F39\u7A97\u540E\u91CD\u8BD5"
|
|
925
|
-
);
|
|
923
|
+
throw new SsoError("popup_blocked", "\u5F39\u7A97\u88AB\u6D4F\u89C8\u5668\u62E6\u622A\uFF0C\u8BF7\u5141\u8BB8\u5F39\u7A97\u540E\u91CD\u8BD5");
|
|
926
924
|
}
|
|
927
925
|
const redirectUriOrigin = new URL(this.config.redirectUri).origin;
|
|
928
926
|
try {
|
|
@@ -951,9 +949,7 @@ var _SsoClient = class _SsoClient {
|
|
|
951
949
|
}
|
|
952
950
|
this.handleCallback(event.data.callbackUrl).then((tokenData) => {
|
|
953
951
|
if (tokenData === null) {
|
|
954
|
-
reject(
|
|
955
|
-
new SsoError("not_authenticated", "SSO \u4F1A\u8BDD\u4E0D\u5B58\u5728\uFF08\u9759\u9ED8\u63A2\u6D4B\u65E0\u4F1A\u8BDD\uFF09")
|
|
956
|
-
);
|
|
952
|
+
reject(new SsoError("not_authenticated", "SSO \u4F1A\u8BDD\u4E0D\u5B58\u5728\uFF08\u9759\u9ED8\u63A2\u6D4B\u65E0\u4F1A\u8BDD\uFF09"));
|
|
957
953
|
return;
|
|
958
954
|
}
|
|
959
955
|
resolve(tokenData);
|
|
@@ -964,12 +960,7 @@ var _SsoClient = class _SsoClient {
|
|
|
964
960
|
if (!completed) {
|
|
965
961
|
completed = true;
|
|
966
962
|
cleanup();
|
|
967
|
-
reject(
|
|
968
|
-
new SsoError(
|
|
969
|
-
"popup_closed",
|
|
970
|
-
"\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED"
|
|
971
|
-
)
|
|
972
|
-
);
|
|
963
|
+
reject(new SsoError("popup_closed", "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED"));
|
|
973
964
|
}
|
|
974
965
|
}
|
|
975
966
|
}, 500);
|
|
@@ -1036,18 +1027,24 @@ var _SsoClient = class _SsoClient {
|
|
|
1036
1027
|
removeOAuthNonce(this.config.clientId);
|
|
1037
1028
|
removePkceVerifier(this.config.clientId);
|
|
1038
1029
|
removeSilentProbe(this.config.clientId);
|
|
1039
|
-
throw new SsoError(
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1030
|
+
throw new SsoError("state_mismatch", "State \u53C2\u6570\u4E0D\u5339\u914D\uFF0C\u53EF\u80FD\u5B58\u5728 CSRF \u653B\u51FB");
|
|
1031
|
+
}
|
|
1032
|
+
const returnedIss = params.get("iss");
|
|
1033
|
+
if (returnedIss) {
|
|
1034
|
+
const discovery = await this._getDiscovery();
|
|
1035
|
+
const expectedIssuer = (discovery?.issuer || this.config.ssoBaseUrl).replace(/\/+$/, "");
|
|
1036
|
+
if (returnedIss.replace(/\/+$/, "") !== expectedIssuer) {
|
|
1037
|
+
removeOAuthState(this.config.clientId);
|
|
1038
|
+
removeOAuthNonce(this.config.clientId);
|
|
1039
|
+
removePkceVerifier(this.config.clientId);
|
|
1040
|
+
removeSilentProbe(this.config.clientId);
|
|
1041
|
+
throw new SsoError("issuer_mismatch", "\u6388\u6743\u54CD\u5E94 issuer \u4E0D\u5339\u914D\uFF0C\u53EF\u80FD\u5B58\u5728 IdP \u6DF7\u53E0\u653B\u51FB");
|
|
1042
|
+
}
|
|
1043
1043
|
}
|
|
1044
1044
|
const expectedNonce = getOAuthNonce(this.config.clientId) ?? void 0;
|
|
1045
1045
|
const verifier = getPkceVerifier(this.config.clientId);
|
|
1046
1046
|
if (!verifier) {
|
|
1047
|
-
throw new SsoError(
|
|
1048
|
-
"pkce_required",
|
|
1049
|
-
"code_verifier \u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u5DF2\u8FC7\u671F\u6216\u6765\u81EA\u5176\u4ED6\u6807\u7B7E\u9875\uFF09"
|
|
1050
|
-
);
|
|
1047
|
+
throw new SsoError("pkce_required", "code_verifier \u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u5DF2\u8FC7\u671F\u6216\u6765\u81EA\u5176\u4ED6\u6807\u7B7E\u9875\uFF09");
|
|
1051
1048
|
}
|
|
1052
1049
|
const tokenEndpoint = await this._getTokenEndpoint();
|
|
1053
1050
|
const body = new URLSearchParams();
|
|
@@ -1094,10 +1091,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1094
1091
|
removePkceVerifier(this.config.clientId);
|
|
1095
1092
|
removeSilentProbe(this.config.clientId);
|
|
1096
1093
|
removeTokenData(this.config.clientId);
|
|
1097
|
-
throw new SsoError(
|
|
1098
|
-
"id_token_invalid",
|
|
1099
|
-
"Token \u54CD\u5E94\u7F3A\u5C11 id_token\uFF08scope \u542B openid \u65F6\u5FC5\u9700\uFF09"
|
|
1100
|
-
);
|
|
1094
|
+
throw new SsoError("id_token_invalid", "Token \u54CD\u5E94\u7F3A\u5C11 id_token\uFF08scope \u542B openid \u65F6\u5FC5\u9700\uFF09");
|
|
1101
1095
|
}
|
|
1102
1096
|
if (data.id_token) {
|
|
1103
1097
|
try {
|
|
@@ -1162,19 +1156,14 @@ var _SsoClient = class _SsoClient {
|
|
|
1162
1156
|
}
|
|
1163
1157
|
let res = null;
|
|
1164
1158
|
let lastErr;
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
break;
|
|
1174
|
-
} catch (err) {
|
|
1175
|
-
lastErr = err;
|
|
1176
|
-
if (attempt < 1) await new Promise((r) => setTimeout(r, 1e3));
|
|
1177
|
-
}
|
|
1159
|
+
try {
|
|
1160
|
+
res = await fetchWithTimeout(tokenEndpoint, {
|
|
1161
|
+
method: "POST",
|
|
1162
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1163
|
+
body: body.toString()
|
|
1164
|
+
});
|
|
1165
|
+
} catch (err) {
|
|
1166
|
+
lastErr = err;
|
|
1178
1167
|
}
|
|
1179
1168
|
if (lastErr || !res) {
|
|
1180
1169
|
throw new SsoError("network_error", "\u5237\u65B0 Token \u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25", lastErr);
|
|
@@ -1256,10 +1245,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1256
1245
|
removeTokenData(this.config.clientId);
|
|
1257
1246
|
throw new SsoError("not_authenticated", "Token \u5DF2\u5931\u6548");
|
|
1258
1247
|
}
|
|
1259
|
-
throw new SsoError(
|
|
1260
|
-
"userinfo_failed",
|
|
1261
|
-
`\u83B7\u53D6\u7528\u6237\u4FE1\u606F\u5931\u8D25: HTTP ${res.status}`
|
|
1262
|
-
);
|
|
1248
|
+
throw new SsoError("userinfo_failed", `\u83B7\u53D6\u7528\u6237\u4FE1\u606F\u5931\u8D25: HTTP ${res.status}`);
|
|
1263
1249
|
}
|
|
1264
1250
|
return await res.json();
|
|
1265
1251
|
}
|
|
@@ -1319,7 +1305,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1319
1305
|
if (this.config.clientSecret) {
|
|
1320
1306
|
revokeBody.set("client_secret", this.config.clientSecret);
|
|
1321
1307
|
}
|
|
1322
|
-
|
|
1308
|
+
const doRevoke = () => fetchWithTimeout(
|
|
1323
1309
|
revokeUrl,
|
|
1324
1310
|
{
|
|
1325
1311
|
method: "POST",
|
|
@@ -1328,6 +1314,14 @@ var _SsoClient = class _SsoClient {
|
|
|
1328
1314
|
},
|
|
1329
1315
|
REVOKE_TIMEOUT_MS
|
|
1330
1316
|
);
|
|
1317
|
+
let res = await doRevoke();
|
|
1318
|
+
if (!res.ok && (res.status >= 500 || res.status === 429)) {
|
|
1319
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1320
|
+
res = await doRevoke();
|
|
1321
|
+
}
|
|
1322
|
+
if (!res.ok) {
|
|
1323
|
+
this.debugLog("logout: \u670D\u52A1\u7AEF\u64A4\u9500 refresh_token \u5931\u8D25", { status: res.status });
|
|
1324
|
+
}
|
|
1331
1325
|
} catch {
|
|
1332
1326
|
}
|
|
1333
1327
|
}
|
|
@@ -1338,10 +1332,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1338
1332
|
logoutUrl.searchParams.set("client_id", this.config.clientId);
|
|
1339
1333
|
const postLogoutUri = this.config.postLogoutRedirectUri || this.config.redirectUri;
|
|
1340
1334
|
if (postLogoutUri) {
|
|
1341
|
-
logoutUrl.searchParams.set(
|
|
1342
|
-
"post_logout_redirect_uri",
|
|
1343
|
-
postLogoutUri
|
|
1344
|
-
);
|
|
1335
|
+
logoutUrl.searchParams.set("post_logout_redirect_uri", postLogoutUri);
|
|
1345
1336
|
}
|
|
1346
1337
|
if (idTokenHint) {
|
|
1347
1338
|
logoutUrl.searchParams.set("id_token_hint", idTokenHint);
|
|
@@ -1571,7 +1562,15 @@ function SsoProvider({
|
|
|
1571
1562
|
const tokenData = getTokenData(client.config.clientId);
|
|
1572
1563
|
if (!tokenData) return;
|
|
1573
1564
|
const remainingSec = (tokenData.expires_at - Date.now()) / 1e3;
|
|
1574
|
-
|
|
1565
|
+
const lifetimeSec = Math.max(
|
|
1566
|
+
1,
|
|
1567
|
+
(tokenData.expires_at - tokenData.issued_at) / 1e3
|
|
1568
|
+
);
|
|
1569
|
+
const effectiveThreshold = Math.max(
|
|
1570
|
+
1,
|
|
1571
|
+
Math.min(refreshThreshold, Math.floor(lifetimeSec / 3))
|
|
1572
|
+
);
|
|
1573
|
+
if (remainingSec <= 0 || remainingSec <= effectiveThreshold) {
|
|
1575
1574
|
refreshTimerRef.current = setTimeout(
|
|
1576
1575
|
() => {
|
|
1577
1576
|
if (active) void attemptRefresh();
|
|
@@ -1580,13 +1579,13 @@ function SsoProvider({
|
|
|
1580
1579
|
);
|
|
1581
1580
|
return;
|
|
1582
1581
|
}
|
|
1583
|
-
const delayMs = Math.max((remainingSec -
|
|
1582
|
+
const delayMs = Math.max((remainingSec - effectiveThreshold) * 1e3, minDelayMs, 1e3);
|
|
1584
1583
|
refreshTimerRef.current = setTimeout(() => {
|
|
1585
1584
|
if (!active) return;
|
|
1586
1585
|
const td = getTokenData(client.config.clientId);
|
|
1587
1586
|
if (!td) return;
|
|
1588
1587
|
const secLeft = (td.expires_at - Date.now()) / 1e3;
|
|
1589
|
-
if (secLeft <=
|
|
1588
|
+
if (secLeft <= effectiveThreshold) {
|
|
1590
1589
|
void attemptRefresh();
|
|
1591
1590
|
} else {
|
|
1592
1591
|
scheduleNextRefresh();
|
|
@@ -1601,7 +1600,7 @@ function SsoProvider({
|
|
|
1601
1600
|
refreshTimerRef.current = null;
|
|
1602
1601
|
}
|
|
1603
1602
|
};
|
|
1604
|
-
}, [client, loadUser, refreshThreshold, onTokenRefreshed]);
|
|
1603
|
+
}, [client, loadUser, refreshThreshold, onTokenRefreshed, markSessionExpired]);
|
|
1605
1604
|
(0, import_react.useEffect)(() => {
|
|
1606
1605
|
const handleStorageChange = (e) => {
|
|
1607
1606
|
if (e.key && e.key.startsWith("nihplod_sso_") && !e.key.startsWith(REFRESH_LOCK_PREFIX)) {
|
|
@@ -1696,6 +1695,26 @@ function useSso() {
|
|
|
1696
1695
|
|
|
1697
1696
|
// src/react/RequireAuth.tsx
|
|
1698
1697
|
var import_react2 = __toESM(require("react"));
|
|
1698
|
+
function LoginErrorContent({
|
|
1699
|
+
loginError,
|
|
1700
|
+
retry,
|
|
1701
|
+
renderLoginError
|
|
1702
|
+
}) {
|
|
1703
|
+
if (renderLoginError) {
|
|
1704
|
+
return import_react2.default.createElement(
|
|
1705
|
+
import_react2.default.Fragment,
|
|
1706
|
+
null,
|
|
1707
|
+
renderLoginError(loginError, retry)
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
const message = loginError instanceof Error ? loginError.message : "\u767B\u5F55\u5931\u8D25";
|
|
1711
|
+
return import_react2.default.createElement(
|
|
1712
|
+
"div",
|
|
1713
|
+
null,
|
|
1714
|
+
import_react2.default.createElement("p", null, `\u767B\u5F55\u672A\u5B8C\u6210\uFF1A${message}`),
|
|
1715
|
+
import_react2.default.createElement("button", { onClick: retry }, "\u91CD\u8BD5\u767B\u5F55")
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1699
1718
|
function RequireAuth({
|
|
1700
1719
|
children,
|
|
1701
1720
|
fallback,
|
|
@@ -1741,20 +1760,11 @@ function RequireAuth({
|
|
|
1741
1760
|
}
|
|
1742
1761
|
if (!isAuthenticated) {
|
|
1743
1762
|
if (loginError) {
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
);
|
|
1750
|
-
}
|
|
1751
|
-
const message = loginError instanceof Error ? loginError.message : "\u767B\u5F55\u5931\u8D25";
|
|
1752
|
-
return import_react2.default.createElement(
|
|
1753
|
-
"div",
|
|
1754
|
-
null,
|
|
1755
|
-
import_react2.default.createElement("p", null, `\u767B\u5F55\u672A\u5B8C\u6210\uFF1A${message}`),
|
|
1756
|
-
import_react2.default.createElement("button", { onClick: retry }, "\u91CD\u8BD5\u767B\u5F55")
|
|
1757
|
-
);
|
|
1763
|
+
return import_react2.default.createElement(LoginErrorContent, {
|
|
1764
|
+
loginError,
|
|
1765
|
+
retry,
|
|
1766
|
+
renderLoginError
|
|
1767
|
+
});
|
|
1758
1768
|
}
|
|
1759
1769
|
if (fallback) return import_react2.default.createElement(import_react2.default.Fragment, null, fallback);
|
|
1760
1770
|
return import_react2.default.createElement("div", null, "\u8BF7\u5148\u767B\u5F55");
|