yogiboook_new/public/userarea/api/_bootstrap.php
2025-12-27 20:48:44 +01:00

133 lines
3.6 KiB
PHP

<?php
// public/userarea/api/_bootstrap.php
// Auth via Laravel Sanctum personal access tokens (PDO only).
// Table: auth_personal_access_tokens
// Exposes: $pdo (PDO), $iduserlogin (int)
declare(strict_types=1);
// CORS for Flutter Web (dev server runs on a different port)
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Authorization, Content-Type, Accept');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Max-Age: 86400');
// Preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../class/db-functions.php';
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
/**
* Get Authorization header (Apache/Nginx/CGI compatible)
*/
function getAuthorizationHeader(): ?string
{
if (function_exists('getallheaders')) {
$headers = getallheaders();
foreach ($headers as $k => $v) {
if (strtolower($k) === 'authorization') return $v;
}
}
if (!empty($_SERVER['HTTP_AUTHORIZATION'])) return $_SERVER['HTTP_AUTHORIZATION'];
if (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) return $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
return null;
}
/**
* Extract Bearer token
*/
function getBearerToken(): ?string
{
$auth = getAuthorizationHeader();
if (!$auth) return null;
if (preg_match('/Bearer\s+(.*)$/i', $auth, $m)) {
return trim($m[1]);
}
return null;
}
$bearer = getBearerToken();
if (!$bearer) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Unauthorized (missing Bearer token).']);
exit;
}
// Sanctum plain token format: "<id>|<plainTextToken>"
if (strpos($bearer, '|') === false) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Unauthorized (invalid token format).']);
exit;
}
[$tokenId, $plainToken] = explode('|', $bearer, 2);
$tokenId = (int)$tokenId;
if ($tokenId <= 0 || $plainToken === '') {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Unauthorized (invalid token).']);
exit;
}
// --- IMPORTANT: your table name ---
$tokensTable = 'auth_personal_access_tokens';
// Lookup token by id
$stmt = $pdo->prepare("
SELECT id, tokenable_id, token, expires_at
FROM {$tokensTable}
WHERE id = ?
LIMIT 1
");
$stmt->execute([$tokenId]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Unauthorized (token not found).']);
exit;
}
// Optional expiry check
if (!empty($row['expires_at'])) {
$now = new DateTimeImmutable('now', new DateTimeZone('Europe/Rome'));
$exp = new DateTimeImmutable($row['expires_at'], new DateTimeZone('Europe/Rome'));
if ($exp <= $now) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Unauthorized (token expired).']);
exit;
}
}
// Sanctum stores SHA-256 hash of the plain token
$hash = hash('sha256', $plainToken);
if (!hash_equals((string)$row['token'], $hash)) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Unauthorized (token mismatch).']);
exit;
}
$iduserlogin = (int)$row['tokenable_id'];
if ($iduserlogin <= 0) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'Unauthorized (invalid user).']);
exit;
}
// Update last_used_at (best effort)
try {
$pdo->prepare("UPDATE {$tokensTable} SET last_used_at = NOW() WHERE id = ?")->execute([$tokenId]);
} catch (Throwable $e) {
// ignore
}