wcz-layout
Version:
109 lines (108 loc) • 4.8 kB
JavaScript
import { r as providers, t as getProvider } from "./providers-Dbl95AxR.mjs";
import { d as sanitizeReturnTo, g as getAppSession, i as buildUser } from "./utils-KaM5VMTJ.mjs";
import { a as createState, i as createPkce, n as getAppToken, o as exchangeCode, r as buildAuthUrl, t as getAccessToken } from "./tokens-D8CVIFTb.mjs";
import * as jose from "jose";
//#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`)
* and drive the full server-side login — the browser never loads an auth library.
* The chosen provider travels in the session alongside the PKCE verifier and
* `state`, so one callback route serves every provider.
*/
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`;
const resolveProviderId = (value) => {
return providers.find((candidate) => candidate.id === value)?.id ?? "entra";
};
/**
* GET /auth/login — start the flow: stash PKCE + state + provider, redirect to the IdP.
* Without `?provider=` and with more than one provider registered, sends the user to
* the chooser at `/login` instead; a single provider (whichever it is) starts right away.
*/
async function handleLogin() {
const { getRequestUrl } = await import("@tanstack/react-start/server");
const url = getRequestUrl();
const returnTo = sanitizeReturnTo(url.searchParams.get("returnTo"));
const requestedProvider = url.searchParams.get("provider");
if (!requestedProvider && providers.length > 1) return redirect(`/login?returnTo=${encodeURIComponent(returnTo)}`);
const providerId = requestedProvider ? resolveProviderId(requestedProvider) : providers[0].id;
const { verifier, challenge } = createPkce();
const state = createState();
await (await getAppSession()).update({
pkceVerifier: verifier,
state,
returnTo,
provider: providerId
});
return redirect(buildAuthUrl(getProvider(providerId), {
redirectUri: resolveRedirectUri(url.origin),
state,
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 = "/", provider: providerId = "entra" } = 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 result = await exchangeCode(getProvider(providerId), {
code,
redirectUri: resolveRedirectUri(url.origin),
codeVerifier: pkceVerifier
});
if (!result.refresh_token) throw new Error("No refresh token returned (is the offline_access scope granted?)");
if (!result.id_token) throw new Error("No id token returned (is the openid scope granted?)");
const claims = jose.decodeJwt(result.id_token);
const user = buildUser(claims, providerId);
if (providerId === "aws" && user.employeeId) {
const { getEmployeeByEmployeeId } = await import("./peoplesoft-CQnc01Hc.mjs").then((n) => n.v);
const employee = await getEmployeeByEmployeeId({ data: { employeeId: user.employeeId } });
user.name = employee.nameTitleCase;
user.department = employee.departmentId.toUpperCase();
user.companyName = employee.companyCode;
}
await session.update({
refreshToken: result.refresh_token,
user,
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 provider's session. */
async function handleLogout() {
const { getRequestUrl } = await import("@tanstack/react-start/server");
const url = getRequestUrl();
const session = await getAppSession();
const providerId = session.data.provider ?? "entra";
await session.clear();
return redirect(getProvider(providerId).logoutUrl(`${url.origin}/`));
}
//#endregion
export { getAccessToken, getAppToken, handleCallback, handleLogin, handleLogout };
//# sourceMappingURL=auth.mjs.map