wcz-layout
Version:
88 lines (87 loc) • 3.61 kB
JavaScript
import { i as getAppSession, t as getAccessToken } from "./user-BQiWoQk1.mjs";
import { i as buildUser } from "./utils-DpcYsXTK.mjs";
import { n as getTokenOnBehalfOf, t as getAppToken } from "./msalServer-BHuM63vM.mjs";
//#region src/lib/auth/authHandlers.ts
/**
* Server-side handlers for the OAuth2 Authorization Code (PKCE) flow. They are
* mounted by thin route files (`/auth/login`, `/auth/callback`, `/auth/logout`)
* in the consuming app and drive the full server-side Entra login — the browser
* never loads an auth library.
*/
const redirect = (location) => new Response(null, {
status: 302,
headers: { Location: location }
});
/** The OAuth redirect URI, derived from the request origin. */
const resolveRedirectUri = (origin) => `${origin}/auth/callback`;
/** Only same-origin paths are allowed, so `returnTo` cannot become an open redirect. */
const sanitizeReturnTo = (value) => value?.startsWith("/") && !value.startsWith("//") ? value : "/";
/** GET /auth/login — start the flow: stash PKCE + state, redirect to Entra. */
async function handleLogin() {
const { getRequestUrl } = await import("@tanstack/react-start/server");
const url = getRequestUrl();
const returnTo = sanitizeReturnTo(url.searchParams.get("returnTo"));
const { cryptoProvider, buildAuthCodeUrl } = await import("./entra-DbC3aZkF.mjs");
const { verifier, challenge } = await cryptoProvider.generatePkceCodes();
const state = cryptoProvider.createNewGuid();
await (await getAppSession()).update({
pkceVerifier: verifier,
state,
returnTo
});
return redirect(await buildAuthCodeUrl({
redirectUri: resolveRedirectUri(url.origin),
state,
codeChallenge: challenge
}));
}
/** GET /auth/callback — finish the flow: validate state, exchange code, set session. */
async function handleCallback() {
const { getRequestUrl } = await import("@tanstack/react-start/server");
const url = getRequestUrl();
const session = await getAppSession();
const { pkceVerifier, state: expectedState, returnTo = "/" } = session.data;
const error = url.searchParams.get("error");
if (error) {
await session.clear();
return redirect(`/?auth_error=${encodeURIComponent(error)}`);
}
const code = url.searchParams.get("code");
const state = url.searchParams.get("state");
if (!code || !state || !pkceVerifier || state !== expectedState) {
await session.clear();
return redirect("/?auth_error=invalid_state");
}
try {
const { exchangeCodeForSession } = await import("./entra-DbC3aZkF.mjs");
const { refreshToken, claims } = await exchangeCodeForSession({
code,
redirectUri: resolveRedirectUri(url.origin),
codeVerifier: pkceVerifier
});
await session.update({
refreshToken,
user: buildUser(claims),
pkceVerifier: void 0,
state: void 0,
returnTo: void 0
});
return redirect(returnTo);
} catch (error) {
const message = error instanceof Error ? error.message : "token_exchange_failed";
console.error("[auth] code exchange failed:", message);
await session.clear();
return redirect(`/?auth_error=${encodeURIComponent(message)}`);
}
}
/** GET /auth/logout — clear the local session and sign out of the Entra session. */
async function handleLogout() {
const { getRequestUrl } = await import("@tanstack/react-start/server");
const url = getRequestUrl();
await (await getAppSession()).clear();
const { buildLogoutUrl } = await import("./entra-DbC3aZkF.mjs");
return redirect(buildLogoutUrl(`${url.origin}/`));
}
//#endregion
export { getAccessToken, getAppToken, getTokenOnBehalfOf, handleCallback, handleLogin, handleLogout };
//# sourceMappingURL=auth.mjs.map