Skip to content
Loadout

Fix

Supabase SSR: getClaims, not getSession, on the server

Answer

Use supabase.auth.getClaims() to protect server pages and data. Never trust getSession() for authorization: it reads the cookie without revalidating it, and anyone can forge that cookie.

  • Next.js 16.4.1
  • React 19.3
  • @supabase/ssr 0.12.7
  • @supabase/supabase-js current, installed alongside @supabase/ssr

Last verified 2026-10-15

The Next.js + Supabase pack pins Next.js 16.4.1 and @supabase/ssr 0.12.7. In server code such as proxy.ts, getSession() reads the session from the cookie without revalidating it. A forged cookie must never decide who can read a page or write data. Never authorize with session.user.

Use getClaims() to protect pages and data. It verifies the JWT signature on every call. With asymmetric signing keys, the default for new projects, verification happens locally against cached JWKS. With symmetric keys, it calls the Auth server. getClaims() also refreshes the session when the access token is close to expiring.

Use getUser() only when you need an up-to-date user record from the Auth server; it makes a network call. Use getSession() only when you need the raw access or refresh token. Reading a token is different from verifying the identity allowed to use server data. Keep the authorization check on the server with getClaims().

Every Server Action re-checks auth with getClaims(). An action is a public HTTP endpoint even if no button calls it. Validate mutation input, check claims, then write. Pages also check claims themselves. proxy.ts refreshes the session, copies cookies onto both the request and the response, and returns the response that setAll last built. Proxy is not the only lock.

For the Next.js 16 interceptor filename and cookie handling, see /fixes/nextjs-16-middleware-to-proxy. Browser code uses createBrowserClient; Server Components, Server Actions, and Route Handlers use createServerClient with cookies.getAll and cookies.setAll.

Last verified 2026-10-15. doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed.

Next.js + Supabase pack — Next.js 16.4 and Supabase SSR, with the APIs that actually work.

doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed

Sources in the pack

Questions

Should Supabase SSR use getSession or getClaims on the server?
Use supabase.auth.getClaims() to protect server pages and data. Never trust getSession() for authorization: it reads the cookie without revalidating it, and anyone can forge that cookie.
Does getClaims always make a network call?
It verifies the JWT signature on every call, locally against cached JWKS for asymmetric signing keys, or through the Auth server for symmetric keys. It also refreshes the session when the access token is close to expiring.
When should I use getUser or getSession?
Use getUser() only for an up-to-date user record from the Auth server; it makes a network call. Use getSession() only for raw access/refresh tokens, never session.user for authorization. Every Server Action re-checks with getClaims().
When was this Supabase SSR rule last checked?
Last verified 2026-10-15. doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed.