S
Supabase2mo ago
sam

Supabase ssr + Sveltekit + GetClaims() sliding session implementation

Is there anyone who can tell me how I would implement a sliding session in the above setup? Using Supabase free plan for now while setting things up. Shouldn't GetSession refresh the access token? I'm super confused. A good start would be being able to circumvent the default 1 hour expiry time that logs me out
23 Replies
sam
samOP2mo ago
Some snippets from my hooks file
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
cookies: {
getAll: () => event.cookies.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) => {
event.cookies.set(name, value, { ...options, path: "/" });
});
},
},
auth: {
flowType: "pkce",
detectSessionInUrl: true,
// autoRefreshToken: true is the default - let Supabase handle refresh automatically
},
});
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
cookies: {
getAll: () => event.cookies.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) => {
event.cookies.set(name, value, { ...options, path: "/" });
});
},
},
auth: {
flowType: "pkce",
detectSessionInUrl: true,
// autoRefreshToken: true is the default - let Supabase handle refresh automatically
},
});
event.locals.safeGetSession = async () => {
const {
data: { session },
} = await event.locals.supabase.auth.getSession();

if (!session) {
return { session: null, user: null };
}

try {
const { data, error } = await event.locals.supabase.auth.getClaims(session.access_token);

if (error || !data?.claims) {
// JWT validation failed - token is invalid or expired
return { session: null, user: null };
}

// Build validated session from verified claims
const { claims } = data;
const validatedSession = {
access_token: session.access_token,
refresh_token: session.refresh_token,
expires_at: claims.exp,
expires_in: claims.exp - Math.floor(Date.now() / 1000),
token_type: "bearer" as const,
user: {
id: claims.sub,
email: claims.email,
phone: claims.phone,
app_metadata: claims.app_metadata ?? {},
user_metadata: claims.user_metadata ?? {},
aud: "authenticated" as const,
created_at: session.user?.created_at ?? "",
is_anonymous: claims.is_anonymous ?? false,
},
};

event.locals.user = validatedSession.user;
return { session: validatedSession, user: validatedSession.user };
} catch (error) {
// getClaims() failed - invalid session
return { session: null, user: null };
}
};
event.locals.safeGetSession = async () => {
const {
data: { session },
} = await event.locals.supabase.auth.getSession();

if (!session) {
return { session: null, user: null };
}

try {
const { data, error } = await event.locals.supabase.auth.getClaims(session.access_token);

if (error || !data?.claims) {
// JWT validation failed - token is invalid or expired
return { session: null, user: null };
}

// Build validated session from verified claims
const { claims } = data;
const validatedSession = {
access_token: session.access_token,
refresh_token: session.refresh_token,
expires_at: claims.exp,
expires_in: claims.exp - Math.floor(Date.now() / 1000),
token_type: "bearer" as const,
user: {
id: claims.sub,
email: claims.email,
phone: claims.phone,
app_metadata: claims.app_metadata ?? {},
user_metadata: claims.user_metadata ?? {},
aud: "authenticated" as const,
created_at: session.user?.created_at ?? "",
is_anonymous: claims.is_anonymous ?? false,
},
};

event.locals.user = validatedSession.user;
return { session: validatedSession, user: validatedSession.user };
} catch (error) {
// getClaims() failed - invalid session
return { session: null, user: null };
}
};
And in the onMount function of my top +layout.svelte file I have
const { data } = supabase.auth.onAuthStateChange((_: unknown, newSession: any) => {
if (newSession?.expires_at !== session?.expires_at) {
invalidate("supabase:auth");
}
});
const { data } = supabase.auth.onAuthStateChange((_: unknown, newSession: any) => {
if (newSession?.expires_at !== session?.expires_at) {
invalidate("supabase:auth");
}
});
Which seems pretty standard.
Fieryduck82579
Fieryduck825792mo ago
Looks like you're missing the src/routes/+layout.ts which is used for refreshing the session. See step (5) in the SvelteKit guide.
sam
samOP2mo ago
Is this also true when we are using the new GetClaims() functionality?
Fieryduck82579
Fieryduck825792mo ago
Not sure honestly, I have not migrated yet. But the +layout.ts is required for passing the new session and user to the client from the server. The event.locals.* are only available on the server.
sam
samOP2mo ago
Ok thank you. I had the file in place but passed through the validated session from the server, since I enabled GetClaims and I thought it became redundant.
Fieryduck82579
Fieryduck825792mo ago
I see your point. Please let me know how it goes. I am interested in this problem too, as I'll have to migrate eventually.
sam
samOP2mo ago
Yeah I will test this first thing in the morning
j4
j42mo ago
What is a "sliding session"? I'm trying to understand what problem you're trying to solve.
sam
samOP2mo ago
I seem to still be getting issues being logged out after 1 hour, even after reintroducing the GetSession call in my +layout.ts file as per the documentation I'm investigating this further, it's a shame the Supabase docs are not up to date For instance I have no idea if there is an option to change the expiry time from the default 1 hour to something else. Every source I find directs me to something that doesn't exist on the newest Supabase website. I also checked every possible option but it's not there. With a sliding session I mean users that are working shouldn't be logged out, and should have their session expiry time 'slide' with activity But I figure once I can disable the 1 hour expiry time, I can implement my own logic and handle session expiry that way. But I have no idea how..
Fieryduck82579
Fieryduck825792mo ago
Per the docs, you should be able to customize the expiry time in the settings.
sam
samOP2mo ago
Docs are outdated There was an expiry time option of 3600 seconds in the legacy JWT tab, but since we migrated to the new JWT system and that tab doesn't have a setting, it won't work
j4
j42mo ago
Good point on the JWT settings. I'll have to check that, but I assume it would still work with the new JWTs. The ssr docs are in the process of being redone, but I don't have details on what that means, or a timeline on completion. If it helps, I've got a repo for ssr and sveltekit. I do a couple of things different, as far as what I'm returning, but maybe you can do a stare-and-compare with what you've got. https://github.com/j4w8n/sveltekit-supabase-ssr
garyaustin
garyaustin2mo ago
No description
garyaustin
garyaustin2mo ago
By default sessions continue until the user signs out. A session is a an access token, refresh token pair which are saved in local storage by supabase-js, or cookies with SSR. The client will refresh the access_token (JWT) as long as the refresh token has not been invalidated by signout or conflict error of being used twice.
sam
samOP2mo ago
My users login to my app via Azure SSO. It probably has something to do with that which I need to look into. Although my Azure environment has very high expiry times, there might be some interaction between Azure and Supabase I'm missing. Does the expiry time still work from the Legacy JWT tab? As mentioned before, I already rotated the keys to the new Signing keys.
j4
j42mo ago
I just checked, and the Access token expiry time field on the Legacy JWT Secret tab still works for me - with asym JWTs. I changed and saved it, and the new expiry was reflected after a session refresh.
sam
samOP2mo ago
Ok thank you. I will try to see if changing to assymetrical JWT's is what's actually causing this. I noticed I get stored chunked cookies when logging in, I'm guessing Azure data bloats this, and this might be causing a problem with the refresh? But my setup was always with Azure so it should have happened before as well. I'm so confused lol Lemme try changing back to GetUser and see if this resolves the problem. Same issue with GetUser(), so now my main target is fragmented/chunked cookies
j4
j42mo ago
The multiple cookies is very common and expected with OAuth, so may be similar with SSO.
sam
samOP2mo ago
Then I am complete lost The only thing I can think of, is that I migrated to Cloudflare a while ago. But the issue also happens when developing locally so...
j4
j42mo ago
Is the cookie still in the browser after the issue happens?
sam
samOP2mo ago
Yeah Cookies *
j4
j42mo ago
Seems like something going on with the code. Is there a public repo for the code? If not, I think sharing a bit more, like your root layout.ts and layout.svelte file may help.
sam
samOP2mo ago
Damn, I had a constant hanging around somewhere that represented an expiry time I completely forgot about Sorry to waste your time It did end up teaching me a lot about how everything works lmao

Did you find this page helpful?