UNPKG

wcz-layout

Version:

109 lines (108 loc) 4.8 kB
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