/** * URLs Authentik (portail utilisateur + flows self-service). * Issuer attendu : `https://host/auth/application/o//`. */ import { getAuthentikBase } from "@/lib/auth/oidc-config" import type { MailThemeMode } from "@/lib/mail-settings/types" export type AuthentikUserSettingsTab = | "details" | "sessions" | "mfa" | "sources" const TAB_PAGE: Record = { details: "page-details", sessions: "page-sessions", mfa: "page-mfa", sources: "page-sources", } /** Flows Authentik par défaut pour self-service (modifiables côté admin). */ export const AUTHENTIK_SELF_SERVICE_FLOWS = { enrollment: "ulti-enrollment", passwordChange: "default-password-change", recovery: "ulti-recovery", totpSetup: "default-authenticator-totp-setup", webauthnSetup: "default-authenticator-webauthn-setup", staticSetup: "default-authenticator-static-setup", } as const export type AuthentikUrlOptions = { tab?: AuthentikUserSettingsTab flowSlug?: string /** Thème Authentik (`?theme=`) aligné sur l’app Ulti. */ theme?: "light" | "dark" } function authentikBaseUrl(): string | null { try { return getAuthentikBase().replace(/\/$/, "") } catch { return null } } function userSettingsHash(tab?: AuthentikUserSettingsTab): string { if (!tab || tab === "details") return "#/settings" const page = TAB_PAGE[tab] const params = encodeURIComponent(JSON.stringify({ page })) return `#/settings;${params}` } function withAuthentikTheme(url: string, theme?: "light" | "dark"): string { if (!theme) return url try { const parsed = new URL(url) parsed.searchParams.set("theme", theme) return parsed.toString() } catch { return url } } /** Thème effectif pour Authentik à partir des réglages Ulti. */ export function resolveAuthentikTheme( themeMode: MailThemeMode, resolvedTheme?: string | null ): "light" | "dark" { if (themeMode === "light" || themeMode === "dark") return themeMode if (resolvedTheme === "dark" || resolvedTheme === "light") return resolvedTheme if (typeof document !== "undefined") { return document.documentElement.classList.contains("dark") ? "dark" : "light" } return "light" } function userSettingsPath(tab?: AuthentikUserSettingsTab): string { const hash = userSettingsHash(tab) return `/if/user/${hash}` } /** Page « Paramètres » du portail utilisateur Authentik. */ export function authentikUserSettingsUrl( tab: AuthentikUserSettingsTab = "details", theme?: "light" | "dark" ): string | null { const base = authentikBaseUrl() if (!base) return null return withAuthentikTheme(`${base}${userSettingsPath(tab)}`, theme) } /** Flow Authentik (mot de passe, MFA, etc.). */ export function authentikFlowUrl( flowSlug: string, theme?: "light" | "dark" ): string | null { const base = authentikBaseUrl() const slug = flowSlug.trim().replace(/^\/+|\/+$/g, "") if (!base || !slug) return null return withAuthentikTheme(`${base}/if/flow/${slug}/`, theme) } export function authentikPasswordChangeFlowUrl( theme?: "light" | "dark" ): string | null { return authentikFlowUrl(AUTHENTIK_SELF_SERVICE_FLOWS.passwordChange, theme) } export function authentikEnrollmentFlowUrl( theme?: "light" | "dark" ): string | null { return authentikFlowUrl(AUTHENTIK_SELF_SERVICE_FLOWS.enrollment, theme) } export function authentikRecoveryFlowUrl( theme?: "light" | "dark" ): string | null { return authentikFlowUrl(AUTHENTIK_SELF_SERVICE_FLOWS.recovery, theme) } /** URL Authentik (flow ou onglet réglages) avec thème. */ export function buildAuthentikUrl(options: AuthentikUrlOptions): string | null { const { tab, flowSlug, theme } = options if (flowSlug) { const flowUrl = authentikFlowUrl(flowSlug, theme) if (flowUrl) return flowUrl } return authentikUserSettingsUrl(tab ?? "details", theme) }