diff --git a/lib/appwrite-rest.ts b/lib/appwrite-rest.ts index cea7b31..536be5e 100644 --- a/lib/appwrite-rest.ts +++ b/lib/appwrite-rest.ts @@ -42,7 +42,7 @@ type FetchOpts = { formData?: FormData; }; -async function aw(path: string, opts: FetchOpts = {}): Promise { +async function awRaw(path: string, opts: FetchOpts = {}): Promise { const url = new URL(ENDPOINT + path); if (opts.query) { for (const [k, v] of Object.entries(opts.query)) { @@ -86,7 +86,11 @@ async function aw(path: string, opts: FetchOpts = {}): Promise { data.type, ); } + return res; +} +async function aw(path: string, opts: FetchOpts = {}): Promise { + const res = await awRaw(path, opts); const ct = res.headers.get("content-type") ?? ""; if (ct.includes("application/json")) { return (await res.json()) as T; @@ -94,6 +98,27 @@ async function aw(path: string, opts: FetchOpts = {}): Promise { return undefined as T; } +function extractSessionFromHeaders(res: Response): string | null { + // Appwrite returns the session secret in X-Fallback-Cookies as JSON, + // or in Set-Cookie header. The response body may have empty/redacted secret. + const fallback = res.headers.get("x-fallback-cookies"); + if (fallback) { + try { + const parsed = JSON.parse(fallback) as Record; + const first = Object.values(parsed)[0]; + if (first) return first; + } catch { + /* ignore */ + } + } + const setCookie = res.headers.get("set-cookie"); + if (setCookie) { + const m = setCookie.match(/a_session_[^=]+=([^;]+)/); + if (m?.[1]) return decodeURIComponent(m[1]); + } + return null; +} + // ─── Query helpers ─────────────────────────────────────────────── export const Q = { @@ -149,11 +174,16 @@ export interface AwUser { // ─── Account ───────────────────────────────────────────────────── export const account = { - createEmailPasswordSession(email: string, password: string) { - return aw("/account/sessions/email", { + async createEmailPasswordSession(email: string, password: string) { + const res = await awRaw("/account/sessions/email", { method: "POST", body: { email, password }, }); + const body = (await res.json()) as AwSession; + // Appwrite redacts `secret` in the response body — read it from the + // X-Fallback-Cookies header (or Set-Cookie) where the real secret lives. + const secretFromHeader = extractSessionFromHeaders(res); + return { ...body, secret: secretFromHeader || body.secret }; }, get(session: string) { return aw("/account", { session });