You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In v2, useSupabaseUser() now returns JWT claims via client.auth.getClaims(). The initialization happens in the page:start hook in supabase.client.ts, which causes the composable to return null in route middlewares on page refresh when using SPA mode (ssr: false).
This happens because Nuxt middlewares execute beforepage:start.
flowchart TD
subgraph timeline["⏱️ Execution Timeline"]
direction TB
A["1️⃣ Page Refresh"] --> B["2️⃣ supabase.client.ts setup"]
B --> C["3️⃣ Route Middleware runs"]
C --> D["4️⃣ useSupabaseUser() = null"]
D --> E["5️⃣ Redirect to /login 🚫"]
end
subgraph late["❌ Runs after middleware"]
F["6️⃣ page:start hook"]
F --> G["getClaims()"]
G --> H["User available ✅"]
end
E -.->|"too late"| F
exportdefaultdefineNuxtRouteMiddleware(()=>{constuser=useSupabaseUser()console.log(user.value)// null on refresh, even if logged inif(!user.value)returnnavigateTo('/login')})
Steps:
Login successfully
Navigate to a protected page (works ✅)
Refresh the page → redirected to login ❌
Root cause
In supabase.client.js:
nuxtApp.hook("page:start",async()=>{const{ data }=awaitclient.auth.getClaims();currentUser.value=data?.claims??null;});
So useSupabaseUser() is still null when middlewares run.
Suggested fix
Initialize claims directly in setup() instead of page:start:
asyncsetup({ provide }){// ... client creation ...constcurrentUser=useSupabaseUser();// Initialize immediately (plugin has enforce: "pre")const{ data }=awaitclient.auth.getClaims();currentUser.value=data?.claims??null;// Keep onAuthStateChange for subsequent updatesclient.auth.onAuthStateChange(/* ... */);}
Current workaround
Use supabase.auth.getClaims() directly in middlewares:
exportdefaultdefineNuxtRouteMiddleware(async()=>{constsupabase=useSupabaseClient()const{ data }=awaitsupabase.auth.getClaims()if(!data?.claims)returnnavigateTo('/login')})
Version
@nuxtjs/supabase: 2.0.3
nuxt: 4.2.1
Description
In v2, useSupabaseUser() now returns JWT claims via
client.auth.getClaims(). The initialization happens in thepage:starthook insupabase.client.ts, which causes the composable to return null in route middlewares on page refresh when using SPA mode (ssr: false).This happens because Nuxt middlewares execute before
page:start.flowchart TD subgraph timeline["⏱️ Execution Timeline"] direction TB A["1️⃣ Page Refresh"] --> B["2️⃣ supabase.client.ts setup"] B --> C["3️⃣ Route Middleware runs"] C --> D["4️⃣ useSupabaseUser() = null"] D --> E["5️⃣ Redirect to /login 🚫"] end subgraph late["❌ Runs after middleware"] F["6️⃣ page:start hook"] F --> G["getClaims()"] G --> H["User available ✅"] end E -.->|"too late"| FReproduction
nuxt.config.ts:
middleware/auth.ts:
Steps:
Root cause
In
supabase.client.js:Nuxt execution order: plugins → middlewares →
page:startSo
useSupabaseUser()is stillnullwhen middlewares run.Suggested fix
Initialize claims directly in
setup()instead ofpage:start:Current workaround
Use
supabase.auth.getClaims()directly in middlewares: