*/ private array $httpClientOptions; public function __construct(string $baseUrl, private string $clientId, private string $clientSecret) { $this->baseUrl = rtrim($baseUrl, '/'); Validator::make(['base_url' => $this->baseUrl], ['base_url' => new SafeExternalUrl])->validate(); $this->httpClientOptions = SafeExternalUrl::httpClientOptions($this->baseUrl); } public function validate(): bool { try { $this->login(); return true; } catch (\Throwable) { return false; } } /** * @return array */ public function fetchSecrets(string $projectId, string $environment, string $secretPath = '/'): array { $client = $this->client()->withToken($this->login()); $secretPath = $secretPath ?: '/'; // Older Infisical versions do not expand ${KEY} references unless asked to. $valueOptions = ['expandSecretReferences' => 'true', 'viewSecretValue' => 'true']; $response = $client->get($this->baseUrl.'/api/v4/secrets', [ 'projectId' => $projectId, 'environment' => $environment, 'secretPath' => $secretPath, ...$valueOptions, ]); // Older self-hosted instances only expose the v3 endpoint. if ($response->status() === 404) { $response = $client->get($this->baseUrl.'/api/v3/secrets/raw', [ 'workspaceId' => $projectId, 'environment' => $environment, 'secretPath' => $secretPath, ...$valueOptions, ]); } if (! $response->successful()) { throw new \RuntimeException('Infisical API error: '.($response->json('message') ?? 'HTTP '.$response->status())); } return collect($response->json('secrets', [])) ->mapWithKeys(fn ($secret) => [(string) data_get($secret, 'secretKey') => (string) data_get($secret, 'secretValue', '')]) ->all(); } private function login(): string { $response = $this->client()->post($this->baseUrl.'/api/v1/auth/universal-auth/login', [ 'clientId' => $this->clientId, 'clientSecret' => $this->clientSecret, ]); $accessToken = $response->json('accessToken'); if (! $response->successful() || blank($accessToken)) { throw new \RuntimeException('Infisical login failed: '.($response->json('message') ?? 'HTTP '.$response->status())); } return $accessToken; } private function client(): PendingRequest { return Http::acceptJson() ->withOptions($this->httpClientOptions) ->connectTimeout(5) ->timeout(10); } }