43 lines
1.7 KiB
PHP
43 lines
1.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
final class ApiAuth
|
|
{
|
|
public static function bearerClaims(): array
|
|
{
|
|
$header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
|
if (!preg_match('/^Bearer\s+([A-Za-z0-9_.-]+)$/i', $header, $matches)) {
|
|
api_json(['error' => 'unauthorized'], 401);
|
|
}
|
|
$claims = JwtToken::verify($matches[1]);
|
|
if ($claims === null) {
|
|
api_json(['error' => 'unauthorized'], 401);
|
|
}
|
|
try {
|
|
$db = Database::getInstance();
|
|
$stmt = $db->prepare('SELECT u.id, u.account_role, u.is_active FROM auth_sessions s JOIN users u ON u.id = s.user_id WHERE s.session_uuid = ? AND s.user_id = ? AND s.revoked_at IS NULL AND s.replaced_by IS NULL AND s.expires_at > UTC_TIMESTAMP() AND u.is_active = 1 LIMIT 1');
|
|
$userId = (int) $claims['sub'];
|
|
$sessionId = $claims['sid'];
|
|
$stmt->bind_param('si', $sessionId, $userId);
|
|
$stmt->execute();
|
|
$result = $stmt->get_result();
|
|
$user = $result->fetch_assoc();
|
|
$stmt->close();
|
|
if (!$user) {
|
|
api_json(['error' => 'unauthorized'], 401);
|
|
}
|
|
return ['user_id' => (int) $user['id'], 'role' => $user['account_role'], 'session_id' => $sessionId];
|
|
} catch (Throwable $exception) {
|
|
error_log('Bearer authorization check failed: ' . $exception->getMessage());
|
|
api_json(['error' => 'service_unavailable'], 503);
|
|
}
|
|
}
|
|
|
|
public static function requireRole(array $auth, array $allowedRoles): void
|
|
{
|
|
if (!in_array($auth['role'] ?? null, $allowedRoles, true)) {
|
|
api_json(['error' => 'forbidden'], 403);
|
|
}
|
|
}
|
|
}
|