76 lines
1.8 KiB
JavaScript
76 lines
1.8 KiB
JavaScript
const crypto = require("crypto");
|
|
|
|
const SESSION_COOKIE = "sb_dashboard";
|
|
const SESSION_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
|
|
function signSession(payload, secret) {
|
|
const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
const sig = crypto
|
|
.createHmac("sha256", secret)
|
|
.update(body)
|
|
.digest("base64url");
|
|
return `${body}.${sig}`;
|
|
}
|
|
|
|
function verifySession(token, secret) {
|
|
if (!token || typeof token !== "string") return null;
|
|
const dot = token.lastIndexOf(".");
|
|
if (dot <= 0) return null;
|
|
|
|
const body = token.slice(0, dot);
|
|
const sig = token.slice(dot + 1);
|
|
const expected = crypto
|
|
.createHmac("sha256", secret)
|
|
.update(body)
|
|
.digest("base64url");
|
|
|
|
if (sig.length !== expected.length) return null;
|
|
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const payload = JSON.parse(
|
|
Buffer.from(body, "base64url").toString("utf8"),
|
|
);
|
|
if (!payload?.userId || !payload?.exp) return null;
|
|
if (Date.now() > payload.exp) return null;
|
|
return payload;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function createSessionPayload(user) {
|
|
return {
|
|
userId: user.id,
|
|
username: user.username,
|
|
globalName: user.global_name || null,
|
|
avatar: user.avatar || null,
|
|
exp: Date.now() + SESSION_MAX_AGE_MS,
|
|
};
|
|
}
|
|
|
|
function sessionCookieOptions(maxAgeMs = SESSION_MAX_AGE_MS, opts = {}) {
|
|
const secure =
|
|
opts.secure !== undefined
|
|
? Boolean(opts.secure)
|
|
: process.env.NODE_ENV === "production";
|
|
return {
|
|
httpOnly: true,
|
|
secure,
|
|
sameSite: "lax",
|
|
maxAge: maxAgeMs,
|
|
path: "/",
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
SESSION_COOKIE,
|
|
SESSION_MAX_AGE_MS,
|
|
signSession,
|
|
verifySession,
|
|
createSessionPayload,
|
|
sessionCookieOptions,
|
|
};
|