diff --git a/CHANGELOG.md b/CHANGELOG.md index d263980..3ca4525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- `http_client_options.timeout` now defaults to `30` seconds when not set, + so a slow or hung identity provider can no longer block worker processes + indefinitely. Previously no timeout was applied and Guzzle's own default + (`0` — wait forever) was used. Set `timeout: 0` to restore the old + behaviour, or override per provider. + ## [5.0.0] - 2026-06-02 ### Changed (BREAKING) diff --git a/README.md b/README.md index 4892b13..529eae6 100644 --- a/README.md +++ b/README.md @@ -154,9 +154,10 @@ Set the actual values your `env.local` file to ensure they are not committed to #### Configuring the HTTP client Each provider accepts an optional `http_client_options` block that is forwarded -to the underlying Guzzle HTTP client used by `league/oauth2-client`. This is -useful for setting a request timeout so a slow IdP cannot block worker -processes indefinitely. +to the underlying Guzzle HTTP client used by `league/oauth2-client`. The bundle +applies a sensible default `timeout` of `30` seconds so a slow IdP cannot block +worker processes indefinitely (Guzzle's own default is `0`, i.e. wait forever). +Override it per provider, or set it to `0` to opt back into Guzzle's behaviour. ```yaml itkdev_openid_connect: @@ -166,7 +167,7 @@ itkdev_openid_connect: # ... existing keys ... # @see https://docs.guzzlephp.org/en/stable/request-options.html http_client_options: - # Float describing the total timeout of the request in seconds. Use 0 to wait indefinitely (the default behavior). + # Float describing the total timeout of the request in seconds. Defaults to 30; set to 0 to wait indefinitely. timeout: 5.0 # Pass a string to specify an HTTP proxy, or an array to specify different proxies for different protocols. (Default: none) proxy: "%env(string:HTTP_PROXY)%" diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index a6bd2a4..5b1393d 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -87,10 +87,12 @@ public function getConfigTreeBuilder(): TreeBuilder // Uses Guzzle under the hood through itk-dev/openid-connect -> league/oauth2-client -> guzzlehttp/guzzle ->arrayNode('http_client_options') ->info('Options forwarded to the underlying Guzzle HTTP client. league/oauth2-client only forwards: timeout, proxy, verify (verify is only consulted when proxy is set).') + ->addDefaultsIfNotSet() ->children() // @see https://docs.guzzlephp.org/en/stable/request-options.html#timeout ->floatNode('timeout') - ->info('Total request timeout in seconds') + ->info('Total request timeout in seconds. Defaults to 30; set to 0 to wait indefinitely (Guzzle\'s own default).') + ->defaultValue(30.0) ->end() // @see https://docs.guzzlephp.org/en/stable/request-options.html#proxy ->scalarNode('proxy') diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index b6ef9e0..92fc03d 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -132,7 +132,7 @@ public function testHttpClientOptionsAccepted(): void $this->assertTrue($httpClientOptions['verify']); } - public function testHttpClientOptionsAbsentByDefault(): void + public function testHttpClientOptionsDefaultsApplied(): void { $config = $this->processor->processConfiguration( $this->configuration, @@ -140,10 +140,12 @@ public function testHttpClientOptionsAbsentByDefault(): void ); $providerOptions = $config['openid_providers']['provider1']['options']; - // The block has no default value, so an omitted input must produce no - // http_client_options key in the processed config — otherwise an empty - // array would still be merged into the provider options. - $this->assertArrayNotHasKey('http_client_options', $providerOptions); + // The block carries a sensible default timeout so an omitted input still + // protects workers from a hung IdP. proxy/verify have no default and so + // stay absent (Guzzle's own defaults apply). + $this->assertSame(30.0, $providerOptions['http_client_options']['timeout']); + $this->assertArrayNotHasKey('proxy', $providerOptions['http_client_options']); + $this->assertArrayNotHasKey('verify', $providerOptions['http_client_options']); } public function testHttpClientOptionsRejectsUnknownKey(): void