@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.
@@ -86,6 +86,7 @@ var INTROSPECT_CACHE_TTL_MS = 3e4;
86
86
  var INTROSPECT_CACHE_MAX_ENTRIES = 500;
87
87
  var INTROSPECT_TIMEOUT_MS = 5e3;
88
88
  var warnedIntrospectClientAuthFailure = false;
89
+ var warnedIntrospectRateLimited = false;
89
90
  async function introspectCacheKey(token, clientId) {
90
91
  const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token));
91
92
  const hex = Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
@@ -136,6 +137,15 @@ async function introspectAccessToken(token, ssoBaseUrl, clientId, clientSecret)
136
137
  signal: AbortSignal.timeout(INTROSPECT_TIMEOUT_MS)
137
138
  });
138
139
  if (!res.ok) {
140
+ if (res.status === 429) {
141
+ if (!warnedIntrospectRateLimited) {
142
+ warnedIntrospectRateLimited = true;
143
+ console.warn(
144
+ "[SSO SDK] introspection \u88AB\u9650\u6D41\uFF08HTTP 429\uFF09\uFF1A\u8BF7\u4E3A\u5B50\u7AD9 BFF \u914D\u7F6E\u72EC\u7ACB\u7684 client \u7EA7\u914D\u989D\uFF0C\u6216\u964D\u4F4E middleware introspection \u9891\u7387\u3002\u672C\u6B21\u6309 unreachable \u5904\u7406\uFF08\u9ED8\u8BA4 fail-open\uFF09\u3002"
145
+ );
146
+ }
147
+ return "unreachable";
148
+ }
139
149
  if (res.status !== 401 && res.status !== 403) {
140
150
  return "unreachable";
141
151
  }
@@ -377,7 +387,19 @@ async function fetchJwks(baseUrl, options = {}) {
377
387
  return cachedJwks.jwks;
378
388
  }
379
389
  const discovery = await fetchDiscoveryDoc(baseUrl);
380
- const jwksUri = discovery?.jwks_uri || `${baseUrl}/api/oauth/jwks`;
390
+ let jwksUri = `${baseUrl}/api/oauth/jwks`;
391
+ if (discovery?.jwks_uri) {
392
+ try {
393
+ if (new URL(discovery.jwks_uri).origin === new URL(baseUrl).origin) {
394
+ jwksUri = discovery.jwks_uri;
395
+ } else {
396
+ console.warn(
397
+ "[SSO SDK] Discovery \u7684 jwks_uri \u4E0E ssoBaseUrl \u4E0D\u540C\u6E90\uFF0C\u5DF2\u5FFD\u7565\u5E76\u4F7F\u7528\u9ED8\u8BA4 JWKS \u7AEF\u70B9"
398
+ );
399
+ }
400
+ } catch {
401
+ }
402
+ }
381
403
  try {
382
404
  const res = await fetch(jwksUri, {
383
405
  signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
@@ -648,8 +670,14 @@ function createCallbackRouteHandler(config) {
648
670
  const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
649
671
  const secureCookies = !insecureLocalDev;
650
672
  const pickName = (explicit, fallback) => insecureLocalDev ? toInsecureCookieName(explicit ?? fallback) : explicit ?? fallback;
651
- const accessTokenCookieName = pickName(config.accessTokenCookieName, DEFAULT_ACCESS_TOKEN_COOKIE_NAME);
652
- const refreshTokenCookieName = pickName(config.refreshTokenCookieName, DEFAULT_REFRESH_TOKEN_COOKIE_NAME);
673
+ const accessTokenCookieName = pickName(
674
+ config.accessTokenCookieName,
675
+ DEFAULT_ACCESS_TOKEN_COOKIE_NAME
676
+ );
677
+ const refreshTokenCookieName = pickName(
678
+ config.refreshTokenCookieName,
679
+ DEFAULT_REFRESH_TOKEN_COOKIE_NAME
680
+ );
653
681
  const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
654
682
  const stateCookieName = pickName(config.stateCookieName, DEFAULT_STATE_COOKIE_NAME);
655
683
  const nonceCookieName = pickName(config.nonceCookieName, DEFAULT_NONCE_COOKIE_NAME);
@@ -683,13 +711,7 @@ function createCallbackRouteHandler(config) {
683
711
  const error = searchParams.get("error");
684
712
  if (error) {
685
713
  const desc = searchParams.get("error_description") || error;
686
- return buildErrorResponse(
687
- request,
688
- 400,
689
- "authorization_failed",
690
- desc,
691
- config.renderErrorPage
692
- );
714
+ return buildErrorResponse(request, 400, "authorization_failed", desc, config.renderErrorPage);
693
715
  }
694
716
  const code = searchParams.get("code");
695
717
  const returnedState = searchParams.get("state");
@@ -743,6 +765,20 @@ function createCallbackRouteHandler(config) {
743
765
  config.renderErrorPage
744
766
  );
745
767
  }
768
+ const returnedIss = searchParams.get("iss");
769
+ if (returnedIss) {
770
+ const discovery = await fetchDiscoveryDoc(normalizedBase);
771
+ const expectedIssuer = (discovery?.issuer || normalizedBase).replace(/\/+$/, "");
772
+ if (returnedIss.replace(/\/+$/, "") !== expectedIssuer) {
773
+ return buildErrorResponse(
774
+ request,
775
+ 400,
776
+ "issuer_mismatch",
777
+ "\u6388\u6743\u54CD\u5E94 issuer \u4E0D\u5339\u914D\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u767B\u5F55",
778
+ config.renderErrorPage
779
+ );
780
+ }
781
+ }
746
782
  const expectedNonce = readTransientCookie(nonceCookieName);
747
783
  const verifier = readTransientCookie(verifierCookieName);
748
784
  if (!verifier) {
@@ -766,35 +802,18 @@ function createCallbackRouteHandler(config) {
766
802
  }
767
803
  let res = null;
768
804
  let lastError;
769
- const maxRetries = 1;
770
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
771
- if (attempt > 0) {
772
- await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
773
- }
774
- try {
775
- res = await fetchWithTimeout(
776
- tokenEndpoint,
777
- {
778
- method: "POST",
779
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
780
- body: body.toString()
781
- },
782
- 1e4
783
- );
784
- lastError = null;
785
- break;
786
- } catch (err) {
787
- lastError = err;
788
- if (attempt >= maxRetries) {
789
- return buildErrorResponse(
790
- request,
791
- 502,
792
- "server_error",
793
- "\u767B\u5F55\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF08Token \u8BF7\u6C42\u5931\u8D25\uFF09\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
794
- config.renderErrorPage
795
- );
796
- }
797
- }
805
+ try {
806
+ res = await fetchWithTimeout(
807
+ tokenEndpoint,
808
+ {
809
+ method: "POST",
810
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
811
+ body: body.toString()
812
+ },
813
+ 1e4
814
+ );
815
+ } catch (err) {
816
+ lastError = err;
798
817
  }
799
818
  if (lastError || !res) {
800
819
  return buildErrorResponse(
@@ -905,8 +924,169 @@ function createCallbackRouteHandler(config) {
905
924
  }
906
925
  }
907
926
 
908
- // src/next/logout.ts
927
+ // src/next/refresh.ts
909
928
  import { NextResponse as NextResponse3 } from "next/server";
929
+ function noStoreJson(body, status = 200) {
930
+ return NextResponse3.json(body, {
931
+ status,
932
+ headers: { "Cache-Control": "no-store", Pragma: "no-cache" }
933
+ });
934
+ }
935
+ function clearAuthCookies(response, names, secureCookies) {
936
+ response.cookies.set(names.access, "", getHostCookieOptions(0, secureCookies));
937
+ response.cookies.set(names.refresh, "", getHostCookieOptions(0, secureCookies));
938
+ response.cookies.set(names.idToken, "", getHostCookieOptions(0, secureCookies));
939
+ return response;
940
+ }
941
+ function createRefreshRouteHandler(config) {
942
+ const {
943
+ clientId,
944
+ ssoBaseUrl,
945
+ redirectUri,
946
+ clientSecret,
947
+ insecureLocalDev: insecureLocalDevOpt = false,
948
+ debug = false
949
+ } = config;
950
+ const insecureLocalDev = resolveInsecureLocalDev(insecureLocalDevOpt, ssoBaseUrl);
951
+ const secureCookies = !insecureLocalDev;
952
+ const pickName = (explicit, fallback) => insecureLocalDev ? toInsecureCookieName(explicit ?? fallback) : explicit ?? fallback;
953
+ const accessTokenCookieName = pickName(
954
+ config.accessTokenCookieName,
955
+ DEFAULT_ACCESS_TOKEN_COOKIE_NAME
956
+ );
957
+ const refreshTokenCookieName = pickName(
958
+ config.refreshTokenCookieName,
959
+ DEFAULT_REFRESH_TOKEN_COOKIE_NAME
960
+ );
961
+ const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
962
+ const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
963
+ const normalizedServerBase = (config.serverBaseUrl ?? ssoBaseUrl).replace(/\/+$/, "");
964
+ const callbackOrigin = new URL(redirectUri).origin;
965
+ const cookieNames = {
966
+ access: accessTokenCookieName,
967
+ refresh: refreshTokenCookieName,
968
+ idToken: idTokenCookieName
969
+ };
970
+ return async function POST(request) {
971
+ const requestOrigin = request.headers.get("origin");
972
+ if (requestOrigin && requestOrigin !== callbackOrigin) {
973
+ return noStoreJson({ error: "forbidden", error_description: "\u8DE8\u7AD9\u8BF7\u6C42\u88AB\u62D2\u7EDD" }, 403);
974
+ }
975
+ const secFetchSite = request.headers.get("sec-fetch-site");
976
+ if (!requestOrigin && secFetchSite && secFetchSite !== "same-origin" && secFetchSite !== "none") {
977
+ return noStoreJson({ error: "forbidden", error_description: "\u8DE8\u7AD9\u8BF7\u6C42\u88AB\u62D2\u7EDD" }, 403);
978
+ }
979
+ const refreshToken = request.cookies.get(refreshTokenCookieName)?.value;
980
+ if (!refreshToken) {
981
+ return noStoreJson({ error: "no_refresh_token", error_description: "\u7F3A\u5C11 refresh_token" }, 401);
982
+ }
983
+ const body = new URLSearchParams({
984
+ grant_type: "refresh_token",
985
+ refresh_token: refreshToken,
986
+ client_id: clientId
987
+ });
988
+ if (clientSecret) body.set("client_secret", clientSecret);
989
+ let res = null;
990
+ let lastError;
991
+ try {
992
+ res = await fetchWithTimeout(
993
+ `${normalizedServerBase}/api/oauth/token`,
994
+ {
995
+ method: "POST",
996
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
997
+ body: body.toString()
998
+ },
999
+ 1e4
1000
+ );
1001
+ } catch (err) {
1002
+ lastError = err;
1003
+ }
1004
+ if (lastError || !res) {
1005
+ if (debug) console.warn("[SSO SDK] refresh: token \u8BF7\u6C42\u5931\u8D25", lastError);
1006
+ return noStoreJson(
1007
+ { error: "server_error", error_description: "\u5237\u65B0\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528" },
1008
+ 502
1009
+ );
1010
+ }
1011
+ let data = {};
1012
+ try {
1013
+ data = await res.json();
1014
+ } catch {
1015
+ }
1016
+ if (!res.ok) {
1017
+ const error = typeof data.error === "string" ? data.error : "";
1018
+ const status = res.status === 429 ? 429 : res.status >= 500 ? 502 : 401;
1019
+ const response2 = noStoreJson(
1020
+ {
1021
+ error: error || "refresh_failed",
1022
+ error_description: data.error_description || "\u5237\u65B0\u767B\u5F55\u6001\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55"
1023
+ },
1024
+ status
1025
+ );
1026
+ if (res.status === 400 || res.status === 401) {
1027
+ return clearAuthCookies(response2, cookieNames, secureCookies);
1028
+ }
1029
+ return response2;
1030
+ }
1031
+ if (!data.access_token || typeof data.expires_in !== "number" || !Number.isFinite(data.expires_in) || data.expires_in <= 0) {
1032
+ return clearAuthCookies(
1033
+ noStoreJson({ error: "server_error", error_description: "\u5237\u65B0\u54CD\u5E94\u4E0D\u5B8C\u6574" }, 502),
1034
+ cookieNames,
1035
+ secureCookies
1036
+ );
1037
+ }
1038
+ if (data.id_token) {
1039
+ try {
1040
+ await validateIdToken(
1041
+ data.id_token,
1042
+ data.access_token,
1043
+ normalizedBase,
1044
+ clientId
1045
+ );
1046
+ } catch (err) {
1047
+ if (debug) console.warn("[SSO SDK] refresh: ID Token \u6821\u9A8C\u5931\u8D25", err);
1048
+ return clearAuthCookies(
1049
+ noStoreJson({ error: "id_token_invalid", error_description: "\u767B\u5F55\u6821\u9A8C\u5931\u8D25" }, 401),
1050
+ cookieNames,
1051
+ secureCookies
1052
+ );
1053
+ }
1054
+ }
1055
+ const response = noStoreJson({ success: true, expires_in: data.expires_in });
1056
+ response.cookies.set(accessTokenCookieName, data.access_token, {
1057
+ ...getHostCookieOptions(data.expires_in, secureCookies)
1058
+ });
1059
+ response.cookies.set(refreshTokenCookieName, data.refresh_token || refreshToken, {
1060
+ ...getHostCookieOptions(
1061
+ typeof data.refresh_expires_in === "number" && data.refresh_expires_in > 0 ? data.refresh_expires_in : 30 * 24 * 60 * 60,
1062
+ secureCookies
1063
+ )
1064
+ });
1065
+ if (data.id_token) {
1066
+ let idTokenMaxAge = 3600;
1067
+ try {
1068
+ const payload = JSON.parse(
1069
+ Buffer.from(data.id_token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"), "base64").toString(
1070
+ "utf8"
1071
+ )
1072
+ );
1073
+ if (typeof payload.exp === "number") {
1074
+ idTokenMaxAge = payload.exp - Math.floor(Date.now() / 1e3);
1075
+ }
1076
+ } catch {
1077
+ }
1078
+ if (idTokenMaxAge > 0) {
1079
+ response.cookies.set(idTokenCookieName, data.id_token, {
1080
+ ...getHostCookieOptions(idTokenMaxAge, secureCookies)
1081
+ });
1082
+ }
1083
+ }
1084
+ return response;
1085
+ };
1086
+ }
1087
+
1088
+ // src/next/logout.ts
1089
+ import { NextResponse as NextResponse4 } from "next/server";
910
1090
 
911
1091
  // src/core/discovery.ts
912
1092
  var DISCOVERY_CACHE_TTL_MS = 5 * 60 * 1e3;
@@ -977,6 +1157,14 @@ function generateRandomString2(length) {
977
1157
  }
978
1158
  return result;
979
1159
  }
1160
+ function trustedSameOriginEndpoint(endpoint, baseUrl) {
1161
+ if (!endpoint) return null;
1162
+ try {
1163
+ return new URL(endpoint).origin === new URL(baseUrl).origin ? endpoint : null;
1164
+ } catch {
1165
+ return null;
1166
+ }
1167
+ }
980
1168
  function createLogoutRouteHandler(config) {
981
1169
  const {
982
1170
  clientId,
@@ -1015,31 +1203,31 @@ function createLogoutRouteHandler(config) {
1015
1203
  if (returnedState) {
1016
1204
  const savedState = request.cookies.get(logoutStateCookieName)?.value;
1017
1205
  if (!savedState || !timingSafeEqualString(savedState, returnedState)) {
1018
- return NextResponse3.json(
1206
+ return NextResponse4.json(
1019
1207
  { error: "invalid_request", error_description: "Logout state \u4E0D\u5339\u914D" },
1020
1208
  { status: 400 }
1021
1209
  );
1022
1210
  }
1023
- const res = NextResponse3.redirect(callbackOrigin + "/");
1211
+ const res = NextResponse4.redirect(callbackOrigin + "/");
1024
1212
  res.cookies.set(logoutStateCookieName, "", getHostCookieOptions(0, secureCookies));
1025
1213
  return res;
1026
1214
  }
1027
1215
  if (request.method === "GET") {
1028
- return new NextResponse3(buildLogoutConfirmHtml(), {
1216
+ return new NextResponse4(buildLogoutConfirmHtml(), {
1029
1217
  status: 200,
1030
1218
  headers: { "content-type": "text/html; charset=utf-8" }
1031
1219
  });
1032
1220
  }
1033
1221
  const requestOrigin = request.headers.get("origin");
1034
1222
  if (requestOrigin && requestOrigin !== callbackOrigin) {
1035
- return NextResponse3.json(
1223
+ return NextResponse4.json(
1036
1224
  { error: "forbidden", error_description: "\u8DE8\u7AD9\u8BF7\u6C42\u88AB\u62D2\u7EDD" },
1037
1225
  { status: 403 }
1038
1226
  );
1039
1227
  }
1040
1228
  const secFetchSite = request.headers.get("sec-fetch-site");
1041
1229
  if (!requestOrigin && secFetchSite && secFetchSite !== "same-origin" && secFetchSite !== "none") {
1042
- return NextResponse3.json(
1230
+ return NextResponse4.json(
1043
1231
  { error: "forbidden", error_description: "\u8DE8\u7AD9\u8BF7\u6C42\u88AB\u62D2\u7EDD" },
1044
1232
  { status: 403 }
1045
1233
  );
@@ -1055,7 +1243,7 @@ function createLogoutRouteHandler(config) {
1055
1243
  if (refreshToken) {
1056
1244
  try {
1057
1245
  const discovery = config.serverBaseUrl ? null : await fetchDiscoveryCached(normalizedServerBase);
1058
- const revokeUrl = config.serverBaseUrl ? `${normalizedServerBase}/api/oauth/revoke` : discovery?.revocation_endpoint || `${normalizedBase}/api/oauth/revoke`;
1246
+ const revokeUrl = (config.serverBaseUrl ? `${normalizedServerBase}/api/oauth/revoke` : trustedSameOriginEndpoint(discovery?.revocation_endpoint, normalizedBase)) || `${normalizedBase}/api/oauth/revoke`;
1059
1247
  const body = new URLSearchParams({
1060
1248
  token: refreshToken,
1061
1249
  token_type_hint: "refresh_token",
@@ -1064,12 +1252,20 @@ function createLogoutRouteHandler(config) {
1064
1252
  if (clientSecret) {
1065
1253
  body.set("client_secret", clientSecret);
1066
1254
  }
1067
- await fetch(revokeUrl, {
1255
+ const doRevoke = () => fetch(revokeUrl, {
1068
1256
  method: "POST",
1069
1257
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
1070
1258
  body: body.toString(),
1071
1259
  signal: AbortSignal.timeout(3e3)
1072
1260
  });
1261
+ let revokeRes = await doRevoke();
1262
+ if (!revokeRes.ok && (revokeRes.status >= 500 || revokeRes.status === 429)) {
1263
+ await new Promise((resolve) => setTimeout(resolve, 300));
1264
+ revokeRes = await doRevoke();
1265
+ }
1266
+ if (!revokeRes.ok) {
1267
+ console.warn(`[SSO SDK] \u767B\u51FA\u64A4\u9500 refresh_token \u5931\u8D25\uFF08HTTP ${revokeRes.status}\uFF09`);
1268
+ }
1073
1269
  } catch {
1074
1270
  }
1075
1271
  }
@@ -1101,7 +1297,7 @@ function createLogoutRouteHandler(config) {
1101
1297
  };
1102
1298
  if (effectiveScope === "global") {
1103
1299
  const discovery = await fetchDiscoveryCached(normalizedServerBase);
1104
- const endSessionEndpoint = discovery?.end_session_endpoint || `${normalizedBase}/api/oauth/end-session`;
1300
+ const endSessionEndpoint = trustedSameOriginEndpoint(discovery?.end_session_endpoint, normalizedBase) || `${normalizedBase}/api/oauth/end-session`;
1105
1301
  const logoutUrl = new URL(endSessionEndpoint);
1106
1302
  logoutUrl.searchParams.set("client_id", clientId);
1107
1303
  logoutUrl.searchParams.set(
@@ -1113,18 +1309,18 @@ function createLogoutRouteHandler(config) {
1113
1309
  }
1114
1310
  const logoutState = generateRandomString2(32);
1115
1311
  logoutUrl.searchParams.set("state", logoutState);
1116
- const res = clearCookies(NextResponse3.redirect(logoutUrl.toString()));
1312
+ const res = clearCookies(NextResponse4.redirect(logoutUrl.toString()));
1117
1313
  res.cookies.set(logoutStateCookieName, logoutState, getHostCookieOptions(600, secureCookies));
1118
1314
  return res;
1119
1315
  }
1120
1316
  return clearCookies(
1121
- NextResponse3.redirect(callbackOrigin + "/")
1317
+ NextResponse4.redirect(callbackOrigin + "/")
1122
1318
  );
1123
1319
  };
1124
1320
  }
1125
1321
 
1126
1322
  // src/next/backchannel-logout.ts
1127
- import { NextResponse as NextResponse4 } from "next/server";
1323
+ import { NextResponse as NextResponse5 } from "next/server";
1128
1324
 
1129
1325
  // src/core/logout-token.ts
1130
1326
  var BACKCHANNEL_LOGOUT_EVENT = "http://schemas.openid.net/event/backchannel-logout";
@@ -1236,6 +1432,12 @@ async function verifyLogoutTokenDetailed(logoutToken, ssoBaseUrl, clientId, opti
1236
1432
  if (Date.now() >= payload.exp * 1e3 + 6e4) {
1237
1433
  throw new SsoError("logout_token_expired", "Logout Token \u5DF2\u8FC7\u671F");
1238
1434
  }
1435
+ if (typeof payload.iat !== "number") {
1436
+ throw new SsoError("logout_token_invalid", "Logout Token \u7F3A\u5C11 iat \u58F0\u660E");
1437
+ }
1438
+ if (payload.nonce !== void 0) {
1439
+ throw new SsoError("logout_token_invalid", "Logout Token \u4E0D\u5F97\u643A\u5E26 nonce");
1440
+ }
1239
1441
  if (payload.type !== "logout_token") {
1240
1442
  throw new SsoError(
1241
1443
  "logout_token_invalid",
@@ -1314,7 +1516,7 @@ function createBackchannelLogoutRouteHandler(config) {
1314
1516
  );
1315
1517
  return async function handler(request) {
1316
1518
  if (request.method !== "POST") {
1317
- return NextResponse4.json(
1519
+ return NextResponse5.json(
1318
1520
  { error: "method_not_allowed", error_description: "\u4EC5\u63A5\u53D7 POST \u8BF7\u6C42" },
1319
1521
  { status: 405 }
1320
1522
  );
@@ -1323,7 +1525,7 @@ function createBackchannelLogoutRouteHandler(config) {
1323
1525
  "logout_token"
1324
1526
  );
1325
1527
  if (!logoutToken) {
1326
- return NextResponse4.json(
1528
+ return NextResponse5.json(
1327
1529
  { error: "invalid_request", error_description: "\u7F3A\u5C11 logout_token" },
1328
1530
  { status: 400 }
1329
1531
  );
@@ -1336,7 +1538,7 @@ function createBackchannelLogoutRouteHandler(config) {
1336
1538
  } catch (err) {
1337
1539
  const code = err instanceof SsoError ? err.code : "logout_token_invalid";
1338
1540
  const description = err instanceof SsoError ? err.description : "Logout Token \u9A8C\u8BC1\u5931\u8D25";
1339
- return NextResponse4.json(
1541
+ return NextResponse5.json(
1340
1542
  { error: code, error_description: description },
1341
1543
  { status: 400 }
1342
1544
  );
@@ -1349,12 +1551,12 @@ function createBackchannelLogoutRouteHandler(config) {
1349
1551
  "[SSO SDK] backchannel logout onLogout \u94A9\u5B50\u6267\u884C\u5931\u8D25:",
1350
1552
  err
1351
1553
  );
1352
- return NextResponse4.json(
1554
+ return NextResponse5.json(
1353
1555
  { error: "server_error", error_description: "onLogout \u5904\u7406\u5931\u8D25" },
1354
1556
  { status: 500 }
1355
1557
  );
1356
1558
  }
1357
- const res = new NextResponse4(null, { status: 200 });
1559
+ const res = new NextResponse5(null, { status: 200 });
1358
1560
  res.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0, secureCookies));
1359
1561
  res.cookies.set(refreshTokenCookieName, "", getHostCookieOptions(0, secureCookies));
1360
1562
  res.cookies.set(idTokenCookieName, "", getHostCookieOptions(0, secureCookies));
@@ -1373,6 +1575,7 @@ export {
1373
1575
  createBackchannelLogoutRouteHandler,
1374
1576
  createCallbackRouteHandler,
1375
1577
  createLogoutRouteHandler,
1578
+ createRefreshRouteHandler,
1376
1579
  createSsoMiddleware,
1377
1580
  getHostCookieOptions,
1378
1581
  getSecureCookieOptions,