dataCache = $dataCache; $this->config = $config; $this->factory = $factory; $this->log = $log; } /** * @return Key[] */ public function get(): array { $list = []; $rawKeys = $this->getRaw(); foreach ($rawKeys as $raw) { try { $list[] = $this->factory->create($raw); } catch (UnsupportedKey $e) { $this->log->debug("OIDC: Unsupported key " . print_r($raw, true)); } } return $list; } /** * @return stdClass[] */ private function getRaw(): array { $raw = $this->getRawFromCache(); if (!$raw) { $raw = $this->load(); $this->storeRawToCache($raw); } return $raw; } /** * @return stdClass[] */ private function load(): array { /** @var ?string $endpoint */ $endpoint = $this->config->get('oidcJwksEndpoint'); if (!$endpoint) { throw new RuntimeException("JSON Web Key Set endpoint not specified in settings."); } $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => $endpoint, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => self::REQUEST_TIMEOUT, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', ]); /** @var string|false $response */ $response = curl_exec($curl); $error = curl_error($curl); $status = curl_getinfo($curl, CURLINFO_HTTP_CODE) ?? 0; curl_close($curl); if ($response === false) { $response = ''; } if ($error) { throw new RuntimeException("OIDC: JWKS request error. Status: {$status}."); } $parsedResponse = null; try { $parsedResponse = Json::decode($response); } catch (JsonException $e) {} if (!$parsedResponse instanceof stdClass || !isset($parsedResponse->keys)) { throw new RuntimeException("OIDC: JWKS bad response."); } return $parsedResponse->keys; } /** * @return ?stdClass[] */ private function getRawFromCache(): ?array { if (!$this->config->get('useCache')) { return null; } if (!$this->dataCache->has(self::CACHE_KEY)) { return null; } $data = $this->dataCache->get(self::CACHE_KEY); if (!$data instanceof stdClass) { return null; } /** @var ?int $timestamp */ $timestamp = $data->timestamp; if (!$timestamp) { return null; } $period = '-' . ($this->config->get('oidcJwksCachePeriod') ?? self::CACHE_PERIOD); if ($timestamp < DateTime::createNow()->modify($period)->getTimestamp()) { return null; } /** @var ?stdClass[] $keys */ $keys = $data->keys ?? null; if ($keys === null) { return null; } return $keys; } /** * @param stdClass[] $raw */ private function storeRawToCache(array $raw): void { if (!$this->config->get('useCache')) { return; } $data = (object) [ 'timestamp' => time(), 'keys' => $raw, ]; $this->dataCache->store(self::CACHE_KEY, $data); } }