connectTimeout(3)->acceptJson()->get($url); } catch (Throwable $e) { throw new OidcDiscoveryException("Failed to fetch discovery document: {$e->getMessage()}", previous: $e); } if ($response->failed()) { throw new OidcDiscoveryException("Discovery endpoint returned HTTP {$response->status()}"); } $json = $response->json(); if (! is_array($json) || $json === []) { throw new OidcDiscoveryException('Discovery endpoint returned invalid JSON.'); } return $json; }); $discovery = OidcDiscoveryDocument::fromArray($payload); if (rtrim($discovery->issuer, '/') !== $issuerUrl) { throw new OidcDiscoveryException('Discovery issuer does not match the configured issuer URL.'); } return $discovery; } /** * @return array */ public function jwks(string $jwksUri): array { $cacheKey = 'oidc:jwks:'.hash('sha256', $jwksUri); return Cache::remember($cacheKey, 21600, function () use ($jwksUri): array { try { $response = Http::timeout(5)->connectTimeout(3)->acceptJson()->get($jwksUri); } catch (Throwable $e) { throw new OidcJwksException("Failed to fetch JWKS: {$e->getMessage()}", previous: $e); } if ($response->failed()) { throw new OidcJwksException("JWKS endpoint returned HTTP {$response->status()}"); } $json = $response->json(); if (! is_array($json) || ! is_array($json['keys'] ?? null)) { throw new OidcJwksException("JWKS endpoint returned an invalid payload without 'keys'."); } return $json; }); } }