151 lines
4.2 KiB
TypeScript
151 lines
4.2 KiB
TypeScript
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),
|
|
),
|
|
);
|
|
});
|
|
});
|
|
},
|
|
};
|