BuiltSafely

Home/Fixes

Supabase

JWT expired (Supabase)

The short-lived access token ran out. Refresh the session (or sign in again) without putting a secret key in the client.

  • Docs-only
  • Reviewed Oct 2026
  • 5 official sources
Sources Last checked October 19, 2026 · Badge: docs-only (official docs; no local JWT stack tested) · Sources: official docs (linked below) · Educational, not an audit

The fix

In your app: refresh the session before retrying the request. Supabase Auth access tokens are short-lived JWTs; the refresh token is what gets you a new pair (Supabase: User sessions).

const { data, error } = await supabase.auth.refreshSession()
const { session, user } = data
// If error: sign the user in again. Don't put a secret key in the browser.

You can also pass a refresh token: refreshSession({ refresh_token }) (Supabase JS: refreshSession). Client libraries normally refresh ahead of expiry when auto-refresh is on; call this when a request still fails with an expired token, or after the app resumes from the background.

In the Supabase Dashboard: if Studio itself says JWT expired, Supabase's troubleshooting note says your computer clock is usually out of sync. Compare it at time.is and sync the clock (Supabase troubleshooting: JWT Expired in Dashboard).

Why it happens

A session is an access token (JWT) plus a refresh token. "Access tokens are designed to be short lived, usually between 5 minutes and 1 hour while refresh tokens never expire but can only be used once" (Supabase: User sessions). After the access token's exp claim passes, APIs that require a user JWT reject the request until you refresh.

Other ways a session ends: sign-out, a password change or other sensitive action, an inactivity timeout, a maximum lifetime, or a single-session setting that drops older devices (Supabase: User sessions). Refresh token reuse detection can also invalidate a session if a refresh token is used twice.

The default JWT expiry is one hour. Values under about five minutes are discouraged because of clock skew and refresh load (Supabase: User sessions).

What not to do

  • Don't disable RLS, and don't move a secret or service_role key into the client to "fix" auth errors. Secret keys bypass RLS and must stay server-side (Supabase: API keys).
  • Don't mint long-lived user JWTs yourself to avoid refresh. Use the session APIs.
  • Don't ignore a failed refresh: send the user through sign-in again.

AI prompt

My Supabase app reports JWT expired on API calls.
Check that the client uses the publishable key only, that auto-refresh is enabled, and that failed requests refresh the session (or sign the user in again) instead of retrying with a stale access token.
Do not disable RLS. Do not put a secret or service_role key in client code.
Show any code changes first and wait for my OK.

How to verify

  1. Sign in, wait past your project's JWT expiry (or temporarily use a shorter expiry in a non-production project), then trigger a data request.
  2. Confirm the client refreshes and the request succeeds without a full sign-in.
  3. Revoke the session (sign out on another device, or use single-session if you rely on it) and confirm the app sends the user to sign in instead of looping on refresh.

A two-account check still matters for the data behind the JWT: after refresh, user A must not see user B's rows (our RLS guide).

Is the publishable key safe? · Supabase RLS guide · 42501 permission denied

Your turn

Did this show up after the app sat in the background, after a deploy, or only in the Dashboard? Tell us which, and we'll tighten this page.

This guide is educational and based on the vendor's public docs as of the review date. Platforms change: check the linked sources. Not a security audit. Disclaimer.

The BuiltSafely weekly

One email a week. What changed, and the one fix that matters.

Platform changes that affect the security of AI-built apps, checked against official docs. Free. Unsubscribe any time.