@nihplod/sso-sdk 1.2.1 → 1.3.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/dist/index.d.mts CHANGED
@@ -7,7 +7,7 @@
7
7
  * 整页跳转与刷新后登录态保留,关闭标签页自动清除;SSR / 隐私模式写入失败时
8
8
  * 降级为内存 Map。对需要多 Tab 共享 token 或 BFF/Confidential Client 场景,
9
9
  * 可通过 setTokenStorage() 注入 localStorage 实现(如 createSecureStorage({ persist: true }))。
10
- * - 临时数据(PKCE verifier / state / returnUrl / popup nonce):必须跨整页重定向存活
10
+ * - 临时数据(PKCE verifier / state / OIDC nonce / returnUrl / popup nonce):必须跨整页重定向存活
11
11
  * (login() 会 302 跳转到 SSO 中心再回来),因此默认写入 sessionStorage;
12
12
  * SSR 等无 sessionStorage 环境自动降级为内存 Map。
13
13
  *
@@ -110,6 +110,13 @@ interface SsoClientConfig {
110
110
  scopes?: string;
111
111
  /** RP-Initiated Logout 返回地址(可选)。不传时回退到 redirectUri */
112
112
  postLogoutRedirectUri?: string;
113
+ /**
114
+ * 服务端到服务端调用的内网地址(可选,如 http://127.0.0.1:3000)。
115
+ * 仅用于 discovery / token / userinfo / revocation 等服务器间请求;
116
+ * 浏览器跳转(authorize、end-session)始终使用 ssoBaseUrl 公网地址。
117
+ * 仅在 BFF/服务端场景配置;浏览器端(Public Client)不要配置。
118
+ */
119
+ serverBaseUrl?: string;
113
120
  }
114
121
  /** 用户信息 */
