// @vitest-environment node /** * Smoke test: a hand-minted HS256 JWT (mintAccessToken) is accepted by * PostgREST. RLS resolves `auth.uid()` to the JWT's `sub` and returns * exactly the rows that user is allowed to see. * * Gated on RUN_INTEGRATION=1; skipped otherwise. * * We test PostgREST rather than Realtime directly because: * - Realtime accepts the same JWT via the same HS256 verification path, * so PostgREST acceptance proves the load-bearing claim. * - The Realtime ws client adds harness complexity disproportionate to * the assertion. */ import { describe, it, expect } from "vitest"; import { createClient } from "@supabase/supabase-js"; import { randomUUID } from "node:crypto"; const RUN = process.env.RUN_INTEGRATION === "1"; const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL ?? ""; const SUPABASE_ANON = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? ""; const SUPABASE_SERVICE = process.env.SUPABASE_SERVICE_ROLE_KEY ?? ""; (RUN ? describe : describe.skip)("integration: minted JWT accepted by PostgREST", () => { it("a JWT signed with JWT_SECRET resolves auth.uid() under RLS", async () => { expect(process.env.JWT_SECRET).toBeTruthy(); // Sign an anon user up (real auth.users row required for FKs / RLS). const anon = createClient(SUPABASE_URL, SUPABASE_ANON, { auth: { persistSession: false, autoRefreshToken: false }, }); const { data: signin } = await anon.auth.signInAnonymously(); const uid = signin.user!.id; // Persist a session row so isSessionLive() would pass — not strictly // required for PostgREST but matches the production token shape. const admin = createClient(SUPABASE_URL, SUPABASE_SERVICE, { auth: { persistSession: false, autoRefreshToken: false }, }); await admin.from("user_sessions").insert({ user_id: uid, id: randomUUID() }); const { mintAccessToken } = await import("@/lib/auth/jwt"); const token = await mintAccessToken({ sub: uid, session_id: randomUUID(), iat_original: Math.floor(Date.now() / 1000), }); // Construct an anon client and inject our minted token. PostgREST will // verify the HS256 signature and resolve auth.uid() = uid for RLS. const minted = createClient(SUPABASE_URL, SUPABASE_ANON, { auth: { persistSession: false, autoRefreshToken: false }, global: { headers: { Authorization: `Bearer ${token}` } }, }); // Read the user's own row (RLS allows: auth.uid() = id). const { data, error } = await minted.from("users").select("id").eq("id", uid).maybeSingle(); expect(error).toBeNull(); expect(data?.id).toBe(uid); }, 30_000); });