@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/README.md +445 -441
- package/dist/index.d.mts +15 -4
- package/dist/index.d.ts +15 -4
- package/dist/index.js +62 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -11
- package/dist/index.mjs.map +1 -1
- package/dist/next/index.d.mts +27 -2
- package/dist/next/index.d.ts +27 -2
- package/dist/next/index.js +118 -41
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +117 -41
- package/dist/next/index.mjs.map +1 -1
- package/dist/react/index.d.mts +15 -4
- package/dist/react/index.d.ts +15 -4
- package/dist/react/index.js +112 -17
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +112 -17
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/next/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ var DEFAULT_ACCESS_TOKEN_COOKIE_NAME = "__Host-nihplod_sso_at";
|
|
|
6
6
|
var DEFAULT_REFRESH_TOKEN_COOKIE_NAME = "__Host-nihplod_sso_rt";
|
|
7
7
|
var DEFAULT_ID_TOKEN_COOKIE_NAME = "__Host-nihplod_sso_id";
|
|
8
8
|
var DEFAULT_STATE_COOKIE_NAME = "__Host-nihplod_sso_state";
|
|
9
|
+
var DEFAULT_NONCE_COOKIE_NAME = "__Host-nihplod_sso_nonce";
|
|
9
10
|
var DEFAULT_RETURN_COOKIE_NAME = "__Host-nihplod_sso_return";
|
|
10
11
|
var DEFAULT_VERIFIER_COOKIE_NAME = "__Secure-nihplod_sso_verifier";
|
|
11
12
|
var DEFAULT_LOGOUT_STATE_COOKIE_NAME = "__Host-nihplod_sso_logout_state";
|
|
@@ -161,20 +162,20 @@ function createSsoMiddleware(config) {
|
|
|
161
162
|
const secureCookies = !insecureLocalDev;
|
|
162
163
|
const accessTokenCookieName = insecureLocalDev ? toInsecureCookieName(config.accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE_NAME) : config.accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE_NAME;
|
|
163
164
|
const stateCookieName = insecureLocalDev ? toInsecureCookieName(config.stateCookieName ?? DEFAULT_STATE_COOKIE_NAME) : config.stateCookieName ?? DEFAULT_STATE_COOKIE_NAME;
|
|
165
|
+
const nonceCookieName = insecureLocalDev ? toInsecureCookieName(config.nonceCookieName ?? DEFAULT_NONCE_COOKIE_NAME) : config.nonceCookieName ?? DEFAULT_NONCE_COOKIE_NAME;
|
|
164
166
|
const returnUrlCookieName = insecureLocalDev ? toInsecureCookieName(config.returnUrlCookieName ?? DEFAULT_RETURN_COOKIE_NAME) : config.returnUrlCookieName ?? DEFAULT_RETURN_COOKIE_NAME;
|
|
165
167
|
const verifierCookieName = insecureLocalDev ? toInsecureCookieName(config.verifierCookieName ?? DEFAULT_VERIFIER_COOKIE_NAME) : config.verifierCookieName ?? DEFAULT_VERIFIER_COOKIE_NAME;
|
|
166
168
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
169
|
+
const normalizedServerBase = (config.serverBaseUrl ?? ssoBaseUrl).replace(/\/+$/, "");
|
|
170
|
+
if (!validateSsoCookie) {
|
|
171
|
+
console.warn(
|
|
172
|
+
"[SSO SDK] validateSsoCookie=false\uFF1A\u4E2D\u95F4\u4EF6\u4EC5\u68C0\u67E5 Cookie \u5B58\u5728\u6027\uFF0C\u53EF\u80FD\u653E\u884C\u5DF2\u5931\u6548\u7684\u4F1A\u8BDD\u3002\u4E2D\u95F4\u4EF6\u53EA\u662F UX \u5C42\uFF0C\u654F\u611F\u6570\u636E\u7684\u9274\u6743\u5FC5\u987B\u5728 Route Handler / Server Component \u4E2D\u5B8C\u6210\u3002"
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (!clientSecret) {
|
|
176
|
+
console.warn(
|
|
177
|
+
"[SSO SDK] \u672A\u914D\u7F6E clientSecret\uFF08Public Client \u6A21\u5F0F\uFF09\uFF1Aintrospection \u65E0\u5BA2\u6237\u7AEF\u8BA4\u8BC1\uFF0C\u4E2D\u95F4\u4EF6\u5224\u5B9A\u7ED3\u679C\u4EC5\u4F5C UX \u53C2\u8003\u3002Confidential Client\uFF08BFF\uFF09\u8BF7\u914D\u7F6E clientSecret\u3002"
|
|
178
|
+
);
|
|
178
179
|
}
|
|
179
180
|
return async function ssoMiddleware(request) {
|
|
180
181
|
const { pathname } = request.nextUrl;
|
|
@@ -193,7 +194,7 @@ function createSsoMiddleware(config) {
|
|
|
193
194
|
if (validateSsoCookie) {
|
|
194
195
|
const tokenActive = await introspectAccessToken(
|
|
195
196
|
ssoSession.value,
|
|
196
|
-
|
|
197
|
+
normalizedServerBase,
|
|
197
198
|
clientId,
|
|
198
199
|
clientSecret
|
|
199
200
|
);
|
|
@@ -208,7 +209,7 @@ function createSsoMiddleware(config) {
|
|
|
208
209
|
if (accessTokenCookie?.value) {
|
|
209
210
|
const tokenActive = await introspectAccessToken(
|
|
210
211
|
accessTokenCookie.value,
|
|
211
|
-
|
|
212
|
+
normalizedServerBase,
|
|
212
213
|
clientId,
|
|
213
214
|
clientSecret
|
|
214
215
|
);
|
|
@@ -217,6 +218,7 @@ function createSsoMiddleware(config) {
|
|
|
217
218
|
}
|
|
218
219
|
}
|
|
219
220
|
const state = generateRandomString(32);
|
|
221
|
+
const nonce = generateRandomString(43);
|
|
220
222
|
const verifier = generateRandomString(64);
|
|
221
223
|
const challenge = await computeCodeChallenge(verifier);
|
|
222
224
|
const authorizeParams = new URLSearchParams();
|
|
@@ -225,12 +227,14 @@ function createSsoMiddleware(config) {
|
|
|
225
227
|
authorizeParams.set("redirect_uri", redirectUri);
|
|
226
228
|
authorizeParams.set("scope", scopes);
|
|
227
229
|
authorizeParams.set("state", state);
|
|
230
|
+
authorizeParams.set("nonce", nonce);
|
|
228
231
|
authorizeParams.set("code_challenge", challenge);
|
|
229
232
|
authorizeParams.set("code_challenge_method", "S256");
|
|
230
233
|
const loginUrl = new URL("/api/oauth/authorize", normalizedBase);
|
|
231
234
|
loginUrl.search = authorizeParams.toString();
|
|
232
235
|
const response = NextResponse.redirect(loginUrl);
|
|
233
236
|
response.cookies.set(stateCookieName, state, getHostCookieOptions(600, secureCookies));
|
|
237
|
+
response.cookies.set(nonceCookieName, nonce, getHostCookieOptions(600, secureCookies));
|
|
234
238
|
response.cookies.set(verifierCookieName, verifier, getSecureCookieOptions(600, callbackPath, secureCookies));
|
|
235
239
|
const safeReturnUrl = (request.nextUrl.pathname + request.nextUrl.search).slice(0, 2048);
|
|
236
240
|
response.cookies.set(returnUrlCookieName, safeReturnUrl, getHostCookieOptions(600, secureCookies));
|
|
@@ -319,18 +323,21 @@ function normalizeIssuer(url) {
|
|
|
319
323
|
var cachedJwks = null;
|
|
320
324
|
var cachedDiscovery = null;
|
|
321
325
|
var JWKS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
326
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
322
327
|
async function fetchDiscoveryDoc(baseUrl) {
|
|
323
328
|
const now = Date.now();
|
|
324
329
|
if (cachedDiscovery && cachedDiscovery.baseUrl === baseUrl && now - cachedDiscovery.fetchedAt < JWKS_CACHE_TTL_MS) {
|
|
325
330
|
return cachedDiscovery.doc;
|
|
326
331
|
}
|
|
327
332
|
try {
|
|
328
|
-
const res = await fetch(`${baseUrl}/api/oauth/.well-known/openid-configuration
|
|
329
|
-
|
|
333
|
+
const res = await fetch(`${baseUrl}/api/oauth/.well-known/openid-configuration`, {
|
|
334
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
335
|
+
});
|
|
336
|
+
if (!res.ok) return null;
|
|
337
|
+
const doc = await res.json();
|
|
330
338
|
cachedDiscovery = { baseUrl, doc, fetchedAt: now };
|
|
331
339
|
return doc;
|
|
332
340
|
} catch {
|
|
333
|
-
cachedDiscovery = { baseUrl, doc: null, fetchedAt: now };
|
|
334
341
|
return null;
|
|
335
342
|
}
|
|
336
343
|
}
|
|
@@ -342,7 +349,10 @@ async function fetchJwks(baseUrl, options = {}) {
|
|
|
342
349
|
const discovery = await fetchDiscoveryDoc(baseUrl);
|
|
343
350
|
const jwksUri = discovery?.jwks_uri || `${baseUrl}/api/oauth/jwks`;
|
|
344
351
|
try {
|
|
345
|
-
const res = await fetch(jwksUri,
|
|
352
|
+
const res = await fetch(jwksUri, {
|
|
353
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
354
|
+
...options.forceRefresh ? { cache: "no-cache" } : {}
|
|
355
|
+
});
|
|
346
356
|
if (!res.ok) return null;
|
|
347
357
|
const jwks = await res.json();
|
|
348
358
|
cachedJwks = { baseUrl, jwks, fetchedAt: now };
|
|
@@ -389,7 +399,7 @@ async function computeAtHash(accessToken) {
|
|
|
389
399
|
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
390
400
|
}
|
|
391
401
|
async function validateIdToken(idToken, accessToken, expectedIssuer, expectedClientId, options = {}) {
|
|
392
|
-
const { rejectHs256WhenRs256Available = true } = options;
|
|
402
|
+
const { rejectHs256WhenRs256Available = true, expectedNonce } = options;
|
|
393
403
|
const header = decodeJwtHeader(idToken);
|
|
394
404
|
if (!header) {
|
|
395
405
|
throw new SsoError("id_token_invalid", "ID Token \u683C\u5F0F\u9519\u8BEF");
|
|
@@ -479,6 +489,12 @@ async function validateIdToken(idToken, accessToken, expectedIssuer, expectedCli
|
|
|
479
489
|
throw new SsoError("id_token_at_hash_mismatch", "ID Token at_hash \u4E0D\u5339\u914D");
|
|
480
490
|
}
|
|
481
491
|
}
|
|
492
|
+
if (expectedNonce !== void 0) {
|
|
493
|
+
const tokenNonce = typeof payload.nonce === "string" ? payload.nonce : "";
|
|
494
|
+
if (!tokenNonce || !timingSafeEqualString(expectedNonce, tokenNonce)) {
|
|
495
|
+
throw new SsoError("id_token_nonce_mismatch", "ID Token nonce \u4E0D\u5339\u914D");
|
|
496
|
+
}
|
|
497
|
+
}
|
|
482
498
|
return { sub: payload.sub };
|
|
483
499
|
}
|
|
484
500
|
|
|
@@ -499,10 +515,19 @@ function createCallbackRouteHandler(config) {
|
|
|
499
515
|
const refreshTokenCookieName = pickName(config.refreshTokenCookieName, DEFAULT_REFRESH_TOKEN_COOKIE_NAME);
|
|
500
516
|
const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
|
|
501
517
|
const stateCookieName = pickName(config.stateCookieName, DEFAULT_STATE_COOKIE_NAME);
|
|
518
|
+
const nonceCookieName = pickName(config.nonceCookieName, DEFAULT_NONCE_COOKIE_NAME);
|
|
502
519
|
const returnUrlCookieName = pickName(config.returnUrlCookieName, DEFAULT_RETURN_COOKIE_NAME);
|
|
503
520
|
const verifierCookieName = pickName(config.verifierCookieName, DEFAULT_VERIFIER_COOKIE_NAME);
|
|
504
521
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
522
|
+
const normalizedServerBase = (config.serverBaseUrl ?? ssoBaseUrl).replace(/\/+$/, "");
|
|
505
523
|
return async function GET(request) {
|
|
524
|
+
const response = await handleCallback(request);
|
|
525
|
+
if (response.status >= 400) {
|
|
526
|
+
response.cookies.set(nonceCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
527
|
+
}
|
|
528
|
+
return response;
|
|
529
|
+
};
|
|
530
|
+
async function handleCallback(request) {
|
|
506
531
|
const { searchParams } = request.nextUrl;
|
|
507
532
|
const error = searchParams.get("error");
|
|
508
533
|
if (error) {
|
|
@@ -533,7 +558,7 @@ function createCallbackRouteHandler(config) {
|
|
|
533
558
|
{ status: 400 }
|
|
534
559
|
);
|
|
535
560
|
}
|
|
536
|
-
if (returnedState
|
|
561
|
+
if (!timingSafeEqualString(returnedState ?? "", savedState)) {
|
|
537
562
|
return NextResponse2.json(
|
|
538
563
|
{
|
|
539
564
|
error: "invalid_request",
|
|
@@ -542,6 +567,7 @@ function createCallbackRouteHandler(config) {
|
|
|
542
567
|
{ status: 400 }
|
|
543
568
|
);
|
|
544
569
|
}
|
|
570
|
+
const expectedNonce = request.cookies.get(nonceCookieName)?.value;
|
|
545
571
|
const verifier = request.cookies.get(verifierCookieName)?.value;
|
|
546
572
|
if (!verifier) {
|
|
547
573
|
return NextResponse2.json(
|
|
@@ -552,7 +578,7 @@ function createCallbackRouteHandler(config) {
|
|
|
552
578
|
{ status: 400 }
|
|
553
579
|
);
|
|
554
580
|
}
|
|
555
|
-
const tokenEndpoint = `${
|
|
581
|
+
const tokenEndpoint = `${normalizedServerBase}/api/oauth/token`;
|
|
556
582
|
const body = new URLSearchParams();
|
|
557
583
|
body.set("grant_type", "authorization_code");
|
|
558
584
|
body.set("code", code);
|
|
@@ -623,7 +649,8 @@ function createCallbackRouteHandler(config) {
|
|
|
623
649
|
tokenData.id_token,
|
|
624
650
|
tokenData.access_token,
|
|
625
651
|
normalizedBase,
|
|
626
|
-
clientId
|
|
652
|
+
clientId,
|
|
653
|
+
{ expectedNonce }
|
|
627
654
|
);
|
|
628
655
|
} catch (err) {
|
|
629
656
|
return NextResponse2.json(
|
|
@@ -667,30 +694,68 @@ function createCallbackRouteHandler(config) {
|
|
|
667
694
|
}
|
|
668
695
|
}
|
|
669
696
|
response.cookies.set(stateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
697
|
+
response.cookies.set(nonceCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
670
698
|
response.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
671
699
|
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/", secureCookies));
|
|
672
700
|
response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, request.nextUrl.pathname, secureCookies));
|
|
673
701
|
return response;
|
|
674
|
-
}
|
|
702
|
+
}
|
|
675
703
|
}
|
|
676
704
|
|
|
677
705
|
// src/next/logout.ts
|
|
678
706
|
import { NextResponse as NextResponse3 } from "next/server";
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
return
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
707
|
+
|
|
708
|
+
// src/core/discovery.ts
|
|
709
|
+
var DISCOVERY_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
710
|
+
var FETCH_TIMEOUT_MS2 = 5e3;
|
|
711
|
+
var cache = /* @__PURE__ */ new Map();
|
|
712
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
713
|
+
function fetchDiscoveryCached(baseUrl) {
|
|
714
|
+
const hit = cache.get(baseUrl);
|
|
715
|
+
if (hit && hit.expiresAt > Date.now()) {
|
|
716
|
+
return Promise.resolve(hit.doc);
|
|
717
|
+
}
|
|
718
|
+
const existing = inflight.get(baseUrl);
|
|
719
|
+
if (existing) return existing;
|
|
720
|
+
const promise = (async () => {
|
|
721
|
+
const controller = new AbortController();
|
|
722
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
723
|
+
try {
|
|
724
|
+
const res = await fetch(`${baseUrl}/api/oauth/.well-known/openid-configuration`, {
|
|
725
|
+
signal: controller.signal
|
|
726
|
+
});
|
|
727
|
+
if (!res.ok) return null;
|
|
728
|
+
const doc = await res.json();
|
|
729
|
+
cache.set(baseUrl, { doc, expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS });
|
|
730
|
+
return doc;
|
|
731
|
+
} catch {
|
|
732
|
+
return null;
|
|
733
|
+
} finally {
|
|
734
|
+
clearTimeout(timeoutId);
|
|
735
|
+
inflight.delete(baseUrl);
|
|
736
|
+
}
|
|
737
|
+
})();
|
|
738
|
+
inflight.set(baseUrl, promise);
|
|
739
|
+
return promise;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// src/next/logout.ts
|
|
743
|
+
function buildLogoutConfirmHtml() {
|
|
744
|
+
return `<!doctype html>
|
|
745
|
+
<html lang="zh-CN">
|
|
746
|
+
<head>
|
|
747
|
+
<meta charset="utf-8">
|
|
748
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
749
|
+
<title>\u786E\u8BA4\u9000\u51FA\u767B\u5F55</title>
|
|
750
|
+
</head>
|
|
751
|
+
<body style="display:flex;align-items:center;justify-content:center;min-height:100vh;font-family:system-ui,sans-serif;">
|
|
752
|
+
<form method="post" style="text-align:center;">
|
|
753
|
+
<p>\u786E\u5B9A\u8981\u9000\u51FA\u767B\u5F55\u5417\uFF1F</p>
|
|
754
|
+
<button type="submit" style="padding:10px 20px;background-color:#ef4444;color:white;border:none;border-radius:8px;cursor:pointer;font-size:14px;">\u9000\u51FA\u767B\u5F55</button>
|
|
755
|
+
<p><a href="/" style="color:#2563eb;text-decoration:underline;">\u53D6\u6D88\u5E76\u8FD4\u56DE\u9996\u9875</a></p>
|
|
756
|
+
</form>
|
|
757
|
+
</body>
|
|
758
|
+
</html>`;
|
|
694
759
|
}
|
|
695
760
|
function generateRandomString2(length) {
|
|
696
761
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
@@ -724,10 +789,12 @@ function createLogoutRouteHandler(config) {
|
|
|
724
789
|
const refreshTokenCookieName = pickName(config.refreshTokenCookieName, DEFAULT_REFRESH_TOKEN_COOKIE_NAME);
|
|
725
790
|
const idTokenCookieName = pickName(config.idTokenCookieName, DEFAULT_ID_TOKEN_COOKIE_NAME);
|
|
726
791
|
const stateCookieName = pickName(config.stateCookieName, DEFAULT_STATE_COOKIE_NAME);
|
|
792
|
+
const nonceCookieName = pickName(config.nonceCookieName, DEFAULT_NONCE_COOKIE_NAME);
|
|
727
793
|
const returnUrlCookieName = pickName(config.returnUrlCookieName, DEFAULT_RETURN_COOKIE_NAME);
|
|
728
794
|
const verifierCookieName = pickName(config.verifierCookieName, DEFAULT_VERIFIER_COOKIE_NAME);
|
|
729
795
|
const logoutStateCookieName = pickName(config.logoutStateCookieName, DEFAULT_LOGOUT_STATE_COOKIE_NAME);
|
|
730
796
|
const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
|
|
797
|
+
const normalizedServerBase = (config.serverBaseUrl ?? ssoBaseUrl).replace(/\/+$/, "");
|
|
731
798
|
const callbackOrigin = new URL(redirectUri).origin;
|
|
732
799
|
return async function handler(request) {
|
|
733
800
|
const returnedState = request.nextUrl.searchParams.get("state");
|
|
@@ -743,12 +810,18 @@ function createLogoutRouteHandler(config) {
|
|
|
743
810
|
res.cookies.set(logoutStateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
744
811
|
return res;
|
|
745
812
|
}
|
|
813
|
+
if (request.method === "GET") {
|
|
814
|
+
return new NextResponse3(buildLogoutConfirmHtml(), {
|
|
815
|
+
status: 200,
|
|
816
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
817
|
+
});
|
|
818
|
+
}
|
|
746
819
|
const refreshToken = request.cookies.get(refreshTokenCookieName)?.value;
|
|
747
820
|
const idTokenHint = request.cookies.get(idTokenCookieName)?.value;
|
|
748
821
|
if (refreshToken) {
|
|
749
822
|
try {
|
|
750
|
-
const discovery = await
|
|
751
|
-
const revokeUrl = discovery?.revocation_endpoint || `${normalizedBase}/api/oauth/revoke`;
|
|
823
|
+
const discovery = config.serverBaseUrl ? null : await fetchDiscoveryCached(normalizedServerBase);
|
|
824
|
+
const revokeUrl = config.serverBaseUrl ? `${normalizedServerBase}/api/oauth/revoke` : discovery?.revocation_endpoint || `${normalizedBase}/api/oauth/revoke`;
|
|
752
825
|
const body = new URLSearchParams({
|
|
753
826
|
token: refreshToken,
|
|
754
827
|
token_type_hint: "refresh_token",
|
|
@@ -760,7 +833,8 @@ function createLogoutRouteHandler(config) {
|
|
|
760
833
|
await fetch(revokeUrl, {
|
|
761
834
|
method: "POST",
|
|
762
835
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
763
|
-
body: body.toString()
|
|
836
|
+
body: body.toString(),
|
|
837
|
+
signal: AbortSignal.timeout(3e3)
|
|
764
838
|
});
|
|
765
839
|
} catch {
|
|
766
840
|
}
|
|
@@ -770,13 +844,14 @@ function createLogoutRouteHandler(config) {
|
|
|
770
844
|
res.cookies.set(refreshTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
771
845
|
res.cookies.set(idTokenCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
772
846
|
res.cookies.set(stateCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
847
|
+
res.cookies.set(nonceCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
773
848
|
res.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0, secureCookies));
|
|
774
849
|
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/", secureCookies));
|
|
775
850
|
res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, callbackPath, secureCookies));
|
|
776
851
|
return res;
|
|
777
852
|
};
|
|
778
853
|
if (redirectToSso) {
|
|
779
|
-
const discovery = await
|
|
854
|
+
const discovery = await fetchDiscoveryCached(normalizedServerBase);
|
|
780
855
|
const endSessionEndpoint = discovery?.end_session_endpoint || `${normalizedBase}/api/oauth/end-session`;
|
|
781
856
|
const logoutUrl = new URL(endSessionEndpoint);
|
|
782
857
|
logoutUrl.searchParams.set("client_id", clientId);
|
|
@@ -802,6 +877,7 @@ export {
|
|
|
802
877
|
DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
|
|
803
878
|
DEFAULT_ID_TOKEN_COOKIE_NAME,
|
|
804
879
|
DEFAULT_LOGOUT_STATE_COOKIE_NAME,
|
|
880
|
+
DEFAULT_NONCE_COOKIE_NAME,
|
|
805
881
|
DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
|
|
806
882
|
DEFAULT_RETURN_COOKIE_NAME,
|
|
807
883
|
DEFAULT_STATE_COOKIE_NAME,
|