realtime-smoke.test.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. // @vitest-environment node
  2. /**
  3. * Smoke test: a hand-minted HS256 JWT (mintAccessToken) is accepted by
  4. * PostgREST. RLS resolves `auth.uid()` to the JWT's `sub` and returns
  5. * exactly the rows that user is allowed to see.
  6. *
  7. * Gated on RUN_INTEGRATION=1; skipped otherwise.
  8. *
  9. * We test PostgREST rather than Realtime directly because:
  10. * - Realtime accepts the same JWT via the same HS256 verification path,
  11. * so PostgREST acceptance proves the load-bearing claim.
  12. * - The Realtime ws client adds harness complexity disproportionate to
  13. * the assertion.
  14. */
  15. import { describe, it, expect } from "vitest";
  16. import { createClient } from "@supabase/supabase-js";
  17. import { randomUUID } from "node:crypto";
  18. const RUN = process.env.RUN_INTEGRATION === "1";
  19. const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL ?? "";
  20. const SUPABASE_ANON = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? "";
  21. const SUPABASE_SERVICE = process.env.SUPABASE_SERVICE_ROLE_KEY ?? "";
  22. (RUN ? describe : describe.skip)("integration: minted JWT accepted by PostgREST", () => {
  23. it("a JWT signed with JWT_SECRET resolves auth.uid() under RLS", async () => {
  24. expect(process.env.JWT_SECRET).toBeTruthy();
  25. // Sign an anon user up (real auth.users row required for FKs / RLS).
  26. const anon = createClient(SUPABASE_URL, SUPABASE_ANON, {
  27. auth: { persistSession: false, autoRefreshToken: false },
  28. });
  29. const { data: signin } = await anon.auth.signInAnonymously();
  30. const uid = signin.user!.id;
  31. // Persist a session row so isSessionLive() would pass — not strictly
  32. // required for PostgREST but matches the production token shape.
  33. const admin = createClient(SUPABASE_URL, SUPABASE_SERVICE, {
  34. auth: { persistSession: false, autoRefreshToken: false },
  35. });
  36. await admin.from("user_sessions").insert({ user_id: uid, id: randomUUID() });
  37. const { mintAccessToken } = await import("@/lib/auth/jwt");
  38. const token = await mintAccessToken({
  39. sub: uid,
  40. session_id: randomUUID(),
  41. iat_original: Math.floor(Date.now() / 1000),
  42. });
  43. // Construct an anon client and inject our minted token. PostgREST will
  44. // verify the HS256 signature and resolve auth.uid() = uid for RLS.
  45. const minted = createClient(SUPABASE_URL, SUPABASE_ANON, {
  46. auth: { persistSession: false, autoRefreshToken: false },
  47. global: { headers: { Authorization: `Bearer ${token}` } },
  48. });
  49. // Read the user's own row (RLS allows: auth.uid() = id).
  50. const { data, error } = await minted.from("users").select("id").eq("id", uid).maybeSingle();
  51. expect(error).toBeNull();
  52. expect(data?.id).toBe(uid);
  53. }, 30_000);
  54. });