import { dev } from "$app/environment"; import { HANDLE_COOKIE, OAUTH_MAX_AGE, SESSION_COOKIE, SESSION_MAX_AGE, } from "$lib/server/constants"; import { decryptText, encryptText } from "$lib/server/crypto"; import { createOAuthClient } from "$lib/server/oauth"; import type { AuthEvent } from "$lib/types"; import { parsePublicUser, type PublicUserData } from "$lib/valibot"; import { Client } from "@atcute/client"; import { isHandle } from "@atcute/lexicons/syntax"; /** * Logout */ export const destroySession = async ( event: AuthEvent, ): Promise => { event.cookies.delete(SESSION_COOKIE, { path: "/" }); if (event.locals.user) { try { const oAuthClient = createOAuthClient(event); await oAuthClient.revoke(event.locals.user.did); } catch { // Do nothing? } event.locals.user = undefined; } event.locals.oAuthClient = undefined; }; /** * Begin auth flow * @returns {URL} OAuth redirect */ export const startSession = async ( event: AuthEvent, handle: string, ): Promise => { if (isHandle(handle) === false) { throw new Error("invalid handle"); } const oAuthClient = createOAuthClient(event); const { url } = await oAuthClient.authorize({ target: { "type": "account", identifier: handle }, }); // Temporary to remember handle across oauth flow event.cookies.set( HANDLE_COOKIE, handle, { httpOnly: true, maxAge: OAUTH_MAX_AGE, path: "/", sameSite: "lax", secure: !dev, }, ); return url; }; /** * Store the logged in user data */ export const updateSession = async ( event: AuthEvent, user: PublicUserData, ) => { const { cookies, platform } = event; if (platform?.env === undefined) { throw new Error(); } const encrypted = await encryptText( JSON.stringify(user), platform.env.PRIVATE_COOKIE_KEY, ); cookies.set( SESSION_COOKIE, encrypted, { httpOnly: true, maxAge: SESSION_MAX_AGE, path: "/", sameSite: "lax", secure: !dev, }, ); }; /** * Setup OAuth client from cookies */ export const restoreSession = async ( event: AuthEvent, ): Promise => { const { cookies, platform } = event; if (platform?.env === undefined) { throw new Error(); } const encrypted = cookies.get(SESSION_COOKIE); if (encrypted === undefined) { return; } // Parse and validate or delete cookie let data: PublicUserData; try { const decrypted = await decryptText( encrypted, platform?.env.PRIVATE_COOKIE_KEY, ); data = parsePublicUser(JSON.parse(decrypted)); } catch { cookies.delete(SESSION_COOKIE, { path: "/" }); return; } try { const oAuthClient = createOAuthClient(event); const session = await oAuthClient.restore(data.did); const client = new Client({ handler: session }); event.locals.user = { ...data, client, session, }; } catch { cookies.delete(SESSION_COOKIE, { path: "/" }); return; } };