@nihplod/sso-sdk 1.0.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.
@@ -0,0 +1,583 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/next/index.ts
21
+ var next_exports = {};
22
+ __export(next_exports, {
23
+ DEFAULT_ACCESS_TOKEN_COOKIE_NAME: () => DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
24
+ DEFAULT_REFRESH_TOKEN_COOKIE_NAME: () => DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
25
+ DEFAULT_RETURN_COOKIE_NAME: () => DEFAULT_RETURN_COOKIE_NAME,
26
+ DEFAULT_STATE_COOKIE_NAME: () => DEFAULT_STATE_COOKIE_NAME,
27
+ DEFAULT_VERIFIER_COOKIE_NAME: () => DEFAULT_VERIFIER_COOKIE_NAME,
28
+ createCallbackRouteHandler: () => createCallbackRouteHandler,
29
+ createLogoutRouteHandler: () => createLogoutRouteHandler,
30
+ createSsoMiddleware: () => createSsoMiddleware,
31
+ getHostCookieOptions: () => getHostCookieOptions,
32
+ getSecureCookieOptions: () => getSecureCookieOptions
33
+ });
34
+ module.exports = __toCommonJS(next_exports);
35
+
36
+ // src/next/middleware.ts
37
+ var import_server = require("next/server");
38
+
39
+ // src/next/constants.ts
40
+ var DEFAULT_ACCESS_TOKEN_COOKIE_NAME = "__Host-nihplod_sso_at";
41
+ var DEFAULT_REFRESH_TOKEN_COOKIE_NAME = "__Host-nihplod_sso_rt";
42
+ var DEFAULT_ID_TOKEN_COOKIE_NAME = "__Host-nihplod_sso_id";
43
+ var DEFAULT_STATE_COOKIE_NAME = "__Host-nihplod_sso_state";
44
+ var DEFAULT_RETURN_COOKIE_NAME = "__Host-nihplod_sso_return";
45
+ var DEFAULT_VERIFIER_COOKIE_NAME = "__Secure-nihplod_sso_verifier";
46
+ function getHostCookieOptions(maxAge) {
47
+ return {
48
+ httpOnly: true,
49
+ secure: true,
50
+ sameSite: "lax",
51
+ path: "/",
52
+ ...maxAge !== void 0 ? { maxAge } : {}
53
+ };
54
+ }
55
+ function getSecureCookieOptions(maxAge, path = "/") {
56
+ return {
57
+ httpOnly: true,
58
+ secure: true,
59
+ sameSite: "lax",
60
+ path,
61
+ ...maxAge !== void 0 ? { maxAge } : {}
62
+ };
63
+ }
64
+
65
+ // src/next/middleware.ts
66
+ function generateRandomString(length) {
67
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
68
+ const maxValid = Math.floor(256 / chars.length) * chars.length;
69
+ let result = "";
70
+ while (result.length < length) {
71
+ const array = new Uint8Array(length * 2);
72
+ crypto.getRandomValues(array);
73
+ for (let i = 0; i < array.length && result.length < length; i++) {
74
+ if (array[i] >= maxValid) continue;
75
+ result += chars[array[i] % chars.length];
76
+ }
77
+ }
78
+ return result;
79
+ }
80
+ async function computeCodeChallenge(verifier) {
81
+ const encoder = new TextEncoder();
82
+ const data = encoder.encode(verifier);
83
+ const hash = await crypto.subtle.digest("SHA-256", data);
84
+ const bytes = new Uint8Array(hash);
85
+ let binary = "";
86
+ for (let i = 0; i < bytes.byteLength; i++) {
87
+ binary += String.fromCharCode(bytes[i]);
88
+ }
89
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
90
+ }
91
+ function matchesPath(pathname, paths) {
92
+ return paths.some((path) => {
93
+ if (pathname === path) return true;
94
+ if (path.endsWith("/:path*") && pathname.startsWith(path.replace("/:path*", ""))) return true;
95
+ if (pathname.startsWith(path + "/")) return true;
96
+ return false;
97
+ });
98
+ }
99
+ async function introspectAccessToken(token, ssoBaseUrl, clientId, clientSecret) {
100
+ try {
101
+ const body = new URLSearchParams({
102
+ token,
103
+ token_type_hint: "access_token",
104
+ client_id: clientId
105
+ });
106
+ if (clientSecret) {
107
+ body.set("client_secret", clientSecret);
108
+ }
109
+ const res = await fetch(`${ssoBaseUrl}/api/oauth/introspect`, {
110
+ method: "POST",
111
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
112
+ body: body.toString()
113
+ });
114
+ if (!res.ok) return false;
115
+ const data = await res.json();
116
+ return data.active === true;
117
+ } catch {
118
+ return false;
119
+ }
120
+ }
121
+ function createSsoMiddleware(config) {
122
+ const {
123
+ clientId,
124
+ clientSecret,
125
+ ssoBaseUrl,
126
+ redirectUri,
127
+ scopes = "openid profile",
128
+ publicPaths = [],
129
+ callbackPath = "/api/auth/callback",
130
+ ssoCookieName = "__Host-user_token",
131
+ accessTokenCookieName = DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
132
+ stateCookieName = DEFAULT_STATE_COOKIE_NAME,
133
+ returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
134
+ verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME
135
+ } = config;
136
+ const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
137
+ return async function ssoMiddleware(request) {
138
+ const { pathname } = request.nextUrl;
139
+ if (pathname.startsWith("/_next/") || pathname.startsWith("/favicon.ico") || pathname.match(/\.(ico|png|jpg|jpeg|svg|css|js|woff2?)$/)) {
140
+ return import_server.NextResponse.next();
141
+ }
142
+ if (pathname === callbackPath) {
143
+ return import_server.NextResponse.next();
144
+ }
145
+ const allPublicPaths = [callbackPath, ...publicPaths];
146
+ if (matchesPath(pathname, allPublicPaths)) {
147
+ return import_server.NextResponse.next();
148
+ }
149
+ const ssoSession = request.cookies.get(ssoCookieName);
150
+ if (ssoSession?.value) {
151
+ return import_server.NextResponse.next();
152
+ }
153
+ const accessTokenCookie = request.cookies.get(accessTokenCookieName);
154
+ if (accessTokenCookie?.value) {
155
+ const tokenActive = await introspectAccessToken(
156
+ accessTokenCookie.value,
157
+ normalizedBase,
158
+ clientId,
159
+ clientSecret
160
+ );
161
+ if (tokenActive) {
162
+ return import_server.NextResponse.next();
163
+ }
164
+ }
165
+ const state = generateRandomString(32);
166
+ const verifier = generateRandomString(64);
167
+ const challenge = await computeCodeChallenge(verifier);
168
+ const authorizeParams = new URLSearchParams();
169
+ authorizeParams.set("response_type", "code");
170
+ authorizeParams.set("client_id", clientId);
171
+ authorizeParams.set("redirect_uri", redirectUri);
172
+ authorizeParams.set("scope", scopes);
173
+ authorizeParams.set("state", state);
174
+ authorizeParams.set("code_challenge", challenge);
175
+ authorizeParams.set("code_challenge_method", "S256");
176
+ const loginUrl = new URL("/api/oauth/authorize", normalizedBase);
177
+ loginUrl.search = authorizeParams.toString();
178
+ const response = import_server.NextResponse.redirect(loginUrl);
179
+ response.cookies.set(stateCookieName, state, getHostCookieOptions(600));
180
+ response.cookies.set(verifierCookieName, verifier, getSecureCookieOptions(600, callbackPath));
181
+ response.cookies.set(returnUrlCookieName, request.url, getHostCookieOptions(600));
182
+ if (accessTokenCookie?.value) {
183
+ response.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0));
184
+ }
185
+ return response;
186
+ };
187
+ }
188
+
189
+ // src/next/callback.ts
190
+ var import_server2 = require("next/server");
191
+ function isTrustedReturnUrl(url, currentOrigin) {
192
+ if (!url) return false;
193
+ if (url.startsWith("/") && !url.startsWith("//")) return true;
194
+ try {
195
+ return new URL(url).origin === currentOrigin;
196
+ } catch {
197
+ return false;
198
+ }
199
+ }
200
+ function base64UrlDecode(input) {
201
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
202
+ const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
203
+ const binary = atob(padded);
204
+ const bytes = new Uint8Array(binary.length);
205
+ for (let i = 0; i < binary.length; i++) {
206
+ bytes[i] = binary.charCodeAt(i);
207
+ }
208
+ return bytes;
209
+ }
210
+ function decodeJwtPayload(token) {
211
+ try {
212
+ const parts = token.split(".");
213
+ if (parts.length !== 3) return null;
214
+ const json = new TextDecoder().decode(base64UrlDecode(parts[1]));
215
+ return JSON.parse(json);
216
+ } catch {
217
+ return null;
218
+ }
219
+ }
220
+ function decodeJwtHeader(token) {
221
+ try {
222
+ const parts = token.split(".");
223
+ if (parts.length !== 3) return null;
224
+ const json = new TextDecoder().decode(base64UrlDecode(parts[0]));
225
+ return JSON.parse(json);
226
+ } catch {
227
+ return null;
228
+ }
229
+ }
230
+ async function fetchJwks(baseUrl) {
231
+ try {
232
+ const res = await fetch(`${baseUrl}/api/oauth/jwks`);
233
+ if (!res.ok) return null;
234
+ return await res.json();
235
+ } catch {
236
+ return null;
237
+ }
238
+ }
239
+ async function verifyRs256Signature(token, jwk) {
240
+ try {
241
+ const [headerB64, payloadB64, signature] = token.split(".");
242
+ if (!signature || !jwk.n || !jwk.e) return false;
243
+ const cryptoKey = await crypto.subtle.importKey(
244
+ "jwk",
245
+ { kty: "RSA", n: jwk.n, e: jwk.e, alg: "RS256", ext: false },
246
+ { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
247
+ false,
248
+ ["verify"]
249
+ );
250
+ const signatureBytes = base64UrlDecode(signature);
251
+ const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
252
+ return await crypto.subtle.verify(
253
+ "RSASSA-PKCS1-v1_5",
254
+ cryptoKey,
255
+ signatureBytes,
256
+ data
257
+ );
258
+ } catch {
259
+ return false;
260
+ }
261
+ }
262
+ async function computeAtHash(accessToken) {
263
+ const hash = await crypto.subtle.digest(
264
+ "SHA-256",
265
+ new TextEncoder().encode(accessToken)
266
+ );
267
+ const bytes = new Uint8Array(hash);
268
+ const half = bytes.slice(0, bytes.length / 2);
269
+ let binary = "";
270
+ for (const b of half) {
271
+ binary += String.fromCharCode(b);
272
+ }
273
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
274
+ }
275
+ async function validateIdToken(idToken, accessToken, expectedIssuer, expectedClientId) {
276
+ const header = decodeJwtHeader(idToken);
277
+ if (!header) throw new Error("ID Token \u683C\u5F0F\u9519\u8BEF");
278
+ const alg = header.alg;
279
+ if (typeof alg !== "string" || alg !== "RS256" && alg !== "HS256") {
280
+ throw new Error(`\u4E0D\u652F\u6301\u7684 ID Token \u7B7E\u540D\u7B97\u6CD5: ${alg}`);
281
+ }
282
+ if (alg === "RS256") {
283
+ const jwks = await fetchJwks(expectedIssuer);
284
+ if (!jwks) throw new Error("\u65E0\u6CD5\u83B7\u53D6 JWKS");
285
+ const kid = typeof header.kid === "string" ? header.kid : void 0;
286
+ const key = jwks.keys.find(
287
+ (k) => k.kty === "RSA" && k.alg === "RS256" && k.use === "sig" && (kid ? k.kid === kid : true)
288
+ );
289
+ if (!key) throw new Error("JWKS \u4E2D\u672A\u627E\u5230\u5339\u914D\u516C\u94A5");
290
+ const valid = await verifyRs256Signature(idToken, key);
291
+ if (!valid) throw new Error("ID Token \u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25");
292
+ }
293
+ if (alg === "HS256") {
294
+ console.warn(
295
+ "[SSO SDK/Next] ID Token \u4F7F\u7528 HS256 \u7B7E\u540D\u3002\u5EFA\u8BAE\u4E3B\u7AD9\u542F\u7528 RS256 \u4EE5\u83B7\u5F97\u5B8C\u6574\u7B7E\u540D\u9A8C\u8BC1\u3002"
296
+ );
297
+ }
298
+ const payload = decodeJwtPayload(idToken);
299
+ if (!payload) throw new Error("ID Token payload \u89E3\u6790\u5931\u8D25");
300
+ const normalizedIssuer = expectedIssuer.replace(/\/+$/, "");
301
+ const tokenIssuer = typeof payload.iss === "string" ? payload.iss.replace(/\/+$/, "") : "";
302
+ if (tokenIssuer !== normalizedIssuer) {
303
+ throw new Error("ID Token issuer \u4E0D\u5339\u914D");
304
+ }
305
+ const aud = payload.aud;
306
+ const audArr = Array.isArray(aud) ? aud : [aud];
307
+ if (!audArr.includes(expectedClientId)) {
308
+ throw new Error("ID Token audience \u4E0D\u5339\u914D");
309
+ }
310
+ if (typeof payload.exp === "number" && Date.now() >= payload.exp * 1e3) {
311
+ throw new Error("ID Token \u5DF2\u8FC7\u671F");
312
+ }
313
+ if (typeof payload.sub !== "string" || !payload.sub) {
314
+ throw new Error("ID Token \u7F3A\u5C11 sub");
315
+ }
316
+ if (typeof payload.at_hash === "string" && payload.at_hash) {
317
+ const actual = await computeAtHash(accessToken);
318
+ if (actual !== payload.at_hash) {
319
+ throw new Error("ID Token at_hash \u4E0D\u5339\u914D");
320
+ }
321
+ }
322
+ }
323
+ function createCallbackRouteHandler(config) {
324
+ const {
325
+ clientId,
326
+ ssoBaseUrl,
327
+ redirectUri,
328
+ clientSecret,
329
+ defaultReturnPath = "/",
330
+ accessTokenCookieName = DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
331
+ refreshTokenCookieName = DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
332
+ idTokenCookieName = DEFAULT_ID_TOKEN_COOKIE_NAME,
333
+ stateCookieName = DEFAULT_STATE_COOKIE_NAME,
334
+ returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
335
+ verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME
336
+ } = config;
337
+ const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
338
+ return async function GET(request) {
339
+ const { searchParams } = request.nextUrl;
340
+ const error = searchParams.get("error");
341
+ if (error) {
342
+ const desc = searchParams.get("error_description") || error;
343
+ return import_server2.NextResponse.json(
344
+ { error: "authorization_failed", error_description: desc },
345
+ { status: 400 }
346
+ );
347
+ }
348
+ const code = searchParams.get("code");
349
+ const returnedState = searchParams.get("state");
350
+ if (!code) {
351
+ return import_server2.NextResponse.json(
352
+ {
353
+ error: "invalid_request",
354
+ error_description: "\u7F3A\u5C11 authorization code"
355
+ },
356
+ { status: 400 }
357
+ );
358
+ }
359
+ const savedState = request.cookies.get(stateCookieName)?.value;
360
+ if (!savedState) {
361
+ return import_server2.NextResponse.json(
362
+ {
363
+ error: "invalid_request",
364
+ error_description: "State \u53C2\u6570\u7F3A\u5931\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u8BF7\u6C42"
365
+ },
366
+ { status: 400 }
367
+ );
368
+ }
369
+ if (returnedState !== savedState) {
370
+ return import_server2.NextResponse.json(
371
+ {
372
+ error: "invalid_request",
373
+ error_description: "State \u53C2\u6570\u4E0D\u5339\u914D\uFF0C\u53EF\u80FD\u5B58\u5728 CSRF \u653B\u51FB"
374
+ },
375
+ { status: 400 }
376
+ );
377
+ }
378
+ const verifier = request.cookies.get(verifierCookieName)?.value;
379
+ if (!verifier) {
380
+ return import_server2.NextResponse.json(
381
+ {
382
+ error: "invalid_request",
383
+ error_description: "PKCE verifier \u7F3A\u5931\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u8BF7\u6C42"
384
+ },
385
+ { status: 400 }
386
+ );
387
+ }
388
+ const tokenEndpoint = `${normalizedBase}/api/oauth/token`;
389
+ const body = new URLSearchParams();
390
+ body.set("grant_type", "authorization_code");
391
+ body.set("code", code);
392
+ body.set("client_id", clientId);
393
+ body.set("redirect_uri", redirectUri);
394
+ body.set("code_verifier", verifier);
395
+ if (clientSecret) {
396
+ body.set("client_secret", clientSecret);
397
+ }
398
+ let res;
399
+ let lastError;
400
+ const maxRetries = 1;
401
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
402
+ if (attempt > 0) {
403
+ await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
404
+ }
405
+ try {
406
+ res = await fetch(tokenEndpoint, {
407
+ method: "POST",
408
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
409
+ body: body.toString()
410
+ });
411
+ lastError = null;
412
+ break;
413
+ } catch (err) {
414
+ lastError = err;
415
+ if (attempt >= maxRetries) {
416
+ return import_server2.NextResponse.json(
417
+ { error: "server_error", error_description: "Token \u8BF7\u6C42\u5931\u8D25\uFF0C\u5DF2\u91CD\u8BD5\u4ECD\u4E0D\u53EF\u8FBE" },
418
+ { status: 502 }
419
+ );
420
+ }
421
+ }
422
+ }
423
+ if (lastError || !res) {
424
+ return import_server2.NextResponse.json(
425
+ { error: "server_error", error_description: "Token \u8BF7\u6C42\u5931\u8D25" },
426
+ { status: 502 }
427
+ );
428
+ }
429
+ if (!res.ok) {
430
+ let errData = {};
431
+ try {
432
+ errData = await res.json();
433
+ } catch {
434
+ }
435
+ return import_server2.NextResponse.json(
436
+ {
437
+ error: "token_request_failed",
438
+ error_description: errData.error_description || `Token \u8BF7\u6C42\u5931\u8D25: HTTP ${res.status}`
439
+ },
440
+ { status: 502 }
441
+ );
442
+ }
443
+ const tokenData = await res.json();
444
+ if (tokenData.id_token) {
445
+ try {
446
+ await validateIdToken(
447
+ tokenData.id_token,
448
+ tokenData.access_token,
449
+ normalizedBase,
450
+ clientId
451
+ );
452
+ } catch (err) {
453
+ return import_server2.NextResponse.json(
454
+ {
455
+ error: "id_token_invalid",
456
+ error_description: err instanceof Error ? err.message : "ID Token \u9A8C\u8BC1\u5931\u8D25"
457
+ },
458
+ { status: 400 }
459
+ );
460
+ }
461
+ }
462
+ const rawReturnUrl = request.cookies.get(returnUrlCookieName)?.value || defaultReturnPath;
463
+ const returnUrl = isTrustedReturnUrl(rawReturnUrl, request.nextUrl.origin) ? rawReturnUrl : "/";
464
+ const response = import_server2.NextResponse.redirect(new URL(returnUrl, request.url));
465
+ response.cookies.set(accessTokenCookieName, tokenData.access_token, {
466
+ ...getHostCookieOptions(tokenData.expires_in)
467
+ });
468
+ const refreshMaxAge = tokenData.refresh_expires_in != null ? tokenData.refresh_expires_in : 30 * 24 * 60 * 60;
469
+ response.cookies.set(refreshTokenCookieName, tokenData.refresh_token, {
470
+ ...getHostCookieOptions(refreshMaxAge)
471
+ });
472
+ if (tokenData.id_token) {
473
+ response.cookies.set(idTokenCookieName, tokenData.id_token, {
474
+ ...getHostCookieOptions(refreshMaxAge)
475
+ });
476
+ }
477
+ response.cookies.set(stateCookieName, "", getHostCookieOptions(0));
478
+ response.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0));
479
+ response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/"));
480
+ response.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, request.nextUrl.pathname));
481
+ return response;
482
+ };
483
+ }
484
+
485
+ // src/next/logout.ts
486
+ var import_server3 = require("next/server");
487
+ async function fetchDiscovery(ssoBaseUrl) {
488
+ try {
489
+ const controller = new AbortController();
490
+ setTimeout(() => controller.abort(), 5e3);
491
+ const res = await fetch(
492
+ `${ssoBaseUrl}/api/oauth/.well-known/openid-configuration`,
493
+ { signal: controller.signal }
494
+ );
495
+ if (!res.ok) return null;
496
+ return await res.json();
497
+ } catch {
498
+ return null;
499
+ }
500
+ }
501
+ function createLogoutRouteHandler(config) {
502
+ const {
503
+ clientId,
504
+ ssoBaseUrl,
505
+ redirectUri,
506
+ clientSecret,
507
+ postLogoutRedirectUri = new URL(redirectUri).origin + "/",
508
+ redirectToSso = true,
509
+ accessTokenCookieName = DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
510
+ refreshTokenCookieName = DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
511
+ idTokenCookieName = DEFAULT_ID_TOKEN_COOKIE_NAME,
512
+ stateCookieName = DEFAULT_STATE_COOKIE_NAME,
513
+ returnUrlCookieName = DEFAULT_RETURN_COOKIE_NAME,
514
+ verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME,
515
+ callbackPath = "/api/auth/callback"
516
+ } = config;
517
+ const normalizedBase = ssoBaseUrl.replace(/\/+$/, "");
518
+ return async function GET(request) {
519
+ const refreshToken = request.cookies.get(refreshTokenCookieName)?.value;
520
+ const idTokenHint = request.cookies.get(idTokenCookieName)?.value;
521
+ if (refreshToken) {
522
+ try {
523
+ const discovery = await fetchDiscovery(normalizedBase);
524
+ const revokeUrl = discovery?.revocation_endpoint || `${normalizedBase}/api/oauth/revoke`;
525
+ const body = new URLSearchParams({
526
+ token: refreshToken,
527
+ token_type_hint: "refresh_token",
528
+ client_id: clientId
529
+ });
530
+ if (clientSecret) {
531
+ body.set("client_secret", clientSecret);
532
+ }
533
+ await fetch(revokeUrl, {
534
+ method: "POST",
535
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
536
+ body: body.toString()
537
+ });
538
+ } catch {
539
+ }
540
+ }
541
+ const clearCookies = (res) => {
542
+ res.cookies.set(accessTokenCookieName, "", getHostCookieOptions(0));
543
+ res.cookies.set(refreshTokenCookieName, "", getHostCookieOptions(0));
544
+ res.cookies.set(idTokenCookieName, "", getHostCookieOptions(0));
545
+ res.cookies.set(stateCookieName, "", getHostCookieOptions(0));
546
+ res.cookies.set(returnUrlCookieName, "", getHostCookieOptions(0));
547
+ res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, "/"));
548
+ res.cookies.set(verifierCookieName, "", getSecureCookieOptions(0, callbackPath));
549
+ return res;
550
+ };
551
+ if (redirectToSso) {
552
+ const discovery = await fetchDiscovery(normalizedBase);
553
+ const endSessionEndpoint = discovery?.end_session_endpoint || `${normalizedBase}/logout`;
554
+ const logoutUrl = new URL(endSessionEndpoint);
555
+ logoutUrl.searchParams.set("client_id", clientId);
556
+ logoutUrl.searchParams.set(
557
+ "post_logout_redirect_uri",
558
+ postLogoutRedirectUri
559
+ );
560
+ if (idTokenHint) {
561
+ logoutUrl.searchParams.set("id_token_hint", idTokenHint);
562
+ }
563
+ return clearCookies(import_server3.NextResponse.redirect(logoutUrl.toString()));
564
+ }
565
+ return clearCookies(
566
+ import_server3.NextResponse.redirect(request.nextUrl.origin + "/")
567
+ );
568
+ };
569
+ }
570
+ // Annotate the CommonJS export names for ESM import in node:
571
+ 0 && (module.exports = {
572
+ DEFAULT_ACCESS_TOKEN_COOKIE_NAME,
573
+ DEFAULT_REFRESH_TOKEN_COOKIE_NAME,
574
+ DEFAULT_RETURN_COOKIE_NAME,
575
+ DEFAULT_STATE_COOKIE_NAME,
576
+ DEFAULT_VERIFIER_COOKIE_NAME,
577
+ createCallbackRouteHandler,
578
+ createLogoutRouteHandler,
579
+ createSsoMiddleware,
580
+ getHostCookieOptions,
581
+ getSecureCookieOptions
582
+ });
583
+ //# sourceMappingURL=index.js.map