@paroicms/site-generator-plugin
Version:
ParoiCMS Site Generator Plugin
81 lines (80 loc) • 3.41 kB
JavaScript
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 };
}