@eminuckan/spine 0.3.8 → 0.3.9
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 +16 -0
- package/README.md +10 -0
- package/dist/auth/auth.server.d.ts.map +1 -1
- package/dist/auth/callback-errors.d.ts +42 -0
- package/dist/auth/callback-errors.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 -0
- package/dist/auth/redis-session-storage.server.d.ts +3 -1
- package/dist/auth/redis-session-storage.server.d.ts.map +1 -1
- package/dist/auth/server.d.ts +1 -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-n0lWWyoh.mjs → redis-session-storage.server-CI_IYIn8.mjs} +27 -12
- package/dist/redis-session-storage.server-CI_IYIn8.mjs.map +1 -0
- package/dist/{server-C3_iNZfj.mjs → server-Cw86FdT-.mjs} +213 -44
- package/dist/server-Cw86FdT-.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 +11 -0
- package/docs/module-reference.md +4 -0
- package/package.json +1 -1
- package/dist/redis-session-storage.server-n0lWWyoh.mjs.map +0 -1
- package/dist/server-C3_iNZfj.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "./logging/index.mjs";
|
|
2
|
-
import { S as updateAuthSession, a as createOAuthState, d as destroyAuthSessionsBySid, f as destroyAuthSessionsForUser, g as getOAuthState, i as createAuthSession, l as destroyAuthSessionsByIdentitySession, o as deleteOAuthState, p as getAuthSession, s as destroyAuthSession, t as AUTH_ERROR_COOKIE_PREFIX, v as listAuthSessionDataForUser } from "./redis-session-storage.server-
|
|
2
|
+
import { C as OAUTH_STATE_TTL_MS, S as updateAuthSession, a as createOAuthState, d as destroyAuthSessionsBySid, f as destroyAuthSessionsForUser, g as getOAuthState, i as createAuthSession, l as destroyAuthSessionsByIdentitySession, o as deleteOAuthState, p as getAuthSession, s as destroyAuthSession, t as AUTH_ERROR_COOKIE_PREFIX, v as listAuthSessionDataForUser, w as OAUTH_STATE_TTL_SECONDS } from "./redis-session-storage.server-CI_IYIn8.mjs";
|
|
3
3
|
import * as oidc from "openid-client";
|
|
4
4
|
import { createRemoteJWKSet, errors, jwtVerify } from "jose";
|
|
5
5
|
//#region src/http/response.ts
|
|
@@ -132,6 +132,75 @@ function serializeTemporaryAuthCookie(name, value, options) {
|
|
|
132
132
|
return segments.join("; ");
|
|
133
133
|
}
|
|
134
134
|
//#endregion
|
|
135
|
+
//#region src/auth/callback-errors.ts
|
|
136
|
+
const OAuthCallbackFailureCodes = {
|
|
137
|
+
StaleTransaction: "stale_transaction",
|
|
138
|
+
StateMismatch: "state_mismatch",
|
|
139
|
+
MalformedCallback: "malformed_callback",
|
|
140
|
+
ApplicationActionFailed: "application_action_failed",
|
|
141
|
+
TokenExchangeFailed: "token_exchange_failed",
|
|
142
|
+
SessionCreationFailed: "session_creation_failed",
|
|
143
|
+
StorageError: "storage_error",
|
|
144
|
+
ConfigurationError: "configuration_error"
|
|
145
|
+
};
|
|
146
|
+
const OAUTH_CALLBACK_ERROR_BRAND = Symbol.for("@eminuckan/spine/OAuthCallbackError");
|
|
147
|
+
function isOAuthCallbackFailureCode(value) {
|
|
148
|
+
return typeof value === "string" && Object.values(OAuthCallbackFailureCodes).includes(value);
|
|
149
|
+
}
|
|
150
|
+
function isHeadersLike(value) {
|
|
151
|
+
return value !== null && typeof value === "object" && typeof value.get === "function" && typeof value.append === "function";
|
|
152
|
+
}
|
|
153
|
+
function getSetCookieValues(headers) {
|
|
154
|
+
const getSetCookie = headers.getSetCookie;
|
|
155
|
+
if (getSetCookie) return getSetCookie.call(headers);
|
|
156
|
+
return Array.from(headers).reduce((cookies, [name, value]) => {
|
|
157
|
+
if (name.toLowerCase() === "set-cookie") cookies.push(value);
|
|
158
|
+
return cookies;
|
|
159
|
+
}, []);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A callback failure that consumers can classify without parsing messages.
|
|
163
|
+
*
|
|
164
|
+
* The message is intentionally constant. Consumers should branch on
|
|
165
|
+
* `code`, not on `message`, and should merge `cleanupHeaders` (or the
|
|
166
|
+
* lossless `cleanupSetCookies` values) with any response they create using
|
|
167
|
+
* repeated `Set-Cookie` appends.
|
|
168
|
+
*/
|
|
169
|
+
var OAuthCallbackError = class extends Error {
|
|
170
|
+
code;
|
|
171
|
+
cleanupHeaders;
|
|
172
|
+
cleanupSetCookies;
|
|
173
|
+
constructor(code, cleanupHeaders = {}) {
|
|
174
|
+
super("Authentication failed");
|
|
175
|
+
this.name = "OAuthCallbackError";
|
|
176
|
+
this.code = code;
|
|
177
|
+
this.cleanupHeaders = new Headers(cleanupHeaders);
|
|
178
|
+
this.cleanupSetCookies = getSetCookieValues(this.cleanupHeaders);
|
|
179
|
+
Object.defineProperty(this, OAUTH_CALLBACK_ERROR_BRAND, {
|
|
180
|
+
configurable: false,
|
|
181
|
+
enumerable: false,
|
|
182
|
+
value: true,
|
|
183
|
+
writable: false
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Cross-package-copy safe type guard for callback failures.
|
|
189
|
+
*/
|
|
190
|
+
function isOAuthCallbackError(error) {
|
|
191
|
+
if (error instanceof OAuthCallbackError) return true;
|
|
192
|
+
if (error === null || typeof error !== "object") return false;
|
|
193
|
+
const candidate = error;
|
|
194
|
+
return candidate[OAUTH_CALLBACK_ERROR_BRAND] === true && isOAuthCallbackFailureCode(candidate.code) && isHeadersLike(candidate.cleanupHeaders);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Only a normal authorization callback whose local transaction is absent or
|
|
198
|
+
* expired may be retried. All other callback failures fail closed.
|
|
199
|
+
*/
|
|
200
|
+
function isRecoverableOAuthCallbackError(error) {
|
|
201
|
+
return isOAuthCallbackError(error) && error.code === "stale_transaction";
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
135
204
|
//#region src/auth/auth.server.ts
|
|
136
205
|
/**
|
|
137
206
|
* OpenID Connect Authentication Server Module
|
|
@@ -384,11 +453,12 @@ async function getOAuthConfig() {
|
|
|
384
453
|
});
|
|
385
454
|
return configuration;
|
|
386
455
|
} catch (error) {
|
|
387
|
-
logger.error("OIDC discovery failed",
|
|
456
|
+
logger.error("OIDC discovery failed", void 0, {
|
|
388
457
|
authority: config.authority,
|
|
389
|
-
clientId: config.clientId
|
|
458
|
+
clientId: config.clientId,
|
|
459
|
+
errorType: error instanceof Error ? error.name : "UnknownError"
|
|
390
460
|
});
|
|
391
|
-
throw new Error(
|
|
461
|
+
throw new Error("OIDC discovery failed");
|
|
392
462
|
}
|
|
393
463
|
}
|
|
394
464
|
function getJwks(jwksUri) {
|
|
@@ -661,6 +731,74 @@ function clearAuthErrorHeaders() {
|
|
|
661
731
|
function hasAuthError(request) {
|
|
662
732
|
return request.headers.get("Cookie")?.includes(`${AUTH_ERROR_COOKIE}=`) ?? false;
|
|
663
733
|
}
|
|
734
|
+
function escapeRegExp(value) {
|
|
735
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
736
|
+
}
|
|
737
|
+
function getOAuthStateIdFromRequest(request) {
|
|
738
|
+
const cookies = request.headers.get("Cookie");
|
|
739
|
+
if (!cookies) return null;
|
|
740
|
+
const cookieRegex = new RegExp(`(?:^|;\\s*)${escapeRegExp(OAUTH_STATE_COOKIE)}=([^;]*)`);
|
|
741
|
+
const cookieValue = cookies.match(cookieRegex)?.[1];
|
|
742
|
+
if (!cookieValue) return null;
|
|
743
|
+
try {
|
|
744
|
+
const stateId = decodeURIComponent(cookieValue);
|
|
745
|
+
return stateId.length > 0 ? stateId : null;
|
|
746
|
+
} catch {
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function hasNonEmptyCallbackValue(value) {
|
|
751
|
+
return value !== null && value.trim().length > 0;
|
|
752
|
+
}
|
|
753
|
+
function appendHeadersPreservingSetCookie(target, source) {
|
|
754
|
+
const sourceHeaders = new Headers(source);
|
|
755
|
+
const setCookies = sourceHeaders.getSetCookie?.call(sourceHeaders) ?? Array.from(sourceHeaders).filter(([name]) => name.toLowerCase() === "set-cookie").map(([, value]) => value);
|
|
756
|
+
if (setCookies.length > 0) for (const cookie of setCookies) target.append("Set-Cookie", cookie);
|
|
757
|
+
else {
|
|
758
|
+
const setCookie = sourceHeaders.get("Set-Cookie");
|
|
759
|
+
if (setCookie) target.append("Set-Cookie", setCookie);
|
|
760
|
+
}
|
|
761
|
+
for (const [name, value] of sourceHeaders) if (name.toLowerCase() !== "set-cookie") target.set(name, value);
|
|
762
|
+
}
|
|
763
|
+
function appendOAuthStateCookieClear(headers) {
|
|
764
|
+
headers.append("Set-Cookie", serializeTemporaryAuthCookie(OAUTH_STATE_COOKIE, "", { maxAge: 0 }));
|
|
765
|
+
}
|
|
766
|
+
function isUsableOAuthState(state, requireNonce) {
|
|
767
|
+
return state !== null && typeof state.state === "string" && state.state.length > 0 && typeof state.codeVerifier === "string" && state.codeVerifier.length > 0 && (state.nonce === void 0 ? !requireNonce : typeof state.nonce === "string" && state.nonce.length > 0) && typeof state.createdAt === "number" && Number.isFinite(state.createdAt) && (state.returnUrl === void 0 || typeof state.returnUrl === "string") && (state.kcAction === void 0 || typeof state.kcAction === "string");
|
|
768
|
+
}
|
|
769
|
+
function getCallbackFailureCode(error, stage) {
|
|
770
|
+
if (isOAuthCallbackError(error)) return error.code;
|
|
771
|
+
switch (stage) {
|
|
772
|
+
case "configuration": return OAuthCallbackFailureCodes.ConfigurationError;
|
|
773
|
+
case "state":
|
|
774
|
+
case "storage": return OAuthCallbackFailureCodes.StorageError;
|
|
775
|
+
case "application_action": return OAuthCallbackFailureCodes.ApplicationActionFailed;
|
|
776
|
+
case "token_exchange": return OAuthCallbackFailureCodes.TokenExchangeFailed;
|
|
777
|
+
case "session_creation": return OAuthCallbackFailureCodes.SessionCreationFailed;
|
|
778
|
+
default: return OAuthCallbackFailureCodes.StorageError;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
async function createOAuthCallbackFailure(request, stateId, failureCode) {
|
|
782
|
+
const cleanupHeaders = new Headers();
|
|
783
|
+
let cleanupError;
|
|
784
|
+
if (stateId) try {
|
|
785
|
+
await deleteOAuthState(stateId);
|
|
786
|
+
} catch (error) {
|
|
787
|
+
cleanupError = error;
|
|
788
|
+
}
|
|
789
|
+
try {
|
|
790
|
+
appendHeadersPreservingSetCookie(cleanupHeaders, await destroyAuthSession(request));
|
|
791
|
+
} catch (error) {
|
|
792
|
+
cleanupError ??= error;
|
|
793
|
+
}
|
|
794
|
+
appendOAuthStateCookieClear(cleanupHeaders);
|
|
795
|
+
const effectiveFailureCode = cleanupError ? OAuthCallbackFailureCodes.StorageError : failureCode;
|
|
796
|
+
if (cleanupError) logger.warn("OAuth callback cleanup failed", {
|
|
797
|
+
failureCode: effectiveFailureCode,
|
|
798
|
+
errorType: cleanupError instanceof Error ? cleanupError.name : "UnknownError"
|
|
799
|
+
});
|
|
800
|
+
return new OAuthCallbackError(effectiveFailureCode, cleanupHeaders);
|
|
801
|
+
}
|
|
664
802
|
/**
|
|
665
803
|
* Initiate OAuth login flow
|
|
666
804
|
*
|
|
@@ -713,7 +851,7 @@ async function login(request, returnUrlOrOptions) {
|
|
|
713
851
|
if (options.kcAction) authorizationParameters.kc_action = options.kcAction;
|
|
714
852
|
const authorizationUrl = oidc.buildAuthorizationUrl(oidcConfig, authorizationParameters);
|
|
715
853
|
const headers = new Headers();
|
|
716
|
-
headers.append("Set-Cookie", serializeTemporaryAuthCookie(OAUTH_STATE_COOKIE, stateId, { maxAge:
|
|
854
|
+
headers.append("Set-Cookie", serializeTemporaryAuthCookie(OAUTH_STATE_COOKIE, stateId, { maxAge: OAUTH_STATE_TTL_SECONDS }));
|
|
717
855
|
logger.info("OIDC authorization flow initiated", {
|
|
718
856
|
clientId: config.clientId,
|
|
719
857
|
redirectUri: config.redirectUri,
|
|
@@ -742,9 +880,13 @@ async function login(request, returnUrlOrOptions) {
|
|
|
742
880
|
* - Redirect to home to start fresh login
|
|
743
881
|
*/
|
|
744
882
|
async function handleCallback(request) {
|
|
883
|
+
let stateId = null;
|
|
884
|
+
let callbackStage = "configuration";
|
|
745
885
|
try {
|
|
886
|
+
stateId = getOAuthStateIdFromRequest(request);
|
|
746
887
|
const config = getAuthConfig();
|
|
747
888
|
const hasLandingPage = getEffectiveHasLandingPage(config);
|
|
889
|
+
const requiresNonce = config.scope.split(/\s+/).includes("openid");
|
|
748
890
|
const url = new URL(request.url);
|
|
749
891
|
const code = url.searchParams.get("code");
|
|
750
892
|
const state = url.searchParams.get("state");
|
|
@@ -752,28 +894,31 @@ async function handleCallback(request) {
|
|
|
752
894
|
const errorDescription = url.searchParams.get("error_description");
|
|
753
895
|
const kcAction = url.searchParams.get("kc_action");
|
|
754
896
|
const kcActionStatus = url.searchParams.get("kc_action_status");
|
|
897
|
+
const hasCode = hasNonEmptyCallbackValue(code);
|
|
898
|
+
const hasState = hasNonEmptyCallbackValue(state);
|
|
899
|
+
const hasApplicationAction = Boolean(hasNonEmptyCallbackValue(kcAction) || hasNonEmptyCallbackValue(kcActionStatus));
|
|
755
900
|
logger.info("OIDC callback received", {
|
|
756
|
-
hasCode
|
|
757
|
-
hasState
|
|
901
|
+
hasCode,
|
|
902
|
+
hasState,
|
|
758
903
|
hasError: Boolean(error),
|
|
759
904
|
hasErrorDescription: Boolean(errorDescription),
|
|
760
|
-
hasKcAction:
|
|
761
|
-
hasKcActionStatus:
|
|
905
|
+
hasKcAction: hasNonEmptyCallbackValue(kcAction),
|
|
906
|
+
hasKcActionStatus: hasNonEmptyCallbackValue(kcActionStatus),
|
|
762
907
|
clientId: config.clientId,
|
|
763
908
|
redirectUri: config.redirectUri
|
|
764
909
|
});
|
|
765
910
|
if (error) {
|
|
766
|
-
if (kcActionStatus &&
|
|
767
|
-
|
|
768
|
-
const oauthStateRegex = new RegExp(`${OAUTH_STATE_COOKIE}=([^;]+)`);
|
|
769
|
-
const stateId = (cookies?.match(oauthStateRegex))?.[1];
|
|
911
|
+
if (kcActionStatus && hasState) {
|
|
912
|
+
callbackStage = "state";
|
|
770
913
|
if (stateId) {
|
|
771
|
-
const oauthState = await getOAuthState(stateId);
|
|
914
|
+
const oauthState = await getOAuthState(stateId, { throwOnMalformed: true });
|
|
772
915
|
const stateAge = oauthState ? Date.now() - oauthState.createdAt : Number.POSITIVE_INFINITY;
|
|
773
|
-
if (oauthState && oauthState.state === state && stateAge <=
|
|
916
|
+
if (oauthState && isUsableOAuthState(oauthState, requiresNonce) && oauthState.state === state && stateAge <= OAUTH_STATE_TTL_MS) {
|
|
917
|
+
callbackStage = "storage";
|
|
774
918
|
await deleteOAuthState(stateId);
|
|
919
|
+
callbackStage = "application_action";
|
|
775
920
|
const headers = new Headers();
|
|
776
|
-
headers
|
|
921
|
+
appendOAuthStateCookieClear(headers);
|
|
777
922
|
const redirectUrl = appendApplicationInitiatedActionResult(sanitizeOAuthReturnUrl(oauthState.returnUrl, request) ?? "/", kcAction || oauthState.kcAction, kcActionStatus);
|
|
778
923
|
logger.info("Application initiated action returned with OAuth error", {
|
|
779
924
|
hasError: Boolean(error),
|
|
@@ -794,9 +939,12 @@ async function handleCallback(request) {
|
|
|
794
939
|
});
|
|
795
940
|
if (error === "access_denied") {
|
|
796
941
|
logger.warn("Access denied for this application", { clientId: config.clientId });
|
|
942
|
+
callbackStage = "storage";
|
|
943
|
+
if (stateId) await deleteOAuthState(stateId);
|
|
797
944
|
const headers = await destroyAuthSession(request);
|
|
945
|
+
appendOAuthStateCookieClear(headers);
|
|
798
946
|
headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_COOKIE, "access_denied", { maxAge: 300 }));
|
|
799
|
-
headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_DESC_COOKIE,
|
|
947
|
+
headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_DESC_COOKIE, "You do not have access to this application.", { maxAge: 300 }));
|
|
800
948
|
const baseUrl = new URL(config.redirectUri).origin;
|
|
801
949
|
logger.info("Redirecting to /auth/login to show access_denied error", {
|
|
802
950
|
hasLandingPage,
|
|
@@ -807,30 +955,47 @@ async function handleCallback(request) {
|
|
|
807
955
|
}
|
|
808
956
|
if (error === "login_required") {
|
|
809
957
|
logger.info("Login required, user session may have expired");
|
|
958
|
+
callbackStage = "storage";
|
|
959
|
+
if (stateId) await deleteOAuthState(stateId);
|
|
810
960
|
const headers = await destroyAuthSession(request);
|
|
961
|
+
appendOAuthStateCookieClear(headers);
|
|
811
962
|
const baseUrl = new URL(config.redirectUri).origin;
|
|
812
963
|
return createRedirectResponse(baseUrl, { headers });
|
|
813
964
|
}
|
|
965
|
+
callbackStage = "storage";
|
|
966
|
+
if (stateId) await deleteOAuthState(stateId);
|
|
814
967
|
const headers = await destroyAuthSession(request);
|
|
968
|
+
appendOAuthStateCookieClear(headers);
|
|
815
969
|
headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_COOKIE, error, { maxAge: 300 }));
|
|
816
|
-
if (errorDescription) headers.append("Set-Cookie", serializeTemporaryAuthCookie(AUTH_ERROR_DESC_COOKIE, errorDescription, { maxAge: 300 }));
|
|
817
970
|
const baseUrl = new URL(config.redirectUri).origin;
|
|
818
971
|
return createRedirectResponse(baseUrl, { headers });
|
|
819
972
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (
|
|
829
|
-
|
|
973
|
+
if (!hasState) {
|
|
974
|
+
callbackStage = hasApplicationAction ? "application_action" : "state";
|
|
975
|
+
throw new OAuthCallbackError(hasApplicationAction ? OAuthCallbackFailureCodes.ApplicationActionFailed : OAuthCallbackFailureCodes.MalformedCallback);
|
|
976
|
+
}
|
|
977
|
+
if (!hasCode && !hasApplicationAction) {
|
|
978
|
+
callbackStage = "state";
|
|
979
|
+
throw new OAuthCallbackError(OAuthCallbackFailureCodes.MalformedCallback);
|
|
980
|
+
}
|
|
981
|
+
if (!stateId) {
|
|
982
|
+
callbackStage = hasApplicationAction ? "application_action" : "state";
|
|
983
|
+
throw new OAuthCallbackError(hasApplicationAction ? OAuthCallbackFailureCodes.ApplicationActionFailed : OAuthCallbackFailureCodes.StaleTransaction);
|
|
984
|
+
}
|
|
985
|
+
callbackStage = "state";
|
|
986
|
+
const oauthState = await getOAuthState(stateId, { throwOnMalformed: true });
|
|
987
|
+
if (!oauthState) throw new OAuthCallbackError(hasApplicationAction ? OAuthCallbackFailureCodes.ApplicationActionFailed : OAuthCallbackFailureCodes.StaleTransaction);
|
|
988
|
+
if (!isUsableOAuthState(oauthState, requiresNonce)) throw new OAuthCallbackError(OAuthCallbackFailureCodes.StorageError);
|
|
989
|
+
if (oauthState.state !== state) throw new OAuthCallbackError(OAuthCallbackFailureCodes.StateMismatch);
|
|
990
|
+
const isApplicationAction = Boolean(hasApplicationAction || oauthState.kcAction);
|
|
991
|
+
if (Date.now() - oauthState.createdAt > OAUTH_STATE_TTL_MS) throw new OAuthCallbackError(isApplicationAction ? OAuthCallbackFailureCodes.ApplicationActionFailed : OAuthCallbackFailureCodes.StaleTransaction);
|
|
992
|
+
if (!hasCode) {
|
|
830
993
|
if (kcActionStatus) {
|
|
994
|
+
callbackStage = "storage";
|
|
831
995
|
await deleteOAuthState(stateId);
|
|
996
|
+
callbackStage = "application_action";
|
|
832
997
|
const headers = new Headers();
|
|
833
|
-
headers
|
|
998
|
+
appendOAuthStateCookieClear(headers);
|
|
834
999
|
const redirectUrl = appendApplicationInitiatedActionResult(sanitizeOAuthReturnUrl(oauthState.returnUrl, request) ?? "/", kcAction || oauthState.kcAction, kcActionStatus);
|
|
835
1000
|
logger.info("Application initiated action returned without authorization code", {
|
|
836
1001
|
hasKcAction: Boolean(kcAction || oauthState.kcAction),
|
|
@@ -838,11 +1003,13 @@ async function handleCallback(request) {
|
|
|
838
1003
|
});
|
|
839
1004
|
return createRedirectResponse(redirectUrl, { headers });
|
|
840
1005
|
}
|
|
841
|
-
throw new
|
|
1006
|
+
throw new OAuthCallbackError(isApplicationAction ? OAuthCallbackFailureCodes.ApplicationActionFailed : OAuthCallbackFailureCodes.MalformedCallback);
|
|
842
1007
|
}
|
|
843
1008
|
logger.info("Initiating token exchange");
|
|
1009
|
+
callbackStage = "configuration";
|
|
844
1010
|
const oidcConfig = await getOAuthConfig();
|
|
845
1011
|
const authorizationResponseUrl = createAuthorizationResponseUrl(request.url, config.redirectUri);
|
|
1012
|
+
callbackStage = "token_exchange";
|
|
846
1013
|
const tokenResult = await oidc.authorizationCodeGrant(oidcConfig, authorizationResponseUrl, {
|
|
847
1014
|
pkceCodeVerifier: oauthState.codeVerifier,
|
|
848
1015
|
expectedState: oauthState.state,
|
|
@@ -850,22 +1017,24 @@ async function handleCallback(request) {
|
|
|
850
1017
|
idTokenExpected: config.scope.split(/\s+/).includes("openid")
|
|
851
1018
|
});
|
|
852
1019
|
logger.info("Token exchange completed successfully");
|
|
853
|
-
|
|
1020
|
+
callbackStage = "session_creation";
|
|
1021
|
+
const newSessionData = await createSessionData(tokenResult, getClaimsFromTokenResponse(tokenResult));
|
|
1022
|
+
callbackStage = "session_creation";
|
|
1023
|
+
const sessionHeaders = await createAuthSession(request, newSessionData);
|
|
1024
|
+
callbackStage = "storage";
|
|
854
1025
|
await deleteOAuthState(stateId);
|
|
855
|
-
const headers = new Headers(
|
|
856
|
-
headers
|
|
1026
|
+
const headers = new Headers();
|
|
1027
|
+
appendHeadersPreservingSetCookie(headers, sessionHeaders);
|
|
1028
|
+
appendOAuthStateCookieClear(headers);
|
|
857
1029
|
logger.info("OAuth callback processed successfully");
|
|
858
1030
|
return createRedirectResponse(appendApplicationInitiatedActionResult(sanitizeOAuthReturnUrl(oauthState.returnUrl, request) ?? "/", kcAction || oauthState.kcAction, kcActionStatus), { headers });
|
|
859
1031
|
} catch (error) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
} catch {}
|
|
867
|
-
await destroyAuthSession(request);
|
|
868
|
-
throw new Error("Authentication failed");
|
|
1032
|
+
const failureCode = getCallbackFailureCode(error, callbackStage);
|
|
1033
|
+
logger.error("OAuth callback failed", void 0, {
|
|
1034
|
+
failureCode,
|
|
1035
|
+
errorType: error instanceof Error ? error.name : "UnknownError"
|
|
1036
|
+
});
|
|
1037
|
+
throw await createOAuthCallbackFailure(request, stateId, failureCode);
|
|
869
1038
|
}
|
|
870
1039
|
}
|
|
871
1040
|
/**
|
|
@@ -1439,6 +1608,6 @@ function shouldAttemptTokenRefresh(response, hasAuth) {
|
|
|
1439
1608
|
return hasAuth && isTokenExpiredResponse(response);
|
|
1440
1609
|
}
|
|
1441
1610
|
//#endregion
|
|
1442
|
-
export { resetAuthClaimMapping as A, handleCallback as C, logout as D, login as E, refreshTokens as O, 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, 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 };
|
|
1611
|
+
export { resetAuthClaimMapping as A, handleCallback as C, logout as D, login as E, OAuthCallbackFailureCodes as M, isOAuthCallbackError as N, refreshTokens as O, isRecoverableOAuthCallbackError as P, 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, OAuthCallbackError 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 };
|
|
1443
1612
|
|
|
1444
|
-
//# sourceMappingURL=server-
|
|
1613
|
+
//# sourceMappingURL=server-Cw86FdT-.mjs.map
|