Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 2

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/composer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Create docker network
run: |
Expand All @@ -55,7 +55,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Create docker network
run: |
Expand All @@ -70,7 +70,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Create docker network
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/github_build_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
APP_ENV: prod
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Create a release in GitHub
run: gh release create ${{ github.ref_name }} --verify-tag --generate-notes
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/markdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Create docker network
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/php.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
name: PHP - Check Coding Standards
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Create docker network
run: |
Expand All @@ -29,7 +29,7 @@ jobs:
name: PHPStan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Create docker network
run: |
Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:
php: "8.5"
prefer: prefer-stable
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Create docker network
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/yaml.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
yaml-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Create docker network
run: |
Expand Down
4 changes: 0 additions & 4 deletions .markdownlint.json

This file was deleted.

23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [4.1.2] - 2026-05-11

- Chained `previous` consistently in `OpenIdConfigurationProvider` catch
blocks (`validateIdToken`, `getJwtVerificationKeys`, `fetchJsonResource`,
`getConfiguration`) so consumers can walk back to the underlying
Guzzle/firebase/PSR exception via `getPrevious()`
- Tightened `@throws` phpdoc on public methods (`validateIdToken`,
`getIdToken`, `getBaseAuthorizationUrl`) to enumerate the actual
transitive exceptions instead of declaring only the parent type. Removed
the inaccurate `ClientExceptionInterface` declaration on `getIdToken`
(the catch-all wraps it as `CodeException` with the original chained)
- Documented HTTP timeout/proxy/verify configuration via constructor `$options`
(capability already provided by league/oauth2-client; no code change)
- Bumped `actions/checkout` from v5 to v6 in all CI workflows
- Added `ci` profile to docker-compose matrix services to avoid starting them during local development
- Fixed `test:coverage` task to run via docker-compose with `XDEBUG_MODE=coverage`
- Fixed `test:run` to remove stale `composer.lock` before `composer update`
- Fixed `test:matrix:reset` to use `--profile ci` flag
- Removed unused `.markdownlint.json`

## [4.1.1] - 2026-05-07

### Security
Expand Down Expand Up @@ -143,7 +163,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- This CHANGELOG file to hopefully serve as an evolving example of a
standardized open source project CHANGELOG.

