You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
48 lines
1.7 KiB
48 lines
1.7 KiB
|
4 days ago
|
import { createHmac, timingSafeEqual } from 'node:crypto';
|
||
|
|
import { terms } from '../shared/terms.mjs';
|
||
|
|
|
||
|
|
export const TERMS_COOKIE = 'juegoland_terms';
|
||
|
|
export const TERMS_MAX_AGE = 365 * 24 * 60 * 60;
|
||
|
|
const signature = (payload, secret) =>
|
||
|
|
createHmac('sha256', secret)
|
||
|
|
.update(`juegoland-terms:${payload}`)
|
||
|
|
.digest('hex');
|
||
|
|
|
||
|
|
export function termsCookie(secret, production, now = Date.now()) {
|
||
|
|
const payload = `${terms.version}.${Math.floor(now / 1000)}`;
|
||
|
|
return `${TERMS_COOKIE}=${payload}.${signature(payload, secret)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${TERMS_MAX_AGE}${production ? '; Secure' : ''}`;
|
||
|
|
}
|
||
|
|
export function clearTermsCookie(production) {
|
||
|
|
return `${TERMS_COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${production ? '; Secure' : ''}`;
|
||
|
|
}
|
||
|
|
export function termsAcceptance(header = '', secret, now = Date.now()) {
|
||
|
|
const rejected = {
|
||
|
|
accepted: false,
|
||
|
|
version: terms.version,
|
||
|
|
acceptedAt: null,
|
||
|
|
expiresAt: null,
|
||
|
|
};
|
||
|
|
const value = header
|
||
|
|
.split(';')
|
||
|
|
.map((part) => part.trim())
|
||
|
|
.find((part) => part.startsWith(`${TERMS_COOKIE}=`))
|
||
|
|
?.slice(TERMS_COOKIE.length + 1);
|
||
|
|
const match = value?.match(
|
||
|
|
/^(\d{4}-\d{2}-\d{2}\.\d+)\.(\d{10})\.([a-f0-9]{64})$/,
|
||
|
|
);
|
||
|
|
if (!match || match[1] !== terms.version) return rejected;
|
||
|
|
const issuedAt = Number(match[2]) * 1000;
|
||
|
|
if (issuedAt > now || now >= issuedAt + TERMS_MAX_AGE * 1000) return rejected;
|
||
|
|
const expected = Buffer.from(
|
||
|
|
signature(`${match[1]}.${match[2]}`, secret),
|
||
|
|
'hex',
|
||
|
|
);
|
||
|
|
if (!timingSafeEqual(expected, Buffer.from(match[3], 'hex'))) return rejected;
|
||
|
|
return {
|
||
|
|
accepted: true,
|
||
|
|
version: terms.version,
|
||
|
|
acceptedAt: new Date(issuedAt).toISOString(),
|
||
|
|
expiresAt: new Date(issuedAt + TERMS_MAX_AGE * 1000).toISOString(),
|
||
|
|
};
|
||
|
|
}
|