deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Next.js 15 Supabase auth: silent token leaks, the getSession() trap and a three-layer fix

A dev.to walkthrough details how Next.js 15's async cookies and deprecated Supabase helpers break old auth boilerplate, why getSession() invites token spoofing, and a three-client fix.

Next.js 15 Supabase auth: silent token leaks, the getSession() trap and a three-layer fix

A hands-on engineering post on dev.to maps out how server-side authentication changes in Next.js 15, and shares the three-part Supabase client architecture its author now runs in production across a micro-SaaS and client projects. The core warning: authentication code lifted from 2023-era tutorials often still executes, but fails quietly — leaking cached sessions at the edge, trapping users in redirect loops during token refresh, or accepting forged JWTs inside Server Actions.

What broke in Next.js 15

According to the post, three shifts invalidate older Supabase setups. First, the request APIs from next/headers — cookies() and headers() — are now asynchronous and must be awaited before use. Second, React 19 server components render as read-only streams and cannot modify cookies at all. Third, Supabase's legacy @supabase/auth-helpers-nextjs package is deprecated in favour of @supabase/ssr.

The consequences are subtle rather than fatal, the author writes: a helper that touches the cookie store synchronously may only produce development warnings, but can surface sporadic 'dynamic server usage' and cookie-mutability errors in production server actions.

The getSession() trap

The sharpest warning in the post concerns supabase.auth.getSession(). That call reads the session straight from local cookies without validating the token against the Supabase Auth server, so a tampered, expired or revoked JWT can still yield what looks like a valid session. The author's rule is blunt: for any server-side access decision — server components, route handlers, middleware, server actions — call getUser() instead, which verifies the token with Supabase or checks its cryptographic signature against the project's signing key so user IDs and claims cannot be forged.

Middleware refreshes can exhaust rate limits

Because Next.js middleware runs on every matched request, a loosely written matcher triggers a Supabase token refresh for each stylesheet, favicon and image a page loads. The post quantifies the effect: a dashboard that pulls 30 assets fires 30 auth round trips on a single page view, which can burn through Supabase API rate limits within minutes. The recommended fix is a matcher that explicitly excludes _next/static, _next/image, the favicon and common image file extensions.

Three clients, one job each

The architecture described in the post splits Supabase access into three small factory modules:

  • An edge middleware module whose only responsibility is refreshing expired tokens before a request reaches pages or server actions. It verifies the user with getUser(), redirects unauthenticated visitors away from protected paths such as /dashboard and /settings to /login while preserving the original path in a redirectTo query parameter, and bounces already-authenticated users away from the login and register screens.
  • A server client for server components that awaits the Next.js 15 cookie store and treats it as read-only. Its cookie-write callback wraps writes in a try/catch and deliberately swallows failures, since components cannot mutate cookies and the middleware has already refreshed the session.
  • A browser client for interactive client components.

For server actions, the post adds a further guard: never trust client payloads. Incoming form data is checked at runtime with Zod schemas — validating email format and enforcing a minimum password length in the login example — before authentication proceeds, to block malformed input and privilege-escalation attempts.

Why it matters

Next.js 15's auth pain points are deprecations and behavioural changes, not loud compile-time errors, so broken patterns can ship to production unnoticed. Any team mid-migration that still calls getSession() for access control, or whose middleware refreshes tokens on every static asset, is carrying a real security or reliability bug. The post is a single team's production account rather than official guidance, and the code is written for Supabase specifically — but the underlying rules (await the new async APIs, verify tokens server-side instead of trusting cookie contents, and keep middleware matchers tight) apply to any Next.js 15 App Router project using cookie-based sessions.

  • #next-js
  • #supabase
  • #authentication
  • #react-19
  • #server-actions