false, 'error' => 'Method not allowed' ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); exit; } require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/session_bootstrap.php'; function userRespond($payload, $status = 200) { http_response_code($status); echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); exit; } function getAuthorizationToken() { $header = ''; if (isset($_SERVER['HTTP_AUTHORIZATION'])) { $header = trim((string)$_SERVER['HTTP_AUTHORIZATION']); } elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { $header = trim((string)$_SERVER['REDIRECT_HTTP_AUTHORIZATION']); } if ($header === '') { return null; } if (preg_match('/^Bearer\s+(.+)$/i', $header, $matches)) { return trim($matches[1]); } if (preg_match('/^Token\s+(.+)$/i', $header, $matches)) { return trim($matches[1]); } return null; } function tableExists(PDO $pdo, $schema, $table) { $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = :schema AND table_name = :table'); $stmt->execute([ ':schema' => $schema, ':table' => $table ]); return (int)$stmt->fetchColumn() > 0; } function getTableColumns(PDO $pdo, $schema, $table) { $stmt = $pdo->prepare('SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema = :schema AND table_name = :table'); $stmt->execute([ ':schema' => $schema, ':table' => $table ]); $columns = $stmt->fetchAll(PDO::FETCH_COLUMN); return is_array($columns) ? $columns : []; } function resolveUserIdFromBearer(PDO $pdo, $rawToken) { $token = trim((string)$rawToken); if ($token === '') { return null; } $schema = (string)$pdo->query('SELECT DATABASE()')->fetchColumn(); if ($schema === '') { return null; } $candidates = [ ['table' => 'remember_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => null], ['table' => 'user_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'], ['table' => 'api_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'], ['table' => 'access_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'], ['table' => 'auth_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'] ]; $hashes = [ $token, hash('sha256', $token) ]; foreach ($candidates as $candidate) { if (!tableExists($pdo, $schema, $candidate['table'])) { continue; } $columns = getTableColumns($pdo, $schema, $candidate['table']); if (!in_array($candidate['user'], $columns, true) || !in_array($candidate['token'], $columns, true)) { continue; } $select = 'SELECT `' . $candidate['user'] . '` AS user_id FROM `' . $candidate['table'] . '` WHERE `' . $candidate['token'] . '` IN (:token_raw, :token_sha)'; if ($candidate['expires'] !== null && in_array($candidate['expires'], $columns, true)) { $select .= ' AND (`' . $candidate['expires'] . '` IS NULL OR `' . $candidate['expires'] . '` > NOW())'; } if ($candidate['revoked'] !== null && in_array($candidate['revoked'], $columns, true)) { $select .= ' AND `' . $candidate['revoked'] . '` IS NULL'; } $select .= ' ORDER BY user_id DESC LIMIT 1'; $stmt = $pdo->prepare($select); $stmt->execute([ ':token_raw' => $hashes[0], ':token_sha' => $hashes[1] ]); $row = $stmt->fetch(PDO::FETCH_ASSOC); if ($row && isset($row['user_id']) && (int)$row['user_id'] > 0) { return (int)$row['user_id']; } } return null; } function requireUserAuth() { if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true && !empty($_SESSION['user_id'])) { return (int)$_SESSION['user_id']; } global $pdo; $token = getAuthorizationToken(); if ($token !== null && isset($pdo) && ($pdo instanceof PDO)) { $tokenUserId = resolveUserIdFromBearer($pdo, $token); if ($tokenUserId !== null) { $_SESSION['logged_in'] = true; $_SESSION['user_id'] = $tokenUserId; return $tokenUserId; } } userRespond([ 'success' => false, 'error' => 'Unauthorized' ], 401); } require_once __DIR__ . '/../administration/includes/config.php'; if (!isset($pdo) || !($pdo instanceof PDO)) { userRespond([ 'success' => false, 'error' => 'Database connection not initialized' ], 500); }