[Unreleased]: https://github.com/itk-dev/openid-connect/compare/4.1.1...HEAD
[Unreleased]: https://github.com/itk-dev/openid-connect/compare/4.1.2...HEAD
[4.1.2]: https://github.com/itk-dev/openid-connect/compare/4.1.1...4.1.2
[4.1.1]: https://github.com/itk-dev/openid-connect/compare/4.1.0...4.1.1
[4.1.0]: https://github.com/itk-dev/openid-connect/compare/4.0.3...4.1.0
[4.0.3]: https://github.com/itk-dev/openid-connect/compare/4.0.2...4.0.3
Expand Down
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Github](https://img.shields.io/badge/source-itk--dev/openid--connect-blue?style=flat-square)](https://github.com/itk-dev/openid-connect)
[![Release](https://img.shields.io/packagist/v/itk-dev/openid-connect.svg?style=flat-square&label=release)](https://packagist.org/packages/itk-dev/openid-connect)
[![PHP Version](https://img.shields.io/packagist/php-v/itk-dev/openid-connect.svg?style=flat-square&colorB=%238892BF)](https://www.php.net/downloads)
[![Build Status](https://img.shields.io/github/actions/workflow/status/itk-dev/openid-connect/pr.yaml?label=CI&logo=github&style=flat-square)](https://github.com/itk-dev/openid-connect/actions?query=workflow%3A%22Test+%26+Code+Style+Review%22)
[![Build Status](https://img.shields.io/github/actions/workflow/status/itk-dev/openid-connect/php.yaml?branch=develop&label=CI&logo=github&style=flat-square)](https://github.com/itk-dev/openid-connect/actions/workflows/php.yaml?query=branch%3Adevelop)
[![Codecov Code Coverage](https://img.shields.io/codecov/c/gh/itk-dev/openid-connect?label=codecov&logo=codecov&style=flat-square)](https://codecov.io/gh/itk-dev/openid-connect)
[![Read License](https://img.shields.io/packagist/l/itk-dev/openid-connect.svg?style=flat-square&colorB=darkcyan)](https://github.com/itk-dev/openid-connect/blob/master/LICENSE.md)
[![Package downloads on Packagist](https://img.shields.io/packagist/dt/itk-dev/openid-connect.svg?style=flat-square&colorB=darkmagenta)](https://packagist.org/packages/itk-dev/openid-connect/stats)
Expand Down Expand Up @@ -77,6 +77,33 @@ $provider = new OpenIdConfigurationProvider([
]);
```

##### HTTP timeout, proxy, and TLS verification

This library extends `league/oauth2-client`, which uses Guzzle for HTTP. To
bound how long a request to the IdP can take (recommended for production),
pass `timeout` (seconds) in the constructor `$options`:

```php
$provider = new OpenIdConfigurationProvider([
// ... required options ...
'timeout' => 5,
'proxy' => 'http://proxy.example.com:8080',
'verify' => true, // only consulted by Guzzle when proxy is set
]);
```

`league/oauth2-client` whitelists exactly these three keys (`timeout`, `proxy`,
`verify`) and forwards them to the underlying Guzzle client. Other Guzzle
options (e.g. `connect_timeout`) are silently dropped.

> **Why Guzzle and not Symfony HttpClient?**
> `league/oauth2-client` hard-types its HTTP client as
> `GuzzleHttp\ClientInterface`. Symfony HttpClient implements PSR-18 / HTTPlug,
> not Guzzle's interface, and there is no maintained adapter going Symfony →
> Guzzle. To plug in a non-Guzzle client you would need to write such an
> adapter yourself and pass it via `$collaborators['httpClient']` to the
> constructor.

##### Leeway

To account for clock skew times between the signing and verifying servers,
Expand Down
5 changes: 3 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ tasks:
test:coverage:
desc: Run tests with coverage
cmds:
- "{{.PHP}} vendor/bin/phpunit --coverage-clover=coverage/unit.xml"
- "{{.DOCKER_COMPOSE}} exec -e XDEBUG_MODE=coverage phpfpm vendor/bin/phpunit --coverage-text --coverage-clover=coverage/unit.xml"

test:run:
desc: "Run tests for a PHP version and dependency set (e.g. task test:run PHP=8.4 DEPS=lowest)"
Expand All @@ -162,6 +162,7 @@ tasks:
cp /app/vendor/.composer.lock /app/composer.lock
composer install -q
else
rm -f /app/composer.lock
composer update -q --{{.PREFER}}
cp /app/composer.lock /app/vendor/.composer.lock
fi'
Expand All @@ -170,7 +171,7 @@ tasks:
test:matrix:reset:
desc: Remove cached vendor volumes to force a fresh dependency resolve
cmds:
- "{{.DOCKER_COMPOSE}} down --volumes"
- "{{.DOCKER_COMPOSE}} --profile ci down --volumes"

test:matrix:
desc: Run tests across all PHP versions (mirrors CI matrix)
Expand Down
16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,22 @@ services:
extends:
service: phpfpm
image: itkdev/php8.4-fpm:latest
profiles:
- ci

phpfpm85:
extends:
service: phpfpm
image: itkdev/php8.5-fpm:latest
profiles:
- ci

# Test matrix services (PHP version × dependency set)
phpfpm83-stable:
extends:
service: phpfpm
profiles:
- ci
volumes:
- .:/app
- phpfpm83-stable-vendor:/app/vendor
Expand All @@ -48,6 +54,8 @@ services:
phpfpm83-lowest:
extends:
service: phpfpm
profiles:
- ci
volumes:
- .:/app
- phpfpm83-lowest-vendor:/app/vendor
Expand All @@ -56,6 +64,8 @@ services:
phpfpm84-stable:
extends:
service: phpfpm84
profiles:
- ci
volumes:
- .:/app
- phpfpm84-stable-vendor:/app/vendor
Expand All @@ -64,6 +74,8 @@ services:
phpfpm84-lowest:
extends:
service: phpfpm84
profiles:
- ci
volumes:
- .:/app
- phpfpm84-lowest-vendor:/app/vendor
Expand All @@ -72,6 +84,8 @@ services:
phpfpm85-stable:
extends:
service: phpfpm85
profiles:
- ci
volumes:
- .:/app
- phpfpm85-stable-vendor:/app/vendor
Expand All @@ -80,6 +94,8 @@ services:
phpfpm85-lowest:
extends:
service: phpfpm85
profiles:
- ci
volumes:
- .:/app
- phpfpm85-lowest-vendor:/app/vendor
Expand Down
26 changes: 18 additions & 8 deletions src/Security/OpenIdConfigurationProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ public function getGuarded(): array
return ['cacheItemPool', 'cacheDuration', 'openIDConnectMetadataUrl', 'leeway', 'allowHttp'];
}

/**
* @throws CacheException
* @throws HttpException
* @throws JsonException
*/
public function getBaseAuthorizationUrl(): string
{
return $this->getConfiguration('authorization_endpoint');
Expand Down Expand Up @@ -197,7 +202,13 @@ public function getEndSessionUrl(?string $postLogoutRedirectUri = null, ?string
* @return object
* The JWT's payload as a PHP object
*
* @throws ItkOpenIdConnectException
* @throws CacheException
* @throws ClaimsException
* @throws DecodeException
* @throws HttpException
* @throws JsonException
* @throws KeyException
* @throws ValidationException
*/
public function validateIdToken(string $idToken, string $nonce): object
{
Expand All @@ -222,7 +233,7 @@ public function validateIdToken(string $idToken, string $nonce): object

return $claims;
} catch (\UnexpectedValueException $e) {
throw new ValidationException('ID token validation failed: '.$e->getMessage());
throw new ValidationException('ID token validation failed: '.$e->getMessage(), 0, $e);
}
}

Expand All @@ -235,8 +246,7 @@ public function validateIdToken(string $idToken, string $nonce): object
* @return string
* The ID token
*
* @throws CodeException
* @throws ClientExceptionInterface
* @throws CodeException Wraps any \Exception thrown by token-endpoint HTTP, JSON parsing, or `getConfiguration()` (with `previous` chained)
*/
public function getIdToken(string $code): string
{
Expand Down Expand Up @@ -371,7 +381,7 @@ private function getJwtVerificationKeys(): array
$this->cacheItemPool->save($item);
}
} catch (InvalidArgumentException $e) {
throw new CacheException($e->getMessage());
throw new CacheException($e->getMessage(), 0, $e);
}

return $keys;
Expand Down Expand Up @@ -418,9 +428,9 @@ private function fetchJsonResource(string $resourceUrl): array

return $resource;
} catch (ClientExceptionInterface $e) {
throw new HttpException($e->getMessage());
throw new HttpException($e->getMessage(), 0, $e);
} catch (\JsonException $e) {
throw new JsonException($e->getMessage());
throw new JsonException($e->getMessage(), 0, $e);
}
}

Expand Down Expand Up @@ -461,7 +471,7 @@ private function getConfiguration(string $key): string

return $value;
} catch (InvalidArgumentException $e) {
throw new CacheException($e->getMessage());
throw new CacheException($e->getMessage(), 0, $e);
}
}

Expand Down
Loading