SvelteKit and OpenAPI-Fetch

I've been playing around with Kinde auth in SvelteKit with OpenAPI-Fetch. I have hit a tricky spot where using the middleware with openapi-fetch I cant obtain the Kinde token since it has no access to the session storage. The way I have done it now im giving the user the ability to see their own token. Could anyone give me some insight on keeping the middleware apart of the code, and removing the token endpoint?
// /routes/api/auth/+server.ts
export const GET: RequestHandler = async ({ request }) => {
const token = await kindeAuthClient.getToken(request as unknown as SessionManager)
return text(token)
};
// /routes/api/auth/+server.ts
export const GET: RequestHandler = async ({ request }) => {
const token = await kindeAuthClient.getToken(request as unknown as SessionManager)
return text(token)
};
// /lib/server/api.ts
import createClient, { type Middleware } from 'openapi-fetch';
import type { paths } from './v1';

const auth: Middleware = {
async onRequest({ request, options }) {
const res = await options.fetch('/api/auth');
const token = await res.text();
request.headers.set("Authorization", `Bearer ${token}`);
return request;
}
};

const client = createClient<paths>({
baseUrl: "http://localhost:4000"
})

client.use(auth)

export default client;
// /lib/server/api.ts
import createClient, { type Middleware } from 'openapi-fetch';
import type { paths } from './v1';

const auth: Middleware = {
async onRequest({ request, options }) {
const res = await options.fetch('/api/auth');
const token = await res.text();
request.headers.set("Authorization", `Bearer ${token}`);
return request;
}
};

const client = createClient<paths>({
baseUrl: "http://localhost:4000"
})

client.use(auth)

export default client;
This allows me to use +page.server.ts without adding headers over and over again via the following line
const {data, error} = await client.GET("/api/v4/todos", {fetch})
const {data, error} = await client.GET("/api/v4/todos", {fetch})
Middleware & Auth | OpenAPI TypeScript
Consume OpenAPI 3.0 & 3.1 schemas in TypeScript
6 Replies
Abdiwak
Abdiwak2mo ago
Hi there, Thanks for reaching out. To keep your Kinde authentication middleware clean in a SvelteKit app—without exposing a token endpoint to the user—use the session provided by the Kinde SvelteKit SDK directly in your server-side code. The SDK is designed so you can securely access tokens via the request/session context on the server, without needing to create a public route that returns the token. - The recommended way to get the Kinde access token in SvelteKit is via kindeAuthClient.getToken(request as unknown as SessionManager)—but this should be done server-side, not via a public API endpoint. - By keeping all token handling within server-only files, you avoid the need for an /api/auth endpoint that returns the token to the client (and thus to users). - Use the SvelteKit server hooks to inject session info, as shown in the docs: https://docs.kinde.com/developer-tools/sdks/backend/sveltekit-sdk/#token-storage
DerpyDinosaur
DerpyDinosaurOP2mo ago
Is there any reason why openapi-fetch wouldnt be hitting the hooks? Otherwise I would be happily adding the token there Cheers for the response, Ill just live with writing it out each time
Abdiwak
Abdiwak2mo ago
In SvelteKit, the hooks.server.ts file (where you use sessionHooks) only runs for requests that are handled by the SvelteKit server—such as page/server endpoints and API routes defined within your SvelteKit app. If you use openapi-fetch on the server side and initiate requests from within SvelteKit server code (like in +page.server.ts or server-side API handlers), those requests will have access to the session and the Kinde helpers as expected1. However, if you are using openapi-fetch from the client/browser, those requests go directly from the browser to your API backend—they do not pass through the SvelteKit server, so the SvelteKit server hooks (and thus Kinde session logic) do not run for those requests.
DerpyDinosaur
DerpyDinosaurOP2mo ago
So I've done this a few different ways and nothing seems to hit the hooks, but this morning I did try adding a handleFetch hook again. Now everything works as expected. Thanks again for your explanations, I guess I just needed to rubber ducky it out.
export const handleFetch: HandleFetch = async ({ event, request, fetch }) => {
const isAuthenticated = await kindeAuthClient.isAuthenticated(
event.request as unknown as SessionManager
); // Boolean: true or false

if (request.url.startsWith('http://localhost:4000/api') && isAuthenticated) {
const token = await kindeAuthClient.getToken(event.request as unknown as SessionManager);
request.headers.set('Authorization', `Bearer ${token}`);
}

return await fetch(request);
};
export const handleFetch: HandleFetch = async ({ event, request, fetch }) => {
const isAuthenticated = await kindeAuthClient.isAuthenticated(
event.request as unknown as SessionManager
); // Boolean: true or false

if (request.url.startsWith('http://localhost:4000/api') && isAuthenticated) {
const token = await kindeAuthClient.getToken(event.request as unknown as SessionManager);
request.headers.set('Authorization', `Bearer ${token}`);
}

return await fetch(request);
};
Abdiwak
Abdiwak2mo ago
Sounds great. Please don't hesitate to reach out further.

Did you find this page helpful?