UNPKG

@npio/internals

Version:

A free visual website editor, powered with your own SolidJS components.

153 lines (128 loc) 3.97 kB
import { createId } from "@paralleldrive/cuid2"; import { decode } from "decode-formdata"; import { getRequestEvent } from "solid-js/web"; import { H3Event, clearSession, getCookie, getSession, updateSession, useSession, type SessionConfig, } from "vinxi/http"; import { Auth, HandlerInput, csrfHeader } from "."; import { defineRequestFunction } from "../src/cache"; export type Config = { password: string; cookie?: SessionConfig["cookie"]; }; export type ServerOptions<T = any, P = any> = { config: () => Config; signin: (args: P) => T; }; const sessionExists = function <T>(config: SessionConfig & { name: string }) { const event = getRequestEvent()!; const cachedSession = !!((event as any).nativeEvent as H3Event).context .sessions?.[config.name]; const sessionCookie = !!getCookie(config.name); return cachedSession || sessionCookie; }; const refreshSession = async function <T>( config: SessionConfig & { name: string }, ) { const event = getRequestEvent() as any; const nativeEvent = (event as any).nativeEvent as H3Event; const refreshedSessions = (event.locals.npRefreshedSessions = event.locals.npRefreshedSessions || {}); if (nativeEvent.handled || refreshedSessions[config.name]) { return; } refreshedSessions[config.name] = true; await updateSession(config); }; const getSessionLazy = async function <T>( config: SessionConfig & { name: string }, ) { const event = getRequestEvent()!; // getSession would try to create a session if it doesnt exist if (!sessionExists(config)) { return; } if (!event.router?.dataOnly) { await refreshSession(config); } return await getSession<{ csrf: string; data: T }>(config); }; export const createServer = function <T, P>( name: string, options: ServerOptions<T, P>, ) { const getSessionConfig = defineRequestFunction("auth-" + name, () => { const config = options.config(); const sameSite = (config.cookie && config.cookie.sameSite) || "lax"; return { name: name, password: config.password, cookie: { path: "/", sameSite, secure: sameSite === "none", ...config.cookie, }, } satisfies SessionConfig; }); const assertCsrf = async function (csrf: string | true) { const sessionConfig = getSessionConfig(); const session = await getSessionLazy(sessionConfig); if (!session) { throw new Error("Access denied!"); } if (csrf === true) { const csrfHeader_ = csrfHeader(sessionConfig.name); csrf = getRequestEvent()!.request.headers.get(csrfHeader_) || ""; } if (session.data.csrf !== csrf) { throw new Error("Access denied!"); } }; const getSession_ = async function () { const session = await getSessionLazy<Awaited<T>>(getSessionConfig()); if (!session) return; return { ...session.data, createdAt: session.createdAt, }; }; const signin = async function (args: P) { const data = await options.signin(args); if (!data) return false; const session = await useSession(getSessionConfig()); await session.update({ data, csrf: createId() }); }; const handler = async function (input: HandlerInput<P>) { const genericInput = input instanceof FormData ? decode(input) : input; const { type, ...rest } = genericInput; if (type === "session") { return await getSession_(); } else if (type === "signin") { return await signin(rest); } else if (type === "signout") { await clearSession(getSessionConfig()); return; } else if (type === "refresh") { const sessionConfig = getSessionConfig(); if (!sessionExists(sessionConfig)) { return; } await refreshSession(sessionConfig); } }; const Auth: Auth<Exclude<Awaited<T>, undefined>, P> = undefined!; return { handler, getSession: getSession_, assertCsrf, Auth, signin, }; };