UNPKG

@paroicms/site-generator-plugin

Version:

ParoiCMS Site Generator Plugin

81 lines (80 loc) 3.41 kB
import { ApiError } from "@paroicms/public-server-lib"; import { nanoid } from "nanoid"; import { getInvalidSessionError } from "../db/db-read.queries.js"; import { insertSession, updateSession } from "../db/db-write.queries.js"; const { sign, verify } = (await import("jsonwebtoken")).default; export async function newSessionCommand(ctx, command) { const { service } = ctx; const { recaptchaToken, language } = command; const isValid = await service.validateRecaptchaResponse(recaptchaToken); if (!isValid) throw new ApiError("Invalid reCAPTCHA token", 400); const date = new Date(); const compressedTs = `${date.toISOString().replace(/[-:]/g, "").split(".")[0]}Z`; const sessionId = `${compressedTs}-${nanoid()}`; const token = sign({ sessionId }, ctx.jwtSecret, { expiresIn: "48h" }); const expireAt = new Date(date.getTime() + 48 * 60 * 60 * 1000); await insertSession(ctx, { sessionId, language, expireAt }); return { token }; } /** * Renews a session token and extends its expiration time */ export async function renewSessionCommand(ctx, command) { const { service } = ctx; const { recaptchaToken } = command; const isValid = await service.validateRecaptchaResponse(recaptchaToken); if (!isValid) throw new ApiError("Invalid reCAPTCHA token", 400); let payload; try { // When renewing, we verify the token but allow expired tokens payload = verify(command.sessionToken, ctx.jwtSecret, { ignoreExpiration: true }); } catch (error) { if (error.name === "JsonWebTokenError") throw new ApiError("Invalid session token", 401); throw error; } if (!payload || !("sessionId" in payload) || typeof payload.sessionId !== "string") { throw new ApiError("Invalid session token", 401); } const sessionId = payload.sessionId; // Check if the session is valid (not deleted, not too many steps) const errorMessage = await getInvalidSessionError(ctx, sessionId); if (errorMessage && errorMessage !== "expired") { throw new ApiError(`Invalid session "${sessionId}": ${errorMessage}`, 401); } // Generate new expiration time - 48 hours from now const expireAt = new Date(Date.now() + 48 * 60 * 60 * 1000); // Update the session's expiration time await updateSession({ ...ctx, sessionId }, { expireAt }); // Generate a new token const token = sign({ sessionId }, ctx.jwtSecret, { expiresIn: "48h" }); return { token }; } /** * Verifies a session token and returns the session ID if valid */ export async function verifySessionToken(ctx, token) { let payload; try { payload = verify(token, ctx.jwtSecret); } catch (error) { if (error.name === "TokenExpiredError") throw new ApiError("Session token expired", 401); if (error.name === "JsonWebTokenError") throw new ApiError("Invalid session token", 401); throw error; } if (!payload || !("sessionId" in payload) || typeof payload.sessionId !== "string") { throw new ApiError("Invalid session token", 401); } const sessionId = payload.sessionId; const errorMessage = await getInvalidSessionError(ctx, sessionId); if (errorMessage) { throw new ApiError(`Invalid session "${sessionId}": ${errorMessage}`, 401); } return { sessionId }; }