@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/index.d.mts
CHANGED
|
@@ -426,7 +426,7 @@ declare function generateState(): string;
|
|
|
426
426
|
* SSO SDK 错误类型
|
|
427
427
|
*/
|
|
428
428
|
/** SSO 错误码 */
|
|
429
|
-
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";
|
|
429
|
+
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";
|
|
430
430
|
/**
|
|
431
431
|
* 将 OAuth 2.0 服务端 error 字段映射到 SsoErrorCode
|
|
432
432
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -426,7 +426,7 @@ declare function generateState(): string;
|
|
|
426
426
|
* SSO SDK 错误类型
|
|
427
427
|
*/
|
|
428
428
|
/** SSO 错误码 */
|
|
429
|
-
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";
|
|
429
|
+
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";
|
|
430
430
|
/**
|
|
431
431
|
* 将 OAuth 2.0 服务端 error 字段映射到 SsoErrorCode
|
|
432
432
|
*
|
package/dist/index.js
CHANGED
|
@@ -143,24 +143,6 @@ var SILENT_PROBE_KEY = "silent_probe";
|
|
|
143
143
|
function buildKey(base, clientId) {
|
|
144
144
|
return clientId ? `${base}:${clientId}` : base;
|
|
145
145
|
}
|
|
146
|
-
function createMemoryStorageAdapter() {
|
|
147
|
-
const store = /* @__PURE__ */ new Map();
|
|
148
|
-
return {
|
|
149
|
-
get(key) {
|
|
150
|
-
return store.get(key) ?? null;
|
|
151
|
-
},
|
|
152
|
-
set(key, value) {
|
|
153
|
-
store.set(key, value);
|
|
154
|
-
},
|
|
155
|
-
remove(key) {
|
|
156
|
-
store.delete(key);
|
|
157
|
-
},
|
|
158
|
-
keys() {
|
|
159
|
-
return [...store.keys()];
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
var memoryStorageAdapter = createMemoryStorageAdapter();
|
|
164
146
|
var localStorageAdapter = {
|
|
165
147
|
get(key) {
|
|
166
148
|
try {
|
|
@@ -482,7 +464,19 @@ async function fetchJwks(baseUrl, options = {}) {
|
|
|
482
464
|
return cachedJwks.jwks;
|
|
483
465
|
}
|
|
484
466
|
const discovery = await fetchDiscoveryDoc(baseUrl);
|
|
485
|
-
|
|
467
|
+
let jwksUri = `${baseUrl}/api/oauth/jwks`;
|
|
468
|
+
if (discovery?.jwks_uri) {
|
|
469
|
+
try {
|
|
470
|
+
if (new URL(discovery.jwks_uri).origin === new URL(baseUrl).origin) {
|
|
471
|
+
jwksUri = discovery.jwks_uri;
|
|
472
|
+
} else {
|
|
473
|
+
console.warn(
|
|
474
|
+
"[SSO SDK] Discovery \u7684 jwks_uri \u4E0E ssoBaseUrl \u4E0D\u540C\u6E90\uFF0C\u5DF2\u5FFD\u7565\u5E76\u4F7F\u7528\u9ED8\u8BA4 JWKS \u7AEF\u70B9"
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
}
|
|
486
480
|
try {
|
|
487
481
|
const res = await fetch(jwksUri, {
|
|
488
482
|
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
@@ -673,11 +667,7 @@ function assertValidTokenResponse(data, requireRefreshToken) {
|
|
|
673
667
|
);
|
|
674
668
|
}
|
|
675
669
|
}
|
|
676
|
-
var SILENT_PROBE_ERRORS = /* @__PURE__ */ new Set([
|
|
677
|
-
"login_required",
|
|
678
|
-
"consent_required",
|
|
679
|
-
"interaction_required"
|
|
680
|
-
]);
|
|
670
|
+
var SILENT_PROBE_ERRORS = /* @__PURE__ */ new Set(["login_required", "consent_required", "interaction_required"]);
|
|
681
671
|
var _SsoClient = class _SsoClient {
|
|
682
672
|
constructor(config) {
|
|
683
673
|
this._discovery = null;
|
|
@@ -743,6 +733,22 @@ var _SsoClient = class _SsoClient {
|
|
|
743
733
|
if (!authorizationOk) bad.push("authorization_endpoint");
|
|
744
734
|
if (!sameOrigin(d.token_endpoint)) bad.push("token_endpoint");
|
|
745
735
|
if (!sameOrigin(d.userinfo_endpoint)) bad.push("userinfo_endpoint");
|
|
736
|
+
if (d.jwks_uri && !sameOrigin(d.jwks_uri)) bad.push("jwks_uri");
|
|
737
|
+
if (d.revocation_endpoint && !sameOrigin(d.revocation_endpoint)) {
|
|
738
|
+
bad.push("revocation_endpoint");
|
|
739
|
+
}
|
|
740
|
+
if (d.introspection_endpoint && !sameOrigin(d.introspection_endpoint)) {
|
|
741
|
+
bad.push("introspection_endpoint");
|
|
742
|
+
}
|
|
743
|
+
if (d.end_session_endpoint) {
|
|
744
|
+
try {
|
|
745
|
+
if (new URL(d.end_session_endpoint).origin !== publicOrigin) {
|
|
746
|
+
bad.push("end_session_endpoint");
|
|
747
|
+
}
|
|
748
|
+
} catch {
|
|
749
|
+
bad.push("end_session_endpoint");
|
|
750
|
+
}
|
|
751
|
+
}
|
|
746
752
|
if (bad.length > 0) {
|
|
747
753
|
if (!this._discoveryOriginWarned) {
|
|
748
754
|
this._discoveryOriginWarned = true;
|
|
@@ -768,16 +774,11 @@ var _SsoClient = class _SsoClient {
|
|
|
768
774
|
const url = `${this._serverBase}/api/oauth/.well-known/openid-configuration`;
|
|
769
775
|
try {
|
|
770
776
|
const controller = new AbortController();
|
|
771
|
-
const timeoutId = setTimeout(
|
|
772
|
-
() => controller.abort(),
|
|
773
|
-
_SsoClient.DISCOVERY_TIMEOUT_MS
|
|
774
|
-
);
|
|
777
|
+
const timeoutId = setTimeout(() => controller.abort(), _SsoClient.DISCOVERY_TIMEOUT_MS);
|
|
775
778
|
const res = await fetch(url, { signal: controller.signal });
|
|
776
779
|
clearTimeout(timeoutId);
|
|
777
780
|
if (!res.ok) {
|
|
778
|
-
console.warn(
|
|
779
|
-
`[SSO SDK] OIDC Discovery \u8BF7\u6C42\u5931\u8D25: HTTP ${res.status}, \u56DE\u9000\u5230\u9ED8\u8BA4\u7AEF\u70B9`
|
|
780
|
-
);
|
|
781
|
+
console.warn(`[SSO SDK] OIDC Discovery \u8BF7\u6C42\u5931\u8D25: HTTP ${res.status}, \u56DE\u9000\u5230\u9ED8\u8BA4\u7AEF\u70B9`);
|
|
781
782
|
return null;
|
|
782
783
|
}
|
|
783
784
|
const doc = await res.json();
|
|
@@ -992,10 +993,7 @@ var _SsoClient = class _SsoClient {
|
|
|
992
993
|
removeOAuthState(this.config.clientId);
|
|
993
994
|
removeOAuthNonce(this.config.clientId);
|
|
994
995
|
removePkceVerifier(`${this.config.clientId}_popup_nonce`);
|
|
995
|
-
throw new SsoError(
|
|
996
|
-
"popup_blocked",
|
|
997
|
-
"\u5F39\u7A97\u88AB\u6D4F\u89C8\u5668\u62E6\u622A\uFF0C\u8BF7\u5141\u8BB8\u5F39\u7A97\u540E\u91CD\u8BD5"
|
|
998
|
-
);
|
|
996
|
+
throw new SsoError("popup_blocked", "\u5F39\u7A97\u88AB\u6D4F\u89C8\u5668\u62E6\u622A\uFF0C\u8BF7\u5141\u8BB8\u5F39\u7A97\u540E\u91CD\u8BD5");
|
|
999
997
|
}
|
|
1000
998
|
const redirectUriOrigin = new URL(this.config.redirectUri).origin;
|
|
1001
999
|
try {
|
|
@@ -1024,9 +1022,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1024
1022
|
}
|
|
1025
1023
|
this.handleCallback(event.data.callbackUrl).then((tokenData) => {
|
|
1026
1024
|
if (tokenData === null) {
|
|
1027
|
-
reject(
|
|
1028
|
-
new SsoError("not_authenticated", "SSO \u4F1A\u8BDD\u4E0D\u5B58\u5728\uFF08\u9759\u9ED8\u63A2\u6D4B\u65E0\u4F1A\u8BDD\uFF09")
|
|
1029
|
-
);
|
|
1025
|
+
reject(new SsoError("not_authenticated", "SSO \u4F1A\u8BDD\u4E0D\u5B58\u5728\uFF08\u9759\u9ED8\u63A2\u6D4B\u65E0\u4F1A\u8BDD\uFF09"));
|
|
1030
1026
|
return;
|
|
1031
1027
|
}
|
|
1032
1028
|
resolve(tokenData);
|
|
@@ -1037,12 +1033,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1037
1033
|
if (!completed) {
|
|
1038
1034
|
completed = true;
|
|
1039
1035
|
cleanup();
|
|
1040
|
-
reject(
|
|
1041
|
-
new SsoError(
|
|
1042
|
-
"popup_closed",
|
|
1043
|
-
"\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED"
|
|
1044
|
-
)
|
|
1045
|
-
);
|
|
1036
|
+
reject(new SsoError("popup_closed", "\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED"));
|
|
1046
1037
|
}
|
|
1047
1038
|
}
|
|
1048
1039
|
}, 500);
|
|
@@ -1109,18 +1100,24 @@ var _SsoClient = class _SsoClient {
|
|
|
1109
1100
|
removeOAuthNonce(this.config.clientId);
|
|
1110
1101
|
removePkceVerifier(this.config.clientId);
|
|
1111
1102
|
removeSilentProbe(this.config.clientId);
|
|
1112
|
-
throw new SsoError(
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1103
|
+
throw new SsoError("state_mismatch", "State \u53C2\u6570\u4E0D\u5339\u914D\uFF0C\u53EF\u80FD\u5B58\u5728 CSRF \u653B\u51FB");
|
|
1104
|
+
}
|
|
1105
|
+
const returnedIss = params.get("iss");
|
|
1106
|
+
if (returnedIss) {
|
|
1107
|
+
const discovery = await this._getDiscovery();
|
|
1108
|
+
const expectedIssuer = (discovery?.issuer || this.config.ssoBaseUrl).replace(/\/+$/, "");
|
|
1109
|
+
if (returnedIss.replace(/\/+$/, "") !== expectedIssuer) {
|
|
1110
|
+
removeOAuthState(this.config.clientId);
|
|
1111
|
+
removeOAuthNonce(this.config.clientId);
|
|
1112
|
+
removePkceVerifier(this.config.clientId);
|
|
1113
|
+
removeSilentProbe(this.config.clientId);
|
|
1114
|
+
throw new SsoError("issuer_mismatch", "\u6388\u6743\u54CD\u5E94 issuer \u4E0D\u5339\u914D\uFF0C\u53EF\u80FD\u5B58\u5728 IdP \u6DF7\u53E0\u653B\u51FB");
|
|
1115
|
+
}
|
|
1116
1116
|
}
|
|
1117
1117
|
const expectedNonce = getOAuthNonce(this.config.clientId) ?? void 0;
|
|
1118
1118
|
const verifier = getPkceVerifier(this.config.clientId);
|
|
1119
1119
|
if (!verifier) {
|
|
1120
|
-
throw new SsoError(
|
|
1121
|
-
"pkce_required",
|
|
1122
|
-
"code_verifier \u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u5DF2\u8FC7\u671F\u6216\u6765\u81EA\u5176\u4ED6\u6807\u7B7E\u9875\uFF09"
|
|
1123
|
-
);
|
|
1120
|
+
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");
|
|
1124
1121
|
}
|
|
1125
1122
|
const tokenEndpoint = await this._getTokenEndpoint();
|
|
1126
1123
|
const body = new URLSearchParams();
|
|
@@ -1167,10 +1164,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1167
1164
|
removePkceVerifier(this.config.clientId);
|
|
1168
1165
|
removeSilentProbe(this.config.clientId);
|
|
1169
1166
|
removeTokenData(this.config.clientId);
|
|
1170
|
-
throw new SsoError(
|
|
1171
|
-
"id_token_invalid",
|
|
1172
|
-
"Token \u54CD\u5E94\u7F3A\u5C11 id_token\uFF08scope \u542B openid \u65F6\u5FC5\u9700\uFF09"
|
|
1173
|
-
);
|
|
1167
|
+
throw new SsoError("id_token_invalid", "Token \u54CD\u5E94\u7F3A\u5C11 id_token\uFF08scope \u542B openid \u65F6\u5FC5\u9700\uFF09");
|
|
1174
1168
|
}
|
|
1175
1169
|
if (data.id_token) {
|
|
1176
1170
|
try {
|
|
@@ -1235,19 +1229,14 @@ var _SsoClient = class _SsoClient {
|
|
|
1235
1229
|
}
|
|
1236
1230
|
let res = null;
|
|
1237
1231
|
let lastErr;
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
break;
|
|
1247
|
-
} catch (err) {
|
|
1248
|
-
lastErr = err;
|
|
1249
|
-
if (attempt < 1) await new Promise((r) => setTimeout(r, 1e3));
|
|
1250
|
-
}
|
|
1232
|
+
try {
|
|
1233
|
+
res = await fetchWithTimeout(tokenEndpoint, {
|
|
1234
|
+
method: "POST",
|
|
1235
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1236
|
+
body: body.toString()
|
|
1237
|
+
});
|
|
1238
|
+
} catch (err) {
|
|
1239
|
+
lastErr = err;
|
|
1251
1240
|
}
|
|
1252
1241
|
if (lastErr || !res) {
|
|
1253
1242
|
throw new SsoError("network_error", "\u5237\u65B0 Token \u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25", lastErr);
|
|
@@ -1329,10 +1318,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1329
1318
|
removeTokenData(this.config.clientId);
|
|
1330
1319
|
throw new SsoError("not_authenticated", "Token \u5DF2\u5931\u6548");
|
|
1331
1320
|
}
|
|
1332
|
-
throw new SsoError(
|
|
1333
|
-
"userinfo_failed",
|
|
1334
|
-
`\u83B7\u53D6\u7528\u6237\u4FE1\u606F\u5931\u8D25: HTTP ${res.status}`
|
|
1335
|
-
);
|
|
1321
|
+
throw new SsoError("userinfo_failed", `\u83B7\u53D6\u7528\u6237\u4FE1\u606F\u5931\u8D25: HTTP ${res.status}`);
|
|
1336
1322
|
}
|
|
1337
1323
|
return await res.json();
|
|
1338
1324
|
}
|
|
@@ -1392,7 +1378,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1392
1378
|
if (this.config.clientSecret) {
|
|
1393
1379
|
revokeBody.set("client_secret", this.config.clientSecret);
|
|
1394
1380
|
}
|
|
1395
|
-
|
|
1381
|
+
const doRevoke = () => fetchWithTimeout(
|
|
1396
1382
|
revokeUrl,
|
|
1397
1383
|
{
|
|
1398
1384
|
method: "POST",
|
|
@@ -1401,6 +1387,14 @@ var _SsoClient = class _SsoClient {
|
|
|
1401
1387
|
},
|
|
1402
1388
|
REVOKE_TIMEOUT_MS
|
|
1403
1389
|
);
|
|
1390
|
+
let res = await doRevoke();
|
|
1391
|
+
if (!res.ok && (res.status >= 500 || res.status === 429)) {
|
|
1392
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1393
|
+
res = await doRevoke();
|
|
1394
|
+
}
|
|
1395
|
+
if (!res.ok) {
|
|
1396
|
+
this.debugLog("logout: \u670D\u52A1\u7AEF\u64A4\u9500 refresh_token \u5931\u8D25", { status: res.status });
|
|
1397
|
+
}
|
|
1404
1398
|
} catch {
|
|
1405
1399
|
}
|
|
1406
1400
|
}
|
|
@@ -1411,10 +1405,7 @@ var _SsoClient = class _SsoClient {
|
|
|
1411
1405
|
logoutUrl.searchParams.set("client_id", this.config.clientId);
|
|
1412
1406
|
const postLogoutUri = this.config.postLogoutRedirectUri || this.config.redirectUri;
|
|
1413
1407
|
if (postLogoutUri) {
|
|
1414
|
-
logoutUrl.searchParams.set(
|
|
1415
|
-
"post_logout_redirect_uri",
|
|
1416
|
-
postLogoutUri
|
|
1417
|
-
);
|
|
1408
|
+
logoutUrl.searchParams.set("post_logout_redirect_uri", postLogoutUri);
|
|
1418
1409
|
}
|
|
1419
1410
|
if (idTokenHint) {
|
|
1420
1411
|
logoutUrl.searchParams.set("id_token_hint", idTokenHint);
|