61 lines
1.6 KiB
PHP
61 lines
1.6 KiB
PHP
<?php
|
|
define('TICKET_LIFETIME_SECONDS', 20 * 60);
|
|
|
|
function getTicketSecret(): string
|
|
{
|
|
$secret = getenv('TICKET_SECRET');
|
|
if ($secret === false || $secret === '') {
|
|
error_log('ticket.php: TICKET_SECRET is not configured');
|
|
throw new RuntimeException('Server misconfiguration');
|
|
}
|
|
|
|
return $secret;
|
|
}
|
|
|
|
function createTicket(int $userId, string $purpose = 'default'): string
|
|
{
|
|
$expiresAt = time() + TICKET_LIFETIME_SECONDS;
|
|
$payload = $userId . '|' . $expiresAt . '|' . $purpose;
|
|
$signature = hash_hmac('sha256', $payload, getTicketSecret());
|
|
|
|
return base64_encode($payload . '.' . $signature);
|
|
}
|
|
|
|
function decodeTicket(string $ticket, string $purpose = 'default'): ?int
|
|
{
|
|
$decoded = base64_decode($ticket, true);
|
|
if ($decoded === false) {
|
|
return null;
|
|
}
|
|
|
|
$separatorPosition = strrpos($decoded, '.');
|
|
if ($separatorPosition === false) {
|
|
return null;
|
|
}
|
|
|
|
$payload = substr($decoded, 0, $separatorPosition);
|
|
$signature = substr($decoded, $separatorPosition + 1);
|
|
|
|
$expectedSignature = hash_hmac('sha256', $payload, getTicketSecret());
|
|
if (!hash_equals($expectedSignature, $signature)) {
|
|
return null;
|
|
}
|
|
|
|
$parts = explode('|', $payload, 3);
|
|
if (count($parts) !== 3 || !ctype_digit($parts[0]) || !ctype_digit($parts[1])) {
|
|
return null;
|
|
}
|
|
|
|
[$userId, $expiresAt, $ticketPurpose] = $parts;
|
|
|
|
if (!hash_equals($purpose, $ticketPurpose)) {
|
|
return null;
|
|
}
|
|
|
|
if ((int) $expiresAt < time()) {
|
|
return null;
|
|
}
|
|
|
|
return (int) $userId;
|
|
}
|