71 lines
1.7 KiB
TypeScript
71 lines
1.7 KiB
TypeScript
const SESSION_COOKIE = "wp_admin_session";
|
|
const SESSION_TTL_SECONDS = 60 * 60 * 24 * 7;
|
|
|
|
function getSecret() {
|
|
const secret = process.env.ADMIN_SESSION_SECRET;
|
|
if (!secret) {
|
|
throw new Error("ADMIN_SESSION_SECRET is not set");
|
|
}
|
|
return secret;
|
|
}
|
|
|
|
function toHex(buffer: ArrayBuffer) {
|
|
return Array.from(new Uint8Array(buffer))
|
|
.map((b) => b.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
}
|
|
|
|
async function sign(value: string) {
|
|
const key = await crypto.subtle.importKey(
|
|
"raw",
|
|
new TextEncoder().encode(getSecret()),
|
|
{ name: "HMAC", hash: "SHA-256" },
|
|
false,
|
|
["sign"]
|
|
);
|
|
|
|
const signature = await crypto.subtle.sign(
|
|
"HMAC",
|
|
key,
|
|
new TextEncoder().encode(value)
|
|
);
|
|
|
|
return toHex(signature);
|
|
}
|
|
|
|
export async function createSessionToken(email: string) {
|
|
const expiresAt = Math.floor(Date.now() / 1000) + SESSION_TTL_SECONDS;
|
|
const payload = `${email}.${expiresAt}`;
|
|
const signature = await sign(payload);
|
|
return `${payload}.${signature}`;
|
|
}
|
|
|
|
export async function verifySessionToken(token?: string | null) {
|
|
if (!token) return false;
|
|
|
|
const parts = token.split(".");
|
|
if (parts.length < 3) return false;
|
|
|
|
const signature = parts.pop()!;
|
|
const expiresAt = Number(parts.pop());
|
|
const email = parts.join(".");
|
|
|
|
if (!email || !expiresAt || Number.isNaN(expiresAt)) return false;
|
|
if (expiresAt < Math.floor(Date.now() / 1000)) return false;
|
|
|
|
const payload = `${email}.${expiresAt}`;
|
|
const expectedSignature = await sign(payload);
|
|
|
|
return signature === expectedSignature;
|
|
}
|
|
|
|
export function getSessionCookieName() {
|
|
return SESSION_COOKIE;
|
|
}
|
|
|
|
export function getAdminCredentials() {
|
|
return {
|
|
email: process.env.ADMIN_EMAIL || "",
|
|
password: process.env.ADMIN_PASSWORD || "",
|
|
};
|
|
} |