verifyUrl = $verifyUrl; $this->timeoutSeconds = (int)($_ENV['EXTERNAL_AUTH_TIMEOUT'] ?? 5); } public function __invoke(ServerRequestInterface $request, callable $next) { // Require Authorization: Bearer $authHeader = $request->getHeaderLine('Authorization'); if (empty($authHeader) || !preg_match('/Bearer\s+(.*)/', $authHeader, $matches)) { return ResponseLib::sendFail('Unauthorized: Missing or invalid Authorization header', [], 'E_VALIDATE')->withStatus(401); } // Call external service [$ok, $payload, $status, $err] = $this->callExternalVerify($this->verifyUrl, $authHeader); if (!$ok) { $message = 'Unauthorized'; if ($status >= 500 || $err) { $message = 'Auth service error'; } return ResponseLib::sendFail($message, ['status' => $status, 'error' => $err], 'E_VALIDATE')->withStatus(401); } // Attach attributes from response if present if (is_array($payload)) { if (isset($payload['sub'])) { $request = $request->withAttribute('api_user_id', $payload['sub']); } if (isset($payload['username'])) { $request = $request->withAttribute('api_user', $payload['username']); } } $request = $request->withAttribute('external_auth', true); return $next($request); } private function callExternalVerify(string $url, string $authorization): array { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_NOBODY => false, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => [ 'Authorization: ' . $authorization, 'Accept: application/json' ], CURLOPT_TIMEOUT => $this->timeoutSeconds, CURLOPT_CONNECTTIMEOUT => min(2, $this->timeoutSeconds), ]); $response = curl_exec($ch); if ($response === false) { $err = curl_error($ch); curl_close($ch); return [false, null, 0, $err]; } $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $body = substr($response, $headerSize); curl_close($ch); $ok = ($status >= 200 && $status < 300); $payload = null; if ($body !== '') { $decoded = json_decode($body, true); if (json_last_error() === JSON_ERROR_NONE) { $payload = $decoded; } else { $payload = $body; } } return [$ok, $payload, $status, null]; } }