// Thin wrappers over the api's `/auth/*` HTTP routes (@voltro/plugin-auth). // // State-changing auth routes are CSRF-protected (double-submit): `GET /auth/csrf` // sets a cookie AND returns a token that must be echoed in the `x-csrf-token` // header on the following POST. `postAuth` does that round-trip so each page is // a one-liner. These call the SAME-ORIGIN api (the dev/serve pipeline proxies // `/auth/*` to the api named in app.config), so the HttpOnly session cookie the // api sets lands on this origin. export interface AuthResponse { readonly ok: boolean readonly status: number readonly data: unknown } /** Fetch a CSRF token (also drops the csrf cookie into the jar). */ export const csrfToken = async (): Promise => { const res = await fetch('/auth/csrf') const body = (await res.json()) as { csrfToken?: string } return body.csrfToken ?? '' } /** POST a JSON body to an `/auth/*` route with the CSRF token attached. */ export const postAuth = async (path: string, body: Record): Promise => { const token = await csrfToken() const res = await fetch(path, { method: 'POST', headers: { 'content-type': 'application/json', 'x-csrf-token': token }, body: JSON.stringify(body), }) let data: unknown = null try { data = await res.json() } catch { /* some routes return 202 with no body */ } return { ok: res.ok, status: res.status, data } }