Refresh Token missing for Sveltekit, is there a manual option
My environment is Sveltekit v2, Svelte 5 and Typescript, and I am trying to connect to Convex and provide the authorized user through an OIDC handshake.
My understanding is that there are three tokens, the refresh, access and ID. The problem is I need a live connection to the refreshing ID token not the access token. Convex uses a browser based client to ship up authentication and requires a function that can request a refresh, if the existing token is expired. kindeAuthClient getToken returns a memory store with the access token always up to date, but if the user is not at their computer for one hour and returns, the ID is expired despite the user retaining access with regular sessionHooks or getToken. I need a way to send the hooks.server.ts up to date sessionHook ID token to convex. Note, hook.server.ts also runs before any route or server code!
Here is snippets of code
---hooks.server.ts
const routeGuard: Handler = async ({ event, resolve }) => {
if (event?.url.pathname.includes("auth")) {
return await resolve(event);
}
const ok = await kindeAuthClient.isAuthenticated(event.request);
if (!ok) throw redirect(303, "/auth");
const kinde_id_token = event.cookies.get("kinde_id_token");
if (!kinde_id_token) throw redirect(303, "/auth");
event.locals.token = kinde_id_token;
const claims = decodeJwt(kinde_id_token) as { user_properties: { "super-user": { v: string } } };
// there is also no way to access properties by the way
if (event.route.id?.includes("admin")) {
const isSuper = claims.user_properties?.["super-user"]?.v === "true";
if (!isSuper) throw redirect(303, "/");
}
return await resolve(event);
};
---
1 Reply
after a thin passthrough of event.locals.token as token from layout.server.ts -> layout.svelte
--- src/routes/(private)/layout.svelte
....
const {data} = $props()
$effect.pre(() => {
const client = useConvexClient();
client.setAuth(async ({ forceRefreshToken }) => {
if (!forceRefreshToken) return data.token;
const refreshedToken = await fetch("/api/auth/refreshToken", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
const { token } = refreshedToken;
// const refreshedToken = await fetchRefreshedToken(fetch);
return token;
});
console.log(client.query(api.tested.testedTwo, {}));
});
---
--- /src/routes/api/auth/refreshToken/+server.ts
import { kindeAuthClient, type SessionManager } from "@kinde-oss/kinde-auth-sveltekit";
import { json } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
export const GET: RequestHandler = async ({ request, cookies }) => {
try {
const accessToken = await kindeAuthClient.getToken(request as unknown as SessionManager);
const kinde_id_token = cookies.get("kinde_id_token"); //<--- this token is not fresh
if (!accessToken) {
return json({ error: "No token available" }, { status: 401 });
}
return json({ token: kinde_id_token });
} catch (error) {
return json({ error: "Failed to get token" }, { status: 500 });
}
};