diff --git a/docs/http/test_authorization.http b/docs/http/test_authorization.http new file mode 100644 index 0000000..8c3b96a --- /dev/null +++ b/docs/http/test_authorization.http @@ -0,0 +1,5 @@ +@term=MA + +GET http://localhost:8000/api/dipendenti/cerca?term={{term}} +Authorization: Basic elixforms_ws:password123 +X-API-Key: myApiAccessToken diff --git a/public/index.php b/public/index.php index 78d458c..d4b83a1 100644 --- a/public/index.php +++ b/public/index.php @@ -82,7 +82,7 @@ if (strpos($request->path(), '/api/') === 0) { $authenticator->authenticate($request); } catch (\Throwable $e) { $logger->warning('External API auth failed', ['path' => $request->path(), 'error' => $e->getMessage()]); - $response->json(['error' => 'Unauthorized'], 401); + $response->unauthorized(); } } diff --git a/src/Api/Auth/ApiTokenAuthenticator.php b/src/Api/Auth/ApiTokenAuthenticator.php index 08d5677..86cbdcc 100644 --- a/src/Api/Auth/ApiTokenAuthenticator.php +++ b/src/Api/Auth/ApiTokenAuthenticator.php @@ -15,20 +15,32 @@ class ApiTokenAuthenticator public function authenticate(Request $request): void { - $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''; - if (!$authorization) { + $authorizationHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''; + if (!$authorizationHeader) { throw new \Exception('Missing Authorization header'); } - if (!preg_match('/^Bearer\s+(.*)$/i', trim($authorization), $matches)) { + // Basic authorization formal test + if (!preg_match('/^Basic\s+(.*)$/i', trim($authorizationHeader), $matches)) { throw new \Exception('Invalid Authorization header format'); } + // Username and password test + $decoded = explode(':', base64_decode($matches[1]), 2); + $authorized = empty(array_diff([ $this->config->secret('api_access_username'), $this->config->secret('api_access_password')], $decoded)); + if (!$authorized) { + throw new \Exception('Authorization failed'); + } - $token = $matches[1]; - $expected = $this->config->secret('api_access_token'); - - if (empty($expected) || !hash_equals((string) $expected, (string) $token)) { - throw new \Exception('Invalid API access token'); + // Now, for the X-API-Key only if it was defined in the config (simple way to disable it for testing) + $expectedApiKey = $this->config->secret('api_access_token'); + if ($expectedApiKey !== null && trim($expectedApiKey) !== '') { + $apiKeyHeader = $_SERVER['HTTP_X_API_KEY'] ?? $_SERVER['REDIRECT_HTTP_X_API_KEY'] ?? ''; + if (!$apiKeyHeader) { + throw new \Exception('Missing API access token'); + } + if (!hash_equals((string) $expectedApiKey, (string) $apiKeyHeader)) { + throw new \Exception('Invalid API access token'); + } } } } diff --git a/src/Api/Core/Response.php b/src/Api/Core/Response.php index 2e5a885..1e2780f 100644 --- a/src/Api/Core/Response.php +++ b/src/Api/Core/Response.php @@ -8,4 +8,15 @@ class Response { echo json_encode($data); exit; } + + public function unauthorized(?string $data = null) { + http_response_code(401); + header('Content-Type: application/json'); + header('HTTP/1.1 401 Unauthorized'); + header('Content-Length: 0'); + if ($data !== null && $data !== '') { + echo json_encode($data); + } + exit; + } }