import { getRequestEvent } from "$app/server"; import { PUBLIC_API_URL } from "$env/static/public"; import { redirect, RequestEvent } from "@sveltejs/kit"; import axios from "axios"; import api from "../axios"; import { io } from "socket.io-client"; export const user = { isLoggedIn(): boolean { const event = getRequestEvent(); const authToken = event.cookies.get("accessToken"); return !!authToken; }, async refreshSession(refreshToken: string) { const refreshedTokens = ( await axios.post( `${PUBLIC_API_URL}/v1/auth/refresh`, {}, { headers: { "x-refresh-token": refreshToken, }, }, ) ).data.data; console.log("Refreshed tokens:", refreshedTokens); return refreshedTokens; }, async fetchInfo(accessToken: string) { const response = await api.get("/v1/user/@me", { headers: { Authorization: `Bearer ${accessToken}`, }, }); return response.data; }, logout(event: RequestEvent) { if (!event) return; // Clear authentication cookies event.cookies.delete("accessToken", { path: "/" }); event.cookies.delete("refreshToken", { path: "/" }); return redirect(303, "/login"); }, async checkPermissions(accessToken: string, permissions: string[]) { const response = await api.post( "/v1/user/@me/check-permission", { permissions }, { headers: { Authorization: `Bearer ${accessToken}`, }, }, ); return response.data; }, /** * @todo Get communication with server working and setup a key system so that the frontend can listen for a specific key from the backend so that nobody can poach off of login events. * * Note: This function no longer mutates SvelteKit request event/cookies asynchronously. * It returns the tokens to the caller so the caller (within the same request lifecycle) * can set cookies using the event object synchronously. */ async awaitAuthCallback(callbackKey: string): Promise<{ accessToken: string; refreshToken: string; }> { const base = PUBLIC_API_URL || ""; return new Promise((resolve, reject) => { let settled = false; const socket = io(`${base}/auth_callback`, { transports: ["websocket"], }); const timeout = setTimeout( () => { if (settled) return; settled = true; try { socket.disconnect(); } catch {} reject(new Error("Timed out waiting for auth callback")); }, 5 * 60 * 1000, ); // 5 minutes const handlePayload = (payload: any) => { try { const { accessToken, refreshToken } = payload ?? {}; if (accessToken && refreshToken) { if (settled) return; settled = true; clearTimeout(timeout); try { socket.disconnect(); } catch {} resolve({ accessToken, refreshToken }); } } catch { // ignore parse errors } }; socket.on("connect", () => {}); // listen for a specific callback key if provided if (callbackKey) { socket.on(`auth:login:callback:${callbackKey}`, handlePayload); } else { socket.on("auth-callback", handlePayload); } socket.on("message", console.log); socket.on("connect_error", (err: any) => { if (settled) return; settled = true; clearTimeout(timeout); try { socket.disconnect(); } catch {} reject(err instanceof Error ? err : new Error("Socket connect_error")); }); socket.on("error", (err: any) => { if (settled) return; settled = true; clearTimeout(timeout); try { socket.disconnect(); } catch {} reject(err instanceof Error ? err : new Error("Socket error")); }); socket.on("disconnect", (reason: any) => { if (settled) return; settled = true; clearTimeout(timeout); reject( new Error( "Socket disconnected before auth was received: " + String(reason), ), ); }); }); }, };