{"version":3,"file":"user-BQiWoQk1.mjs","names":["User","serverEnv","AppSessionData","refreshToken","user","pkceVerifier","state","returnTo","getAppSession","useSession","getSession","name","password","SESSION_SECRET","cookie","httpOnly","sameSite","secure","path","createServerFn","createServerOnlyFn","scopes","definedScopes","queryClient","User","getAppSession","getSessionUser","method","handler","Promise","session","data","user","getUser","import","meta","env","SSR","ensureQueryData","queryKey","queryFn","staleTime","Infinity","getAccessToken","scopeKey","refreshToken","Error","acquireDelegatedToken","accessToken","update"],"sources":["../src/lib/auth/session.ts","../src/lib/auth/user.ts"],"sourcesContent":["import type { User } from \"~/models/User\";\r\nimport { serverEnv } from \"~/env\";\r\n\r\n/**\r\n * Data persisted in the sealed (encrypted, httpOnly) session cookie.\r\n *\r\n * Kept deliberately small so it never approaches the ~4 KB single-cookie limit:\r\n * the bulky MSAL token cache is NOT stored (serialized it runs to several KB once\r\n * group claims are included, which forces cookie chunking). Instead we keep only\r\n * the refresh token — which the server uses to mint delegated access tokens on\r\n * demand — and a trimmed user with permissions already resolved. The browser sees\r\n * neither: the cookie is httpOnly and encrypted with `SESSION_SECRET`.\r\n */\r\nexport interface AppSessionData {\r\n  /** Refresh token used to acquire delegated access tokens server-side. */\r\n  refreshToken?: string;\r\n  /** Serializable signed-in user (profile + granted permission keys). */\r\n  user?: User;\r\n  /** Transient PKCE verifier, only present between /auth/login and /auth/callback. */\r\n  pkceVerifier?: string;\r\n  /** Transient CSRF state, only present between /auth/login and /auth/callback. */\r\n  state?: string;\r\n  /** Where to send the user after a successful login. */\r\n  returnTo?: string;\r\n}\r\n\r\n/**\r\n * Returns the request-scoped session manager. Must be called inside a server\r\n * context (server function, server route handler, or SSR), where the request\r\n * cookies are available.\r\n */\r\nexport const getAppSession = async () => {\r\n  // Dynamic import: `@tanstack/react-start/server` is the server entry and drags\r\n  // in the SSR renderer (`react-dom/server`). Keep it out of the client graph —\r\n  // this helper is only ever called server-side, and all callers already await it.\r\n  // Aliased off the `use*` name so it isn't mistaken for a React hook (it's a\r\n  // server session helper, not a hook).\r\n  const { useSession: getSession } = await import(\"@tanstack/react-start/server\");\r\n  return getSession<AppSessionData>({\r\n    name: \"wcz-auth\",\r\n    password: serverEnv.SESSION_SECRET,\r\n    cookie: {\r\n      httpOnly: true,\r\n      sameSite: \"lax\",\r\n      secure: true,\r\n      path: \"/\",\r\n    },\r\n  });\r\n};\r\n","import { createServerFn, createServerOnlyFn } from \"@tanstack/react-start\";\r\nimport { scopes as definedScopes } from \"virtual:wcz-layout\";\r\nimport { queryClient } from \"~/lib/queryClient\";\r\nimport type { User } from \"~/models/User\";\r\nimport { getAppSession } from \"./session\";\r\n\r\n/**\r\n * Reads the signed-in user from the session cookie, or null. As a server function\r\n * it runs in-process when called on the server (SSR, middleware) and as an RPC\r\n * when called from the client — so it doubles as the client `queryFn`.\r\n */\r\nexport const getSessionUser = createServerFn({ method: \"GET\" }).handler(\r\n  async (): Promise<User | null> => {\r\n    const session = await getAppSession();\r\n    return session.data.user ?? null;\r\n  },\r\n);\r\n\r\nexport const getUser = (): Promise<User | null> => {\r\n  if (import.meta.env.SSR) return getSessionUser();\r\n\r\n  return queryClient.ensureQueryData({\r\n    queryKey: [\"auth\", \"user\"],\r\n    queryFn: () => getSessionUser(),\r\n    staleTime: Infinity,\r\n  });\r\n};\r\n\r\n/**\r\n * Server-only token acquisition: a delegated access token for the given API\r\n * scope, minted from the user's session refresh token. Entra rotates the refresh\r\n * token on each use, so the rotated token is persisted back to the session. Use\r\n * inside server functions and middleware — it is stripped from the client bundle\r\n * and throws if called there.\r\n */\r\nexport const getAccessToken = createServerOnlyFn(\r\n  async (scopeKey: keyof typeof definedScopes): Promise<string> => {\r\n    const session = await getAppSession();\r\n    if (!session.data.refreshToken) throw new Error(\"No active session. User not signed in.\");\r\n\r\n    // Dynamic import so `entra` (and its `@azure/msal-node` dependency) stays out\r\n    // of the client graph — this module also exports the isomorphic `getUser`.\r\n    const { acquireDelegatedToken } = await import(\"./entra\");\r\n    const { accessToken, refreshToken } = await acquireDelegatedToken({\r\n      refreshToken: session.data.refreshToken,\r\n      scopes: definedScopes[scopeKey],\r\n    });\r\n\r\n    if (refreshToken !== session.data.refreshToken) await session.update({ refreshToken });\r\n    return accessToken;\r\n  },\r\n);\r\n"],"mappings":";;;;;;;;;;AA+BA,MAAaQ,gBAAgB,YAAY;CAMvC,MAAM,EAAEC,YAAYC,eAAe,MAAM,OAAO;CAChD,OAAOA,WAA2B;EAChCC,MAAM;EACNC,UAAUX,YAAUY;EACpBC,QAAQ;GACNC,UAAU;GACVC,UAAU;GACVC,QAAQ;GACRC,MAAM;EACR;CACF,CAAC;AACH;;;;;;;;ACrCA,MAAaQ,iBAAiBP,eAAe,EAAEQ,QAAQ,MAAM,CAAC,CAAC,CAACC,QAC9D,YAAkC;CAEhC,QAAOE,MADeL,cAAc,EAAA,CACrBM,KAAKC,QAAQ;AAC9B,CACF;AAEA,MAAaC,gBAAsC;CACjD,IAAIC,OAAOC,KAAKC,IAAIC,KAAK,OAAOX,eAAe;CAE/C,OAAOH,YAAYe,gBAAgB;EACjCC,UAAU,CAAC,QAAQ,MAAM;EACzBC,eAAed,eAAe;EAC9Be,WAAWC;CACb,CAAC;AACH;;;;;;;;AASA,MAAaC,iBAAiBvB,mBAC5B,OAAOwB,aAA0D;CAC/D,MAAMd,UAAU,MAAML,cAAc;CACpC,IAAI,CAACK,QAAQC,KAAKc,cAAc,MAAM,IAAIC,MAAM,wCAAwC;CAIxF,MAAM,EAAEC,0BAA0B,MAAM,OAAO;CAC/C,MAAM,EAAEC,aAAaH,iBAAiB,MAAME,sBAAsB;EAChEF,cAAcf,QAAQC,KAAKc;EAC3BxB,QAAQC,OAAcsB;CACxB,CAAC;CAED,IAAIC,iBAAiBf,QAAQC,KAAKc,cAAc,MAAMf,QAAQmB,OAAO,EAAEJ,aAAa,CAAC;CACrF,OAAOG;AACT,CACF"}