115
122
  interface SsoUser {
@@ -123,6 +130,8 @@ interface SsoUser {
123
130
  /** 生日(ISO 8601,需 birthday scope,未设置时为 null) */
124
131
  birthday?: string | null;
125
132
  membership_level?: string;
133
+ /** 累计消费金额(单位:元,需 membership scope,与 membership_level 同源下发) */
134
+ total_spent?: number;
126
135
  total_points?: number;
127
136
  }
128
137
  /** Token 响应 */
@@ -150,6 +159,8 @@ interface OidcDiscovery {
150
159
  }
151
160
  declare class SsoClient {
152
161
  readonly config: SsoClientConfig;
162
+ /** 服务器间调用的基准地址(未配置 serverBaseUrl 时等于 ssoBaseUrl) */
163
+ private readonly _serverBase;
153
164
  private _discovery;
154
165
  private _discoveryFetchedAt;
155
166
  private _refreshLock;
@@ -167,9 +178,9 @@ declare class SsoClient {
167
178
  private _getDiscovery;
168
179
  /** 获取 authorize 端点 URL(优先 Discovery,回退默认) */
169
180
  private _getAuthorizeEndpoint;
170
- /** 获取 token 端点 URL(优先 Discovery,回退默认) */
181
+ /** 获取 token 端点 URL(服务器间调用:serverBaseUrl 直连优先,其次 Discovery,回退默认) */
171
182
  private _getTokenEndpoint;
172
- /** 获取 userinfo 端点 URL(优先 Discovery,回退默认) */
183
+ /** 获取 userinfo 端点 URL(服务器间调用:serverBaseUrl 直连优先,其次 Discovery,回退默认) */
173
184
  private _getUserinfoEndpoint;
174
185
  /**
175
186
  * 开放重定向防护:仅保存相对路径或与当前页面同源的 returnUrl,
@@ -348,7 +359,7 @@ declare function generateState(): string;
348
359
  * SSO SDK 错误类型
349
360
  */
350
361
  /** SSO 错误码 */
351
- 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";
362
+ 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";
352
363
  /**
353
364
  * 将 OAuth 2.0 服务端 error 字段映射到 SsoErrorCode
354
365
  *
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * 整页跳转与刷新后登录态保留,关闭标签页自动清除;SSR / 隐私模式写入失败时
8
8
  * 降级为内存 Map。对需要多 Tab 共享 token 或 BFF/Confidential Client 场景,
9
9
  * 可通过 setTokenStorage() 注入 localStorage 实现(如 createSecureStorage({ persist: true }))。
10
- * - 临时数据(PKCE verifier / state / returnUrl / popup nonce):必须跨整页重定向存活
10
+ * - 临时数据(PKCE verifier / state / OIDC nonce / returnUrl / popup nonce):必须跨整页重定向存活
11
11
  * (login() 会 302 跳转到 SSO 中心再回来),因此默认写入 sessionStorage;
12
12
  * SSR 等无 sessionStorage 环境自动降级为内存 Map。
13
13
  *
@@ -110,6 +110,13 @@ interface SsoClientConfig {
110
110
  scopes?: string;
111
111
  /** RP-Initiated Logout 返回地址(可选)。不传时回退到 redirectUri */
112
112
  postLogoutRedirectUri?: string;
113
+ /**
114
+ * 服务端到服务端调用的内网地址(可选,如 http://127.0.0.1:3000)。
115
+ * 仅用于 discovery / token / userinfo / revocation 等服务器间请求;
116
+ * 浏览器跳转(authorize、end-session)始终使用 ssoBaseUrl 公网地址。
117
+ * 仅在 BFF/服务端场景配置;浏览器端(Public Client)不要配置。
118
+ */
119
+ serverBaseUrl?: string;
113
120
  }
114
121
  /** 用户信息 */
115
122
  interface SsoUser {
@@ -123,6 +130,8 @@ interface SsoUser {
123
130
  /** 生日(ISO 8601,需 birthday scope,未设置时为 null) */
124
131
  birthday?: string | null;
125
132
  membership_level?: string;
133
+ /** 累计消费金额(单位:元,需 membership scope,与 membership_level 同源下发) */
134
+ total_spent?: number;
126
135
  total_points?: number;
127
136
  }
128
137
  /** Token 响应 */
@@ -150,6 +159,8 @@ interface OidcDiscovery {
150
159
  }
151
160
  declare class SsoClient {
152
161
  readonly config: SsoClientConfig;
162
+ /** 服务器间调用的基准地址(未配置 serverBaseUrl 时等于 ssoBaseUrl) */
163
+ private readonly _serverBase;
153
164
  private _discovery;
154
165
  private _discoveryFetchedAt;
155
166
  private _refreshLock;
@@ -167,9 +178,9 @@ declare class SsoClient {
167
178
  private _getDiscovery;
168
179
  /** 获取 authorize 端点 URL(优先 Discovery,回退默认) */
169
180
  private _getAuthorizeEndpoint;
170
- /** 获取 token 端点 URL(优先 Discovery,回退默认) */
181
+ /** 获取 token 端点 URL(服务器间调用:serverBaseUrl 直连优先,其次 Discovery,回退默认) */
171
182
  private _getTokenEndpoint;
172
- /** 获取 userinfo 端点 URL(优先 Discovery,回退默认) */
183
+ /** 获取 userinfo 端点 URL(服务器间调用:serverBaseUrl 直连优先,其次 Discovery,回退默认) */
173
184
  private _getUserinfoEndpoint;
174
185
  /**
175
186
  * 开放重定向防护:仅保存相对路径或与当前页面同源的 returnUrl,
@@ -348,7 +359,7 @@ declare function generateState(): string;
348
359
  * SSO SDK 错误类型
349
360
  */
350
361
  /** SSO 错误码 */
351
- 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";
362
+ 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";
352
363
  /**
353
364
  * 将 OAuth 2.0 服务端 error 字段映射到 SsoErrorCode
354
365
  *
package/dist/index.js CHANGED
@@ -136,6 +136,7 @@ var STORAGE_PREFIX = "nihplod_sso_";
136
136
  var TOKEN_KEY = "token";
137
137
  var VERIFIER_KEY_PREFIX = "pkce_verifier_";
138
138
  var STATE_KEY = "oauth_state";
139
+ var NONCE_KEY = "oidc_nonce";
139
140
  var RETURN_URL_KEY = "return_url";
140
141
  var LOGOUT_STATE_KEY = "logout_state";
141
142
  function buildKey(base, clientId) {
@@ -247,6 +248,15 @@ function getOAuthState(clientId) {
247
248
  function removeOAuthState(clientId) {
248
249
  _transient.remove(buildKey(STATE_KEY, clientId));
249
250
  }
251
+ function saveOAuthNonce(nonce, clientId) {
252
+ _transient.set(buildKey(NONCE_KEY, clientId), nonce);
253
+ }
254
+ function getOAuthNonce(clientId) {
255
+ return _transient.get(buildKey(NONCE_KEY, clientId));
256
+ }
257
+ function removeOAuthNonce(clientId) {
258
+ _transient.remove(buildKey(NONCE_KEY, clientId));
259
+ }
250
260
  function saveLogoutState(state, clientId) {
251
261
  _transient.set(buildKey(LOGOUT_STATE_KEY, clientId), state);
252
262
  }
@@ -269,6 +279,7 @@ function clearAllSsoData(clientId) {
269
279
  if (clientId) {
270
280
  removeTokenData(clientId);
271
281
  removeOAuthState(clientId);
282
+ removeOAuthNonce(clientId);
272
283
  removeReturnUrl(clientId);
273
284
  removeLogoutState(clientId);
274
285
  removePkceVerifier(clientId);
@@ -277,6 +288,7 @@ function clearAllSsoData(clientId) {
277
288
  }
278
289
  removeTokenData();
279
290
  removeOAuthState();
291
+ removeOAuthNonce();
280
292
  removeReturnUrl();
281
293
  removeLogoutState();
282
294
  const prefix = STORAGE_PREFIX + VERIFIER_KEY_PREFIX;
@@ -367,18 +379,21 @@ function normalizeIssuer(url) {
367
379
  var cachedJwks = null;
368
380
  var cachedDiscovery = null;
369
381
  var JWKS_CACHE_TTL_MS = 5 * 60 * 1e3;
382
+ var FETCH_TIMEOUT_MS = 1e4;
370
383
  async function fetchDiscoveryDoc(baseUrl) {
371
384
  const now = Date.now();
372
385
  if (cachedDiscovery && cachedDiscovery.baseUrl === baseUrl && now - cachedDiscovery.fetchedAt < JWKS_CACHE_TTL_MS) {
373
386
  return cachedDiscovery.doc;
374
387
  }
375
388
  try {
376
- const res = await fetch(`${baseUrl}/api/oauth/.well-known/openid-configuration`);
377
- const doc = res.ok ? await res.json() : null;
389
+ const res = await fetch(`${baseUrl}/api/oauth/.well-known/openid-configuration`, {
390
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
391
+ });
392
+ if (!res.ok) return null;
393
+ const doc = await res.json();
378
394
  cachedDiscovery = { baseUrl, doc, fetchedAt: now };
379
395
  return doc;
380
396
  } catch {
381
- cachedDiscovery = { baseUrl, doc: null, fetchedAt: now };
382
397
  return null;
383
398
  }
384
399
  }
@@ -390,7 +405,10 @@ async function fetchJwks(baseUrl, options = {}) {
390
405
  const discovery = await fetchDiscoveryDoc(baseUrl);
391
406
  const jwksUri = discovery?.jwks_uri || `${baseUrl}/api/oauth/jwks`;
392
407
  try {
393
- const res = await fetch(jwksUri, options.forceRefresh ? { cache: "no-cache" } : void 0);
408
+ const res = await fetch(jwksUri, {
409
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
410
+ ...options.forceRefresh ? { cache: "no-cache" } : {}
411
+ });
394
412
  if (!res.ok) return null;
395
413
  const jwks = await res.json();
396
414
  cachedJwks = { baseUrl, jwks, fetchedAt: now };
@@ -437,7 +455,7 @@ async function computeAtHash(accessToken) {
437
455
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
438
456
  }
439
457
  async function validateIdToken(idToken, accessToken, expectedIssuer, expectedClientId, options = {}) {
440
- const { rejectHs256WhenRs256Available = true } = options;
458
+ const { rejectHs256WhenRs256Available = true, expectedNonce } = options;
441
459
  const header = decodeJwtHeader(idToken);
442
460
  if (!header) {
443
461
  throw new SsoError("id_token_invalid", "ID Token \u683C\u5F0F\u9519\u8BEF");
@@ -527,6 +545,12 @@ async function validateIdToken(idToken, accessToken, expectedIssuer, expectedCli
527
545
  throw new SsoError("id_token_at_hash_mismatch", "ID Token at_hash \u4E0D\u5339\u914D");
528
546
  }
529
547
  }
548
+ if (expectedNonce !== void 0) {
549
+ const tokenNonce = typeof payload.nonce === "string" ? payload.nonce : "";
550
+ if (!tokenNonce || !timingSafeEqualString(expectedNonce, tokenNonce)) {
551
+ throw new SsoError("id_token_nonce_mismatch", "ID Token nonce \u4E0D\u5339\u914D");
552
+ }
553
+ }
530
554
  return { sub: payload.sub };
531
555
  }
532
556
 
@@ -541,6 +565,7 @@ var _SsoClient = class _SsoClient {
541
565
  if (!config.ssoBaseUrl) throw new SsoError("invalid_config", "ssoBaseUrl \u4E0D\u80FD\u4E3A\u7A7A");
542
566
  const base = config.ssoBaseUrl.replace(/\/+$/, "");
543
567
  this.config = { ...config, ssoBaseUrl: base };
568
+ this._serverBase = (config.serverBaseUrl ?? base).replace(/\/+$/, "");
544
569
  }
545
570
  // ============================================
546
571
  // 内部方法
@@ -556,7 +581,7 @@ var _SsoClient = class _SsoClient {
556
581
  if (this._discovery && now - this._discoveryFetchedAt < _SsoClient.DISCOVERY_TTL_MS) {
557
582
  return this._discovery;
558
583
  }
559
- const url = `${this.config.ssoBaseUrl}/api/oauth/.well-known/openid-configuration`;
584
+ const url = `${this._serverBase}/api/oauth/.well-known/openid-configuration`;
560
585
  try {
561
586
  const controller = new AbortController();
562
587
  const timeoutId = setTimeout(
@@ -587,14 +612,16 @@ var _SsoClient = class _SsoClient {
587
612
  if (d) return d.authorization_endpoint;
588
613
  return `${this.config.ssoBaseUrl}/api/oauth/authorize`;
589
614
  }
590
- /** 获取 token 端点 URL(优先 Discovery,回退默认) */
615
+ /** 获取 token 端点 URL(服务器间调用:serverBaseUrl 直连优先,其次 Discovery,回退默认) */
591
616
  async _getTokenEndpoint() {
617
+ if (this.config.serverBaseUrl) return `${this._serverBase}/api/oauth/token`;
592
618
  const d = await this._getDiscovery();
593
619
  if (d) return d.token_endpoint;
594
620
  return `${this.config.ssoBaseUrl}/api/oauth/token`;
595
621
  }
596
- /** 获取 userinfo 端点 URL(优先 Discovery,回退默认) */
622
+ /** 获取 userinfo 端点 URL(服务器间调用:serverBaseUrl 直连优先,其次 Discovery,回退默认) */
597
623
  async _getUserinfoEndpoint() {
624
+ if (this.config.serverBaseUrl) return `${this._serverBase}/api/oauth/userinfo`;
598
625
  const d = await this._getDiscovery();
599
626
  if (d) return d.userinfo_endpoint;
600
627
  return `${this.config.ssoBaseUrl}/api/oauth/userinfo`;
@@ -630,8 +657,10 @@ var _SsoClient = class _SsoClient {
630
657
  const verifier = generateCodeVerifier();
631
658
  const challenge = await generateCodeChallenge(verifier);
632
659
  const state = generateState();
660
+ const nonce = generateState();
633
661
  savePkceVerifier(this.config.clientId, verifier);
634
662
  saveOAuthState(state, this.config.clientId);
663
+ saveOAuthNonce(nonce, this.config.clientId);
635
664
  if (returnUrl) {
636
665
  this._saveReturnUrlIfTrusted(returnUrl);
637
666
  }
@@ -642,6 +671,7 @@ var _SsoClient = class _SsoClient {
642
671
  params.set("redirect_uri", this.config.redirectUri);
643
672
  params.set("scope", this.config.scopes || "openid profile");
644
673
  params.set("state", state);
674
+ params.set("nonce", nonce);
645
675
  params.set("code_challenge", challenge);
646
676
  params.set("code_challenge_method", "S256");
647
677
  window.location.href = `${authorizeEndpoint}?${params.toString()}`;
@@ -659,8 +689,10 @@ var _SsoClient = class _SsoClient {
659
689
  const verifier = generateCodeVerifier();
660
690
  const challenge = await generateCodeChallenge(verifier);
661
691
  const state = generateState();
692
+ const nonce = generateState();
662
693
  savePkceVerifier(this.config.clientId, verifier);
663
694
  saveOAuthState(state, this.config.clientId);
695
+ saveOAuthNonce(nonce, this.config.clientId);
664
696
  if (returnUrl) this._saveReturnUrlIfTrusted(returnUrl);
665
697
  const authorizeEndpoint = await this._getAuthorizeEndpoint();
666
698
  const params = new URLSearchParams();
@@ -669,6 +701,7 @@ var _SsoClient = class _SsoClient {
669
701
  params.set("redirect_uri", this.config.redirectUri);
670
702
  params.set("scope", this.config.scopes || "openid profile");
671
703
  params.set("state", state);
704
+ params.set("nonce", nonce);
672
705
  params.set("code_challenge", challenge);
673
706
  params.set("code_challenge_method", "S256");
674
707
  return `${authorizeEndpoint}?${params.toString()}`;
@@ -710,9 +743,11 @@ var _SsoClient = class _SsoClient {
710
743
  const verifier = generateCodeVerifier();
711
744
  const challenge = await generateCodeChallenge(verifier);
712
745
  const state = generateState();
746
+ const nonce = generateState();
713
747
  const popupNonce = generateState();
714
748
  savePkceVerifier(this.config.clientId, verifier);
715
749
  saveOAuthState(state, this.config.clientId);
750
+ saveOAuthNonce(nonce, this.config.clientId);
716
751
  savePkceVerifier(`${this.config.clientId}_popup_nonce`, popupNonce);
717
752
  if (options.returnUrl) {
718
753
  this._saveReturnUrlIfTrusted(options.returnUrl);
@@ -724,6 +759,7 @@ var _SsoClient = class _SsoClient {
724
759
  params.set("redirect_uri", this.config.redirectUri);
725
760
  params.set("scope", this.config.scopes || "openid profile");
726
761
  params.set("state", state);
762
+ params.set("nonce", nonce);
727
763
  params.set("code_challenge", challenge);
728
764
  params.set("code_challenge_method", "S256");
729
765
  params.set("popup_nonce", popupNonce);
@@ -748,6 +784,7 @@ var _SsoClient = class _SsoClient {
748
784
  if (!popup) {
749
785
  removePkceVerifier(this.config.clientId);
750
786
  removeOAuthState(this.config.clientId);
787
+ removeOAuthNonce(this.config.clientId);
751
788
  removePkceVerifier(`${this.config.clientId}_popup_nonce`);
752
789
  throw new SsoError(
753
790
  "popup_blocked",
@@ -767,6 +804,13 @@ var _SsoClient = class _SsoClient {
767
804
  const savedNonce = getPkceVerifier(`${this.config.clientId}_popup_nonce`);
768
805
  if (!savedNonce || !timingSafeEqualString(event.data.nonce ?? "", savedNonce)) return;
769
806
  removePkceVerifier(`${this.config.clientId}_popup_nonce`);
807
+ try {
808
+ event.source?.postMessage(
809
+ { type: "nihplod_sso_popup_ack", nonce: savedNonce },
810
+ event.origin
811
+ );
812
+ } catch {
813
+ }
770
814
  completed = true;
771
815
  cleanup();
772
816
  if (popup && !popup.closed) {
@@ -798,6 +842,7 @@ var _SsoClient = class _SsoClient {
798
842
  } catch (err) {
799
843
  removePkceVerifier(this.config.clientId);
800
844
  removeOAuthState(this.config.clientId);
845
+ removeOAuthNonce(this.config.clientId);
801
846
  removePkceVerifier(`${this.config.clientId}_popup_nonce`);
802
847
  throw err;
803
848
  }
@@ -817,6 +862,7 @@ var _SsoClient = class _SsoClient {
817
862
  const error = params.get("error");
818
863
  if (error) {
819
864
  removeOAuthState(this.config.clientId);
865
+ removeOAuthNonce(this.config.clientId);
820
866
  removePkceVerifier(this.config.clientId);
821
867
  const desc = params.get("error_description") || error;
822
868
  throw new SsoError(mapOAuthErrorToSsoCode(error), `\u6388\u6743\u5931\u8D25: ${desc}`);
@@ -825,18 +871,21 @@ var _SsoClient = class _SsoClient {
825
871
  const returnedState = params.get("state");
826
872
  if (!code) {
827
873
  removeOAuthState(this.config.clientId);
874
+ removeOAuthNonce(this.config.clientId);
828
875
  removePkceVerifier(this.config.clientId);
829
876
  throw new SsoError("token_request_failed", "\u56DE\u8C03 URL \u4E2D\u7F3A\u5C11 authorization code");
830
877
  }
831
878
  const savedState = getOAuthState(this.config.clientId);
832
879
  if (!savedState || !timingSafeEqualString(savedState, returnedState ?? "")) {
833
880
  removeOAuthState(this.config.clientId);
881
+ removeOAuthNonce(this.config.clientId);
834
882
  removePkceVerifier(this.config.clientId);
835
883
  throw new SsoError(
836
884
  "state_mismatch",
837
885
  "State \u53C2\u6570\u4E0D\u5339\u914D\uFF0C\u53EF\u80FD\u5B58\u5728 CSRF \u653B\u51FB"
838
886
  );
839
887
  }
888
+ const expectedNonce = getOAuthNonce(this.config.clientId) ?? void 0;
840
889
  const verifier = getPkceVerifier(this.config.clientId);
841
890
  if (!verifier) {
842
891
  throw new SsoError(
@@ -883,7 +932,8 @@ var _SsoClient = class _SsoClient {
883
932
  data.id_token,
884
933
  data.access_token,
885
934
  this.config.ssoBaseUrl,
886
- this.config.clientId
935
+ this.config.clientId,
936
+ { expectedNonce }
887
937
  );
888
938
  } catch (err) {
889
939
  removeTokenData(this.config.clientId);
@@ -891,6 +941,7 @@ var _SsoClient = class _SsoClient {
891
941
  }
892
942
  }
893
943
  removeOAuthState(this.config.clientId);
944
+ removeOAuthNonce(this.config.clientId);
894
945
  removePkceVerifier(this.config.clientId);
895
946
  const now = Date.now();
896
947
  const tokenData = {
@@ -1071,8 +1122,8 @@ var _SsoClient = class _SsoClient {
1071
1122
  clearAllSsoData(this.config.clientId);
1072
1123
  if (refreshToken && this.config.clientId) {
1073
1124
  try {
1074
- const discovery = await this._getDiscovery();
1075
- const revokeUrl = discovery?.revocation_endpoint || `${this.config.ssoBaseUrl}/api/oauth/revoke`;
1125
+ const discovery = this.config.serverBaseUrl ? null : await this._getDiscovery();
1126
+ const revokeUrl = this.config.serverBaseUrl ? `${this._serverBase}/api/oauth/revoke` : discovery?.revocation_endpoint || `${this.config.ssoBaseUrl}/api/oauth/revoke`;
1076
1127
  const revokeBody = new URLSearchParams({
1077
1128
  token: refreshToken,
1078
1129
  token_type_hint: "refresh_token",