@eminuckan/spine 0.3.9 → 0.3.10
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/CHANGELOG.md +17 -0
- package/README.md +17 -7
- package/dist/auth/auth.server.d.ts.map +1 -1
- package/dist/auth/oauth-recovery.server.d.ts +66 -0
- package/dist/auth/oauth-recovery.server.d.ts.map +1 -0
- package/dist/auth/oauth-state-config.d.ts +7 -0
- package/dist/auth/oauth-state-config.d.ts.map +1 -1
- package/dist/auth/redis-session-storage.server.d.ts +18 -0
- package/dist/auth/redis-session-storage.server.d.ts.map +1 -1
- package/dist/auth/server.d.ts +2 -0
- package/dist/auth/server.d.ts.map +1 -1
- package/dist/auth/server.mjs +3 -3
- package/dist/react-router/server.mjs +3 -3
- package/dist/{redis-session-storage.server-CI_IYIn8.mjs → redis-session-storage.server-DLJ0B7-C.mjs} +67 -2
- package/dist/redis-session-storage.server-DLJ0B7-C.mjs.map +1 -0
- package/dist/{server-Cw86FdT-.mjs → server-BJTz5-BE.mjs} +238 -7
- package/dist/server-BJTz5-BE.mjs.map +1 -0
- package/dist/server.mjs +3 -3
- package/dist/tanstack-start/server.mjs +3 -3
- package/dist/tenant/server.mjs +1 -1
- package/docs/adapters.md +17 -8
- package/docs/module-reference.md +5 -0
- package/package.json +1 -1
- package/dist/redis-session-storage.server-CI_IYIn8.mjs.map +0 -1
- package/dist/server-Cw86FdT-.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "./logging/index.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { D as OAUTH_RECOVERY_TTL_SECONDS, E as OAUTH_RECOVERY_TTL_MS, O as OAUTH_STATE_TTL_MS, T as updateAuthSession, a as createAuthSession, c as deleteOAuthRecoveryRecord, f as destroyAuthSessionsByIdentitySession, g as getAuthSession, h as destroyAuthSessionsForUser, i as consumeOAuthRecoveryRecord, k as OAUTH_STATE_TTL_SECONDS, l as deleteOAuthState, m as destroyAuthSessionsBySid, o as createOAuthRecoveryRecord, s as createOAuthState, t as AUTH_ERROR_COOKIE_PREFIX, u as destroyAuthSession, x as listAuthSessionDataForUser, y as getOAuthState } from "./redis-session-storage.server-DLJ0B7-C.mjs";
|
|
3
3
|
import * as oidc from "openid-client";
|
|
4
4
|
import { createRemoteJWKSet, errors, jwtVerify } from "jose";
|
|
5
5
|
//#region src/http/response.ts
|
|
@@ -150,7 +150,7 @@ function isOAuthCallbackFailureCode(value) {
|
|
|
150
150
|
function isHeadersLike(value) {
|
|
151
151
|
return value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.append === "function";
|
|
152
152
|
}
|
|
153
|
-
function getSetCookieValues(headers) {
|
|
153
|
+
function getSetCookieValues$1(headers) {
|
|
154
154
|
const getSetCookie = headers.getSetCookie;
|
|
155
155
|
if (getSetCookie) return getSetCookie.call(headers);
|
|
156
156
|
return Array.from(headers).reduce((cookies, [name, value]) => {
|
|
@@ -175,7 +175,7 @@ var OAuthCallbackError = class extends Error {
|
|
|
175
175
|
this.name = "OAuthCallbackError";
|
|
176
176
|
this.code = code;
|
|
177
177
|
this.cleanupHeaders = new Headers(cleanupHeaders);
|
|
178
|
-
this.cleanupSetCookies = getSetCookieValues(this.cleanupHeaders);
|
|
178
|
+
this.cleanupSetCookies = getSetCookieValues$1(this.cleanupHeaders);
|
|
179
179
|
Object.defineProperty(this, OAUTH_CALLBACK_ERROR_BRAND, {
|
|
180
180
|
configurable: false,
|
|
181
181
|
enumerable: false,
|
|
@@ -201,6 +201,199 @@ function isRecoverableOAuthCallbackError(error) {
|
|
|
201
201
|
return isOAuthCallbackError(error) && error.code === "stale_transaction";
|
|
202
202
|
}
|
|
203
203
|
//#endregion
|
|
204
|
+
//#region src/auth/oauth-recovery.server.ts
|
|
205
|
+
/**
|
|
206
|
+
* Server-only recovery intent for stale OAuth callbacks.
|
|
207
|
+
*
|
|
208
|
+
* A recovery intent is an opaque, HttpOnly cookie backed by a separate Redis
|
|
209
|
+
* record. Consumers must atomically consume it with the typed stale callback
|
|
210
|
+
* error; the client never supplies a boolean budget or a return URL.
|
|
211
|
+
*/
|
|
212
|
+
const OAUTH_RECOVERY_COOKIE_NAME = `${AUTH_ERROR_COOKIE_PREFIX}_oauth_recovery`;
|
|
213
|
+
function escapeRegExp$1(value) {
|
|
214
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
215
|
+
}
|
|
216
|
+
function getCookieValue(request) {
|
|
217
|
+
const cookies = request.headers.get("Cookie");
|
|
218
|
+
if (!cookies) return null;
|
|
219
|
+
const cookieRegex = new RegExp(`(?:^|;\\s*)${escapeRegExp$1(OAUTH_RECOVERY_COOKIE_NAME)}=([^;]*)`);
|
|
220
|
+
const rawValue = cookies.match(cookieRegex)?.[1];
|
|
221
|
+
if (!rawValue) return null;
|
|
222
|
+
try {
|
|
223
|
+
const value = decodeURIComponent(rawValue);
|
|
224
|
+
return value.length > 0 ? value : null;
|
|
225
|
+
} catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function hasCookie(request) {
|
|
230
|
+
const cookies = request.headers.get("Cookie");
|
|
231
|
+
if (!cookies) return false;
|
|
232
|
+
return cookies.split(";").some((part) => {
|
|
233
|
+
const separator = part.indexOf("=");
|
|
234
|
+
return separator > 0 && part.slice(0, separator).trim() === OAUTH_RECOVERY_COOKIE_NAME;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const SAFE_FALLBACK_COOKIE_PATH = "/";
|
|
238
|
+
function normalizeCookiePath(pathname) {
|
|
239
|
+
const candidate = pathname.trim();
|
|
240
|
+
const hasUnsafeCharacter = Array.from(candidate).some((character) => {
|
|
241
|
+
const code = character.charCodeAt(0);
|
|
242
|
+
return code <= 31 || code === 127 || character === ";";
|
|
243
|
+
});
|
|
244
|
+
if (candidate.length === 0 || !candidate.startsWith("/") || candidate.startsWith("//") || hasUnsafeCharacter) return SAFE_FALLBACK_COOKIE_PATH;
|
|
245
|
+
return candidate;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Resolve the callback cookie path from a configured redirect URI or the
|
|
249
|
+
* actual callback request. Unsafe path values fall back to `/` rather than
|
|
250
|
+
* being interpolated into a Set-Cookie header.
|
|
251
|
+
*/
|
|
252
|
+
function getOAuthRecoveryCookiePath(source) {
|
|
253
|
+
try {
|
|
254
|
+
return normalizeCookiePath((source instanceof Request ? new URL(source.url) : source instanceof URL ? source : new URL(source, source.startsWith("/") ? "http://spine.local" : void 0)).pathname);
|
|
255
|
+
} catch {
|
|
256
|
+
return SAFE_FALLBACK_COOKIE_PATH;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function serializeRecoveryCookie(ticket, maxAge, callbackPath) {
|
|
260
|
+
const segments = [
|
|
261
|
+
`${OAUTH_RECOVERY_COOKIE_NAME}=${encodeURIComponent(ticket)}`,
|
|
262
|
+
`Path=${callbackPath}`,
|
|
263
|
+
"HttpOnly",
|
|
264
|
+
"SameSite=Lax",
|
|
265
|
+
`Max-Age=${maxAge}`
|
|
266
|
+
];
|
|
267
|
+
if (process.env.NODE_ENV === "production") segments.push("Secure");
|
|
268
|
+
return segments.join("; ");
|
|
269
|
+
}
|
|
270
|
+
function appendRecoveryCookieClear(headers, callbackPath) {
|
|
271
|
+
headers.append("Set-Cookie", serializeRecoveryCookie("", 0, callbackPath));
|
|
272
|
+
}
|
|
273
|
+
function getSetCookieValues(headers) {
|
|
274
|
+
const getSetCookie = headers.getSetCookie;
|
|
275
|
+
if (getSetCookie) return getSetCookie.call(headers);
|
|
276
|
+
return Array.from(headers).filter(([name]) => name.toLowerCase() === "set-cookie").map(([, value]) => value);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Build a lossless ticket-clear header using a callback request, URL, or
|
|
280
|
+
* configured redirect URI as the cookie-path source.
|
|
281
|
+
*/
|
|
282
|
+
function clearOAuthRecoveryIntentHeaders(source) {
|
|
283
|
+
const headers = new Headers();
|
|
284
|
+
appendRecoveryCookieClear(headers, getOAuthRecoveryCookiePath(source));
|
|
285
|
+
return headers;
|
|
286
|
+
}
|
|
287
|
+
function hasNonEmptyParam(url, name) {
|
|
288
|
+
return url.searchParams.get(name)?.trim().length ? true : false;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* This check is intentionally stricter than the callback error code. It
|
|
292
|
+
* prevents recovery for provider errors, malformed callbacks, and account
|
|
293
|
+
* actions even if an adapter accidentally calls the consume helper there.
|
|
294
|
+
*/
|
|
295
|
+
function isOAuthRecoveryEligibleCallback(request) {
|
|
296
|
+
const url = new URL(request.url);
|
|
297
|
+
return hasNonEmptyParam(url, "code") && hasNonEmptyParam(url, "state") && !url.searchParams.has("error") && !url.searchParams.has("kc_action") && !url.searchParams.has("kc_action_status");
|
|
298
|
+
}
|
|
299
|
+
function isRecoveryRecord(value) {
|
|
300
|
+
if (value === null || typeof value !== "object") return false;
|
|
301
|
+
const record = value;
|
|
302
|
+
return typeof record.state === "string" && record.state.length > 0 && typeof record.returnUrl === "string" && typeof record.createdAt === "number" && Number.isFinite(record.createdAt);
|
|
303
|
+
}
|
|
304
|
+
function getCallbackState(request) {
|
|
305
|
+
const value = new URL(request.url).searchParams.get("state");
|
|
306
|
+
return value && value.trim().length > 0 ? value : null;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Create the opaque ticket cookie for an ordinary login transaction.
|
|
310
|
+
* Application-action transactions deliberately do not receive a recovery
|
|
311
|
+
* ticket and therefore cannot enter the automatic recovery path.
|
|
312
|
+
*/
|
|
313
|
+
async function createOAuthRecoveryForLogin(request, state, redirectUri) {
|
|
314
|
+
const returnUrl = sanitizeOAuthReturnUrl(state.returnUrl, request) ?? "/";
|
|
315
|
+
const callbackPath = getOAuthRecoveryCookiePath(redirectUri);
|
|
316
|
+
const ticket = await createOAuthRecoveryRecord({
|
|
317
|
+
state: state.state,
|
|
318
|
+
returnUrl,
|
|
319
|
+
createdAt: Date.now()
|
|
320
|
+
});
|
|
321
|
+
return {
|
|
322
|
+
ticket,
|
|
323
|
+
cookie: serializeRecoveryCookie(ticket, OAUTH_RECOVERY_TTL_SECONDS, callbackPath)
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Delete an unconsumed ticket during terminal/successful callback cleanup.
|
|
328
|
+
* The caller owns error classification and should still clear the browser
|
|
329
|
+
* cookie if deletion fails.
|
|
330
|
+
*/
|
|
331
|
+
async function discardOAuthRecoveryForRequest(request) {
|
|
332
|
+
const ticket = getCookieValue(request);
|
|
333
|
+
const headers = new Headers();
|
|
334
|
+
if (!hasCookie(request)) return headers;
|
|
335
|
+
if (ticket) await deleteOAuthRecoveryRecord(ticket);
|
|
336
|
+
appendRecoveryCookieClear(headers, getOAuthRecoveryCookiePath(request));
|
|
337
|
+
return headers;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Atomically consume a stale-flow recovery intent.
|
|
341
|
+
*
|
|
342
|
+
* The typed error and the callback shape are both checked server-side. Redis
|
|
343
|
+
* compares the callback state and deletes only on an exact match, so a
|
|
344
|
+
* mismatch cannot burn a valid ticket while a replay or concurrent loser
|
|
345
|
+
* cannot retry.
|
|
346
|
+
*/
|
|
347
|
+
async function consumeOAuthRecoveryIntent(request, callbackError) {
|
|
348
|
+
const cleanupHeaders = clearOAuthRecoveryIntentHeaders(request);
|
|
349
|
+
const cleanupSetCookies = getSetCookieValues(cleanupHeaders);
|
|
350
|
+
if (!isRecoverableOAuthCallbackError(callbackError) || !isOAuthRecoveryEligibleCallback(request)) return {
|
|
351
|
+
intent: null,
|
|
352
|
+
cleanupHeaders,
|
|
353
|
+
cleanupSetCookies
|
|
354
|
+
};
|
|
355
|
+
const ticket = getCookieValue(request);
|
|
356
|
+
const callbackState = getCallbackState(request);
|
|
357
|
+
if (!ticket || !callbackState) return {
|
|
358
|
+
intent: null,
|
|
359
|
+
cleanupHeaders,
|
|
360
|
+
cleanupSetCookies
|
|
361
|
+
};
|
|
362
|
+
let record;
|
|
363
|
+
try {
|
|
364
|
+
record = await consumeOAuthRecoveryRecord(ticket, callbackState);
|
|
365
|
+
} catch (error) {
|
|
366
|
+
logger.warn("OAuth recovery record could not be consumed", { errorType: error instanceof Error ? error.name : "UnknownError" });
|
|
367
|
+
return {
|
|
368
|
+
intent: null,
|
|
369
|
+
cleanupHeaders,
|
|
370
|
+
cleanupSetCookies
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
if (!isRecoveryRecord(record)) return {
|
|
374
|
+
intent: null,
|
|
375
|
+
cleanupHeaders,
|
|
376
|
+
cleanupSetCookies
|
|
377
|
+
};
|
|
378
|
+
const age = Date.now() - record.createdAt;
|
|
379
|
+
if (age < 0 || age > OAUTH_RECOVERY_TTL_MS) return {
|
|
380
|
+
intent: null,
|
|
381
|
+
cleanupHeaders,
|
|
382
|
+
cleanupSetCookies
|
|
383
|
+
};
|
|
384
|
+
const returnUrl = sanitizeOAuthReturnUrl(record.returnUrl, request);
|
|
385
|
+
if (!returnUrl) return {
|
|
386
|
+
intent: null,
|
|
387
|
+
cleanupHeaders,
|
|
388
|
+
cleanupSetCookies
|
|
389
|
+
};
|
|
390
|
+
return {
|
|
391
|
+
intent: { returnUrl },
|
|
392
|
+
cleanupHeaders,
|
|
393
|
+
cleanupSetCookies
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
//#endregion
|
|
204
397
|
//#region src/auth/auth.server.ts
|
|
205
398
|
/**
|
|
206
399
|
* OpenID Connect Authentication Server Module
|
|
@@ -781,6 +974,7 @@ function getCallbackFailureCode(error, stage) {
|
|
|
781
974
|
async function createOAuthCallbackFailure(request, stateId, failureCode) {
|
|
782
975
|
const cleanupHeaders = new Headers();
|
|
783
976
|
let cleanupError;
|
|
977
|
+
const preserveRecoveryTicket = failureCode === OAuthCallbackFailureCodes.StaleTransaction && isOAuthRecoveryEligibleCallback(request);
|
|
784
978
|
if (stateId) try {
|
|
785
979
|
await deleteOAuthState(stateId);
|
|
786
980
|
} catch (error) {
|
|
@@ -791,6 +985,12 @@ async function createOAuthCallbackFailure(request, stateId, failureCode) {
|
|
|
791
985
|
} catch (error) {
|
|
792
986
|
cleanupError ??= error;
|
|
793
987
|
}
|
|
988
|
+
if (!preserveRecoveryTicket || cleanupError) try {
|
|
989
|
+
appendHeadersPreservingSetCookie(cleanupHeaders, await discardOAuthRecoveryForRequest(request));
|
|
990
|
+
} catch (error) {
|
|
991
|
+
cleanupError ??= error;
|
|
992
|
+
appendHeadersPreservingSetCookie(cleanupHeaders, clearOAuthRecoveryIntentHeaders(request));
|
|
993
|
+
}
|
|
794
994
|
appendOAuthStateCookieClear(cleanupHeaders);
|
|
795
995
|
const effectiveFailureCode = cleanupError ? OAuthCallbackFailureCodes.StorageError : failureCode;
|
|
796
996
|
if (cleanupError) logger.warn("OAuth callback cleanup failed", {
|
|
@@ -806,6 +1006,9 @@ async function createOAuthCallbackFailure(request, stateId, failureCode) {
|
|
|
806
1006
|
* @param returnUrlOrOptions - Either a return URL string or LoginOptions object
|
|
807
1007
|
*/
|
|
808
1008
|
async function login(request, returnUrlOrOptions) {
|
|
1009
|
+
let oauthStateId = null;
|
|
1010
|
+
let recoveryTicket = null;
|
|
1011
|
+
let recoveryCookie = null;
|
|
809
1012
|
try {
|
|
810
1013
|
const config = getAuthConfig();
|
|
811
1014
|
const options = typeof returnUrlOrOptions === "string" ? { returnUrl: returnUrlOrOptions } : returnUrlOrOptions || {};
|
|
@@ -837,7 +1040,15 @@ async function login(request, returnUrlOrOptions) {
|
|
|
837
1040
|
hasKcAction: Boolean(options.kcAction),
|
|
838
1041
|
extraAuthParamKeys: Object.keys(extraAuthParams)
|
|
839
1042
|
});
|
|
840
|
-
|
|
1043
|
+
oauthStateId = await createOAuthState(oauthState);
|
|
1044
|
+
if (!options.kcAction) {
|
|
1045
|
+
const recovery = await createOAuthRecoveryForLogin(request, {
|
|
1046
|
+
state,
|
|
1047
|
+
returnUrl
|
|
1048
|
+
}, config.redirectUri);
|
|
1049
|
+
recoveryTicket = recovery.ticket;
|
|
1050
|
+
recoveryCookie = recovery.cookie;
|
|
1051
|
+
}
|
|
841
1052
|
const authorizationParameters = {
|
|
842
1053
|
redirect_uri: config.redirectUri,
|
|
843
1054
|
scope: config.scope,
|
|
@@ -851,7 +1062,8 @@ async function login(request, returnUrlOrOptions) {
|
|
|
851
1062
|
if (options.kcAction) authorizationParameters.kc_action = options.kcAction;
|
|
852
1063
|
const authorizationUrl = oidc.buildAuthorizationUrl(oidcConfig, authorizationParameters);
|
|
853
1064
|
const headers = new Headers();
|
|
854
|
-
headers.append("Set-Cookie", serializeTemporaryAuthCookie(OAUTH_STATE_COOKIE,
|
|
1065
|
+
headers.append("Set-Cookie", serializeTemporaryAuthCookie(OAUTH_STATE_COOKIE, oauthStateId, { maxAge: OAUTH_STATE_TTL_SECONDS }));
|
|
1066
|
+
if (recoveryCookie) headers.append("Set-Cookie", recoveryCookie);
|
|
855
1067
|
logger.info("OIDC authorization flow initiated", {
|
|
856
1068
|
clientId: config.clientId,
|
|
857
1069
|
redirectUri: config.redirectUri,
|
|
@@ -863,6 +1075,16 @@ async function login(request, returnUrlOrOptions) {
|
|
|
863
1075
|
});
|
|
864
1076
|
return createRedirectResponse(authorizationUrl.toString(), { headers });
|
|
865
1077
|
} catch (error) {
|
|
1078
|
+
if (oauthStateId) try {
|
|
1079
|
+
await deleteOAuthState(oauthStateId);
|
|
1080
|
+
} catch (cleanupError) {
|
|
1081
|
+
logger.warn("OAuth login state cleanup failed", { errorType: cleanupError instanceof Error ? cleanupError.name : "UnknownError" });
|
|
1082
|
+
}
|
|
1083
|
+
if (recoveryTicket) try {
|
|
1084
|
+
await deleteOAuthRecoveryRecord(recoveryTicket);
|
|
1085
|
+
} catch (cleanupError) {
|
|
1086
|
+
logger.warn("OAuth recovery intent cleanup failed", { errorType: cleanupError instanceof Error ? cleanupError.name : "UnknownError" });
|
|
1087
|
+
}
|
|
866
1088
|
logger.error("Failed to initiate OAuth login", void 0, { errorType: error instanceof Error ? error.name : "UnknownError" });
|
|
867
1089
|
throw new Error("Login failed");
|
|
868
1090
|
}
|
|
@@ -916,9 +1138,11 @@ async function handleCallback(request) {
|
|
|
916
1138
|
if (oauthState && isUsableOAuthState(oauthState, requiresNonce) && oauthState.state === state && stateAge <= OAUTH_STATE_TTL_MS) {
|
|
917
1139
|
callbackStage = "storage";
|
|
918
1140
|
await deleteOAuthState(stateId);
|
|
1141
|
+
const recoveryHeaders = await discardOAuthRecoveryForRequest(request);
|
|
919
1142
|
callbackStage = "application_action";
|
|
920
1143
|
const headers = new Headers();
|
|
921
1144
|
appendOAuthStateCookieClear(headers);
|
|
1145
|
+
appendHeadersPreservingSetCookie(headers, recoveryHeaders);
|
|
922
1146
|
const redirectUrl = appendApplicationInitiatedActionResult(sanitizeOAuthReturnUrl(oauthState.returnUrl, request) ?? "/", kcAction || oauthState.kcAction, kcActionStatus);
|
|
923
1147
|
logger.info("Application initiated action returned with OAuth error", {
|
|
924
1148
|
hasError: Boolean(error),
|
|
@@ -943,6 +1167,7 @@ async function handleCallback(request) {
|
|
|
943
1167
|
if (stateId) await deleteOAuthState(stateId);
|
|
944
1168
|
const headers = await destroyAuthSession(request);
|
|
945
1169
|
appendOAuthStateCookieClear(headers);
|
|
1170
|
+
appendHeadersPreservingSetCookie(headers, await discardOAuthRecoveryForRequest(request));
|
|
946
1171
|
headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_COOKIE, "access_denied", { maxAge: 300 }));
|
|
947
1172
|
headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_DESC_COOKIE, "You do not have access to this application.", { maxAge: 300 }));
|
|
948
1173
|
const baseUrl = new URL(config.redirectUri).origin;
|
|
@@ -959,6 +1184,7 @@ async function handleCallback(request) {
|
|
|
959
1184
|
if (stateId) await deleteOAuthState(stateId);
|
|
960
1185
|
const headers = await destroyAuthSession(request);
|
|
961
1186
|
appendOAuthStateCookieClear(headers);
|
|
1187
|
+
appendHeadersPreservingSetCookie(headers, await discardOAuthRecoveryForRequest(request));
|
|
962
1188
|
const baseUrl = new URL(config.redirectUri).origin;
|
|
963
1189
|
return createRedirectResponse(baseUrl, { headers });
|
|
964
1190
|
}
|
|
@@ -966,6 +1192,7 @@ async function handleCallback(request) {
|
|
|
966
1192
|
if (stateId) await deleteOAuthState(stateId);
|
|
967
1193
|
const headers = await destroyAuthSession(request);
|
|
968
1194
|
appendOAuthStateCookieClear(headers);
|
|
1195
|
+
appendHeadersPreservingSetCookie(headers, await discardOAuthRecoveryForRequest(request));
|
|
969
1196
|
headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_COOKIE, error, { maxAge: 300 }));
|
|
970
1197
|
const baseUrl = new URL(config.redirectUri).origin;
|
|
971
1198
|
return createRedirectResponse(baseUrl, { headers });
|
|
@@ -993,9 +1220,11 @@ async function handleCallback(request) {
|
|
|
993
1220
|
if (kcActionStatus) {
|
|
994
1221
|
callbackStage = "storage";
|
|
995
1222
|
await deleteOAuthState(stateId);
|
|
1223
|
+
const recoveryHeaders = await discardOAuthRecoveryForRequest(request);
|
|
996
1224
|
callbackStage = "application_action";
|
|
997
1225
|
const headers = new Headers();
|
|
998
1226
|
appendOAuthStateCookieClear(headers);
|
|
1227
|
+
appendHeadersPreservingSetCookie(headers, recoveryHeaders);
|
|
999
1228
|
const redirectUrl = appendApplicationInitiatedActionResult(sanitizeOAuthReturnUrl(oauthState.returnUrl, request) ?? "/", kcAction || oauthState.kcAction, kcActionStatus);
|
|
1000
1229
|
logger.info("Application initiated action returned without authorization code", {
|
|
1001
1230
|
hasKcAction: Boolean(kcAction || oauthState.kcAction),
|
|
@@ -1023,9 +1252,11 @@ async function handleCallback(request) {
|
|
|
1023
1252
|
const sessionHeaders = await createAuthSession(request, newSessionData);
|
|
1024
1253
|
callbackStage = "storage";
|
|
1025
1254
|
await deleteOAuthState(stateId);
|
|
1255
|
+
const recoveryHeaders = await discardOAuthRecoveryForRequest(request);
|
|
1026
1256
|
const headers = new Headers();
|
|
1027
1257
|
appendHeadersPreservingSetCookie(headers, sessionHeaders);
|
|
1028
1258
|
appendOAuthStateCookieClear(headers);
|
|
1259
|
+
appendHeadersPreservingSetCookie(headers, recoveryHeaders);
|
|
1029
1260
|
logger.info("OAuth callback processed successfully");
|
|
1030
1261
|
return createRedirectResponse(appendApplicationInitiatedActionResult(sanitizeOAuthReturnUrl(oauthState.returnUrl, request) ?? "/", kcAction || oauthState.kcAction, kcActionStatus), { headers });
|
|
1031
1262
|
} catch (error) {
|
|
@@ -1608,6 +1839,6 @@ function shouldAttemptTokenRefresh(response, hasAuth) {
|
|
|
1608
1839
|
return hasAuth && isTokenExpiredResponse(response);
|
|
1609
1840
|
}
|
|
1610
1841
|
//#endregion
|
|
1611
|
-
export { resetAuthClaimMapping as A, handleCallback as C, logout as D, login as E, OAuthCallbackFailureCodes as
|
|
1842
|
+
export { resetAuthClaimMapping as A, handleCallback as C, logout as D, login as E, OAuthCallbackError as F, OAuthCallbackFailureCodes as I, isOAuthCallbackError as L, clearOAuthRecoveryIntentHeaders as M, consumeOAuthRecoveryIntent as N, refreshTokens as O, getOAuthRecoveryCookiePath as P, isRecoverableOAuthCallbackError as R, handleBackChannelLogout as S, hasAuthError as T, createAuthorizationResponseUrl as _, configureRouteProtection as a, getAuthError as b, protectRoute as c, resetRouteProtectionConfig as d, shouldRefreshToken as f, configureAuthClaimMapping as g, clearAuthServerCache as h, authRoute as i, OAUTH_RECOVERY_COOKIE_NAME as j, requireAuth as k, protectedRoute as l, clearAuthErrorHeaders as m, isTokenExpiredResponse as n, onboardingRoute as o, subscriptionRoute as p, shouldAttemptTokenRefresh as r, policyRoute as s, attemptTokenRefresh as t, publicRoute as u, extractUserInfo as v, handleFrontChannelLogout as w, getUser as x, getAccessToken as y };
|
|
1612
1843
|
|
|
1613
|
-
//# sourceMappingURL=server-
|
|
1844
|
+
//# sourceMappingURL=server-BJTz5-BE.mjs.map
|