import { ADMIN_APP_URL, API_BASE_URL } from "@/lib/env"; /** * Where signing in and out happens - which is never here. Mirrors * `lib/admin-auth.ts` in nachklang-feedback; keep the two in step. * * The session is an httpOnly cookie that the *API* sets on `.nachklang.art`, * shared by the calendar, feedback, tickets and admin apps. This app can * neither read nor mint it, so a 401 is not something it can fix by * re-authenticating: it hands the user over to admin.nachklang.art and lets * that app decide. */ /** * The admin app's login URL, carrying an absolute `?redirect=` back to where * the user is now. The admin app checks that target against its own * allow-list by exact origin, so an origin missing from its * NEXT_PUBLIC_ALLOWED_REDIRECT_ORIGINS lands the user on the admin start page * rather than back here - which is why the dev server has to run on :4200. */ export const adminLoginUrl = (returnTo?: string): string => { const target = returnTo ?? (typeof window === "undefined" ? "/admin" : `${window.location.pathname}${window.location.search}`); const absolute = typeof window === "undefined" ? target : new URL(target, window.location.origin).href; return `${ADMIN_APP_URL}/login?redirect=${encodeURIComponent(absolute)}`; }; /** * Hard navigation, not router.push: every target here is a different origin. * Kept behind a helper so the destination is a value rather than a template * literal at the assignment - otherwise Next's * no-location-assign-relative-destination rule reads an absolute URL built from * an env var as an internal route. */ const leaveApp = (url: string): void => { window.location.href = url; }; export const goToAdminLogin = (returnTo?: string): void => { leaveApp(adminLoginUrl(returnTo)); }; /** * Signs out of every Nachklang app at once - the cookie is shared, so there is * no signing out of the calendar alone. The redirect happens even if the call * fails: leaving someone on an admin page that will 401 on its next request is * worse than a login form they may not have needed. * * The login URL carries this page as the return target, so an accidental * sign-out costs one sign-in to undo. */ export const signOut = async (): Promise => { // Captured before the request, so the target is where the user actually was. const target = adminLoginUrl(); try { await fetch(`${API_BASE_URL}/admin/auth/sign-out`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: "{}", }); } catch { // Network failure: fall through to the redirect anyway. } leaveApp(target); };