133 lines
3.6 KiB
PHP
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
|
|
}
|