Viel neues

This commit is contained in:
Sven Steinert
2026-04-30 12:06:00 +02:00
parent 118809bfae
commit fce31ebcd7
1274 changed files with 181255 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
**List of common tasks a pull request require complete**
- [ ] Changelog entry is added or the pull request don't alter library's functionality

View File

@@ -0,0 +1,38 @@
---
name: build
on: [push, pull_request]
env:
DEFAULT_COMPOSER_FLAGS: "--prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi"
jobs:
phpunit:
name: PHP ${{ matrix.php }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v1
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer update $DEFAULT_COMPOSER_FLAGS
- name: Run unit tests
run: vendor/bin/phpunit --verbose --colors=always tests

View File

@@ -0,0 +1,4 @@
/.idea
/vendor
/composer.lock
.phpunit.result.cache

View File

@@ -0,0 +1,180 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0] - 2023-12-13
### Added
- PHP 7.0 is required. #327
- Support for signed and encrypted UserInfo response and ID Token. #305
- Allow to set User-Agent header. #370
### Fixed
- User-Agent is set for any HTTP method in fetchURL() (not just POST). #382
- Update visibility of getWellKnownConfigValue to protected. #363
- Fixed issue on authentication for php8. #354
- Update construct typehint in docblock. #364
- Fixed LogoutToken verification for single value aud claims. #334
- Update well known config value function response types. #376
## [0.9.10] - 2022-09-30
### Fixed
- `private_key_jwt` and `client_secret_jwt` need to explicitly be enabled #331
## [0.9.9] - 2022-09-28
### Added
- Added support for back-channel logout. #302
- Added support for `private_key_jwt` Client Authentication method #322
- Added support for `client_secret_jwt` Client Authentication method #324
- Added PS512 encryption support #342
### Fixed
- Harden self-signed JWK header usage. #323
## [0.9.8] - 2022-08-05
### Fixed
- Do not use PKCE if IdP does not support it. #317
## [0.9.7] - 2022-07-13
### Added
- Support for Self-Contained JWTs. #308
- Support for RFC8693 Token Exchange Request. #275
### Fixed
- PHP 5.4 compatibility. #304
- Use session_status(). #306
## [0.9.6] - 2022-05-08
### Added
- Support for [phpseclib/phpseclib](https://phpseclib.com/) version **3**. #260
- Support client_secret on token endpoint with PKCE. #293
- Added new parameter to `requestTokens()` to pass custom HTTP headers #297
### Changed
- Allow serializing `OpenIDConnectClient` using `serialize()` #295
## [0.9.5] - 2021-11-24
### Changed
- signOut() Method parameter $accessToken -> $idToken to prevent confusion about access and id tokens usage. #127
- Fixed issue where missing nonce within the claims was causing an exception. #280
## [0.9.4] - 2021-11-21
### Added
- Enabled `client_secret_basic` authentication on `refreshToken()` #215
- Basic auth support for requestResourceOwnerToken #271
## [0.9.3] - 2021-11-20
### Added
- getRedirectURL() will not log a warning for PHP 7.1+ #179
- it is now possible to disable upgrading from HTTP to HTTPS for development purposes by calling `setHttpUpgradeInsecureRequests(false)` #241
- bugfix in getSessionKey when _SESSION key does not exist #251
- Added scope parameter to refresh token request #225
- bugfix in `verifyJWTclaims` when $accessToken is empty and $claims->at_hash is not #276
- bugfix with the `empty` function in PHP 5.4 #267
## [0.9.2] - 2020-11-16
### Added
- Support for [PKCE](https://tools.ietf.org/html/rfc7636). Currently, the supported methods are 'plain' and 'S256'.
## [0.9.1] - 2020-08-27
### Added
- Add support for MS Azure Active Directory B2C user flows
### Changed
- Fix at_hash verification #200
- Getters for public parameters #204
- Removed client ID query parameter when making a token request using Basic Auth
- Use of `random_bytes()` for token generation instead of `uniqid()`; polyfill for PHP < 7.0 provided.
### Removed
- Removed explicit content-length header - caused issues with proxy servers
## [0.9.0] - 2020-03-09
### Added
- php 7.4 deprecates array_key_exists on objects, use property_exists in getVerifiedClaims and requestUserInfo
- Adding a header to indicate JSON as the return type for userinfo endpoint #151
- ~Updated OpenIDConnectClient to conditionally verify nonce #146~
- Add possibility to change enc_type parameter for http_build_query #155
- Adding OAuth 2.0 Token Introspection #156
- Add optional parameters clientId/clientSecret for introspection #157 & #158
- Adding OAuth 2.0 Token Revocation #160
- Adding issuer validator #145
- Adding signing algorithm PS256 #180
- Check http status of request user info #186
- URL encode clientId and clientSecret when using basic authentication, according to https://tools.ietf.org/html/rfc6749#section-2.3.1 #192
- Adjust PHPDoc to state that null is also allowed #193
### Changed
- Bugfix/code cleanup #152
- Cleanup PHPDoc #46e5b59
- Replace unnecessary double quotes with single quotes #2a76b57
- Use original function names instead of aliases #1f37892
- Remove unnecessary default values #5ab801e
- Explicit declare field $redirectURL #9187c0b
- Remove unused code #1e65384
- Fix indent #e9cdf56
- Cleanup conditional code flow for better readability #107f3fb
- Added strict type comparisons #167
- Bugfix: required `openid` scope was omitted when additional scopes were registered using `addScope` method. This resulted in failing OpenID process.
## [0.8.0] - 2019-01-02
### Added
- Fix `verifyJWTsignature()`: verify JWT to prevent php errors and warnings on invalid token
### Changed
- Decouple session manipulation, it's allow use of other session libraries #134
- Broaden version requirements of the phpseclib/phpseclib package. #144
## [0.7.0] - 2018-10-15
### Added
- Add "license" field to composer.json #138
- Ensure key_alg is set when getting key #139
- Add option to send additional registration parameters like post_logout_redirect_uris. #140
### Changed
- disabled autoload for Crypt_RSA + make refreshToken() method tolerant for errors #137
## [0.6.0] - 2018-07-17
### Added
- Added five minutes leeway due to clock skew between openidconnect server and client.
- Fix save access_token from request in implicit flow authentication #129
- `verifyJWTsignature()` method private -> public #126
- Support for providers where provider/login URL is not the same as the issuer URL. #125
- Support for providers that has a different login URL from the issuer URL, for instance Azure Active Directory. Here, the provider URL is on the format: https://login.windows.net/(tenant-id), while the issuer claim actually is on the format: https://sts.windows.net/(tenant-id).
### Changed
- refreshToken method update #124
## [0.5.0] - 2018-04-09
### Added
- Implement Azure AD B2C Implicit Workflow
## [0.4.1] - 2018-02-16
### Changed
- Documentation updates for include path.
## [0.4.0] - 2018-02-15
### Added
- Timeout is configurable via setTimeout method. This addresses issue #94.
- Add the ability to authenticate using the Resource Owner flow (with or without the Client ID and ClientSecret). This addresses issue #98
- Add support for HS256, HS512 and HS384 signatures
- Removed unused calls to $this->getProviderConfigValue("token_endpoint_…

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,238 @@
PHP OpenID Connect Basic Client
========================
A simple library that allows an application to authenticate a user through the basic OpenID Connect flow.
This library hopes to encourage OpenID Connect use by making it simple enough for a developer with little knowledge of
the OpenID Connect protocol to set up authentication.
A special thanks goes to Justin Richer and Amanda Anganes for their help and support of the protocol.
# Requirements #
1. PHP 5.4 or greater
2. CURL extension
3. JSON extension
## Install ##
1. Install library using composer
```
composer require jumbojett/openid-connect-php
```
2. Include composer autoloader
```php
require __DIR__ . '/vendor/autoload.php';
```
## Example 1: Basic Client ##
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
'ClientSecretHere');
$oidc->setCertPath('/path/to/my.cert');
$oidc->authenticate();
$name = $oidc->requestUserInfo('given_name');
```
[See openid spec for available user attributes][1]
## Example 2: Dynamic Registration ##
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient("https://id.provider.com");
$oidc->register();
$client_id = $oidc->getClientID();
$client_secret = $oidc->getClientSecret();
// Be sure to add logic to store the client id and client secret
```
## Example 3: Network and Security ##
```php
// Configure a proxy
$oidc->setHttpProxy("http://my.proxy.com:80/");
// Configure a cert
$oidc->setCertPath("/path/to/my.cert");
```
## Example 4: Request Client Credentials Token ##
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
'ClientSecretHere');
$oidc->providerConfigParam(array('token_endpoint'=>'https://id.provider.com/connect/token'));
$oidc->addScope('my_scope');
// this assumes success (to validate check if the access_token property is there and a valid JWT) :
$clientCredentialsToken = $oidc->requestClientCredentialsToken()->access_token;
```
## Example 5: Request Resource Owners Token (with client auth) ##
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
'ClientSecretHere');
$oidc->providerConfigParam(array('token_endpoint'=>'https://id.provider.com/connect/token'));
$oidc->addScope('my_scope');
//Add username and password
$oidc->addAuthParam(array('username'=>'<Username>'));
$oidc->addAuthParam(array('password'=>'<Password>'));
//Perform the auth and return the token (to validate check if the access_token property is there and a valid JWT) :
$token = $oidc->requestResourceOwnerToken(TRUE)->access_token;
```
## Example 6: Basic client for implicit flow e.g. with Azure AD B2C (see http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth) ##
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
'ClientSecretHere');
$oidc->setResponseTypes(array('id_token'));
$oidc->addScope(array('openid'));
$oidc->setAllowImplicitFlow(true);
$oidc->addAuthParam(array('response_mode' => 'form_post'));
$oidc->setCertPath('/path/to/my.cert');
$oidc->authenticate();
$sub = $oidc->getVerifiedClaims('sub');
```
## Example 7: Introspection of an access token (see https://tools.ietf.org/html/rfc7662) ##
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
'ClientSecretHere');
$data = $oidc->introspectToken('an.access-token.as.given');
if (!$data->active) {
// the token is no longer usable
}
```
## Example 8: PKCE Client ##
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
null);
$oidc->setCodeChallengeMethod('S256');
$oidc->authenticate();
$name = $oidc->requestUserInfo('given_name');
```
## Example 9: Back-channel logout ##
Back-channel authentication assumes you can end a session on the server side on behalf of the user (without relying
on their browser). The request is a POST from the OP direct to your RP. In this way, the use of this library can
ensure your RP performs 'single sign out' for the user even if they didn't have your RP open in a browser or other
device, but still had an active session there.
Either the sid or the sub may be accessible from the logout token sent from the OP. You can use either
`getSidFromBackChannel()` or `getSubjectFromBackChannel()` to retrieve them if it is helpful to match them to a session
in order to destroy it.
The below ensures the use of this library to ensure validation of the back-channel logout token, but is afterward
just a hypothetical way of finding such a session and destroying it. Adjust it to the needs of your RP.
```php
function handleLogout() {
// NOTE: assumes that $this->oidc is an instance of OpenIDConnectClient()
if ($this->oidc->verifyLogoutToken()) {
$sid = $this->oidc->getSidFromBackChannel();
if (isset($sid)) {
// Somehow find the session based on the $sid and
// destroy it. This depends on your RP's design,
// there is nothing in the OIDC spec to mandate how.
//
// In this example, we find a Redis key, which was
// previously stored using the sid we obtained from
// the access token after login.
//
// The value of the Redis key is that of the user's
// session ID specific to this hypothetical RP app.
//
// We then switch to that session and destroy it.
$this->redis->connect('127.0.0.1', 6379);
$session_id_to_destroy = $this->redis->get($sid);
if ($session_id_to_destroy) {
session_commit();
session_id($session_id_to_destroy); // switches to that session
session_start();
$_SESSION = array(); // effectively ends the session
}
}
}
}
```
## Example 10: Enable Token Endpoint Auth Methods ##
By default, only `client_secret_basic` is enabled on client side which was the only supported for a long time.
Recently `client_secret_jwt` and `private_key_jwt` have been added, but they remain disabled until explicitly enabled.
```php
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient('https://id.provider.com',
'ClientIDHere',
null);
# enable 'client_secret_basic' and 'client_secret_jwt'
$oidc->setTokenEndpointAuthMethodsSupported(['client_secret_basic', 'client_secret_jwt']);
# for 'private_key_jwt' in addition also the generator function has to be set.
$oidc->setTokenEndpointAuthMethodsSupported(['private_key_jwt']);
$oidc->setPrivateKeyJwtGenerator(function(string $token_endpoint) {
# TODO: what ever is necessary
})
```
## Development Environments ##
In some cases you may need to disable SSL security on your development systems.
Note: This is not recommended on production systems.
```php
$oidc->setVerifyHost(false);
$oidc->setVerifyPeer(false);
```
Also, your local system might not support HTTPS, so you might disable upgrading to it:
```php
$oidc->setHttpUpgradeInsecureRequests(false);
```
### Todo ###
- Dynamic registration does not support registration auth tokens and endpoints
[1]: http://openid.net/specs/openid-connect-basic-1_0-15.html#id_res
## Contributing ###
- All pull requests, once merged, should be added to the CHANGELOG.md file.

View File

@@ -0,0 +1,56 @@
<?php
/**
*
* Copyright MITRE 2012
*
* OpenIDConnectClient for PHP5
* Author: Michael Jett <mjett@mitre.org>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
require __DIR__ . '/vendor/autoload.php';
use Jumbojett\OpenIDConnectClient;
$oidc = new OpenIDConnectClient(
'http://myproviderURL.com/',
'ClientIDHere',
'ClientSecretHere'
);
$oidc->authenticate();
$name = $oidc->requestUserInfo('given_name');
?>
<html>
<head>
<title>Example OpenID Connect Client Use</title>
<style>
body {
font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<div>
Hello <?php echo $name; ?>
</div>
</body>
</html>

View File

@@ -0,0 +1,23 @@
{
"name": "jumbojett/openid-connect-php",
"description": "Bare-bones OpenID Connect client",
"license": "Apache-2.0",
"require": {
"php": ">=7.0",
"ext-json": "*",
"ext-curl": "*",
"phpseclib/phpseclib": "~3.0"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
"yoast/phpunit-polyfills": "^1.0"
},
"archive" : {
"exclude" : [
".*"
]
},
"autoload" : {
"classmap": [ "src/"]
}
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
bootstrap="./vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
verbose="true"
stopOnFailure="false"
processIsolation="false"
backupGlobals="false"
syntaxCheck="true"
>
<testsuites>
<testsuite name="Tests">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
<exclude>
<directory>./vendor</directory>
<directory>./tests</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
<?php
use Jumbojett\OpenIDConnectClient;
use Jumbojett\OpenIDConnectClientException;
use PHPUnit\Framework\MockObject\MockObject;
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
class OpenIDConnectClientTest extends TestCase
{
/**
* @return void
*/
public function testGetRedirectURL()
{
$client = new OpenIDConnectClient();
self::assertSame('http:///', $client->getRedirectURL());
$_SERVER['SERVER_NAME'] = 'domain.test';
$_SERVER['REQUEST_URI'] = '/path/index.php?foo=bar&baz#fragment';
self::assertSame('http://domain.test/path/index.php', $client->getRedirectURL());
}
public function testAuthenticateDoesNotThrowExceptionIfClaimsIsMissingNonce()
{
$fakeClaims = new StdClass();
$fakeClaims->iss = 'fake-issuer';
$fakeClaims->aud = 'fake-client-id';
$fakeClaims->nonce = null;
$_REQUEST['id_token'] = 'abc.123.xyz';
$_REQUEST['state'] = false;
$_SESSION['openid_connect_state'] = false;
/** @var OpenIDConnectClient | MockObject $client */
$client = $this->getMockBuilder(OpenIDConnectClient::class)->setMethods(['decodeJWT', 'getProviderConfigValue', 'verifyJWTSignature'])->getMock();
$client->method('decodeJWT')->willReturn($fakeClaims);
$client->method('getProviderConfigValue')->with('jwks_uri')->willReturn(true);
$client->method('verifyJWTSignature')->willReturn(true);
$client->setClientID('fake-client-id');
$client->setIssuer('fake-issuer');
$client->setIssuerValidator(function() {
return true;
});
$client->setAllowImplicitFlow(true);
$client->setProviderURL('https://jwt.io/');
try {
$authenticated = $client->authenticate();
$this->assertTrue($authenticated);
} catch ( OpenIDConnectClientException $e ) {
if ( $e->getMessage() === 'Unable to verify JWT claims' ) {
self::fail( 'OpenIDConnectClientException was thrown when it should not have been.' );
}
}
}
public function testSerialize()
{
$client = new OpenIDConnectClient('https://example.com', 'foo', 'bar', 'baz');
$serialized = serialize($client);
$this->assertInstanceOf(OpenIDConnectClient::class, unserialize($serialized));
}
/**
* @dataProvider provider
*/
public function testAuthMethodSupport($expected, $authMethod, $clientAuthMethods, $idpAuthMethods)
{
$client = new OpenIDConnectClient();
if ($clientAuthMethods !== null) {
$client->setTokenEndpointAuthMethodsSupported($clientAuthMethods);
}
$this->assertEquals($expected, $client->supportsAuthMethod($authMethod, $idpAuthMethods));
}
public function provider(): array
{
return [
'client_secret_basic - default config' => [true, 'client_secret_basic', null, ['client_secret_basic']],
'client_secret_jwt - default config' => [false, 'client_secret_jwt', null, ['client_secret_basic', 'client_secret_jwt']],
'client_secret_jwt - explicitly enabled' => [true, 'client_secret_jwt', ['client_secret_jwt'], ['client_secret_basic', 'client_secret_jwt']],
'private_key_jwt - default config' => [false, 'private_key_jwt', null, ['client_secret_basic', 'client_secret_jwt', 'private_key_jwt']],
'private_key_jwt - explicitly enabled' => [true, 'private_key_jwt', ['private_key_jwt'], ['client_secret_basic', 'client_secret_jwt', 'private_key_jwt']],
];
}
/**
* @covers Jumbojett\\OpenIDConnectClient::verifyLogoutTokenClaims
* @dataProvider provideTestVerifyLogoutTokenClaimsData
* @throws OpenIDConnectClientException
*/
public function testVerifyLogoutTokenClaims( $claims, $expectedResult )
{
/** @var OpenIDConnectClient | MockObject $client */
$client = $this->getMockBuilder(OpenIDConnectClient::class)->setMethods(['decodeJWT'])->getMock();
$client->setClientID('fake-client-id');
$client->setIssuer('fake-issuer');
$client->setIssuerValidator(function() {
return true;
});
$client->setProviderURL('https://jwt.io/');
$actualResult = $client->verifyLogoutTokenClaims( $claims );
$this->assertEquals( $expectedResult, $actualResult );
}
/**
* @return array
*/
public function provideTestVerifyLogoutTokenClaimsData(): array
{
return [
'valid-single-aud' => [
(object)[
'iss' => 'fake-issuer',
'aud' => 'fake-client-id',
'sid' => 'fake-client-sid',
'sub' => 'fake-client-sub',
'iat' => time(),
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
],
],
true
],
'valid-multiple-auds' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sid' => 'fake-client-sid',
'sub' => 'fake-client-sub',
'iat' => time(),
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
],
],
true
],
'invalid-no-sid-and-no-sub' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'iat' => time(),
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
],
],
false
],
'valid-no-sid' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sub' => 'fake-client-sub',
'iat' => time(),
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
],
],
true
],
'valid-no-sub' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sid' => 'fake-client-sid',
'iat' => time(),
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
],
],
true
],
'invalid-with-nonce' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sid' => 'fake-client-sid',
'iat' => time(),
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
],
'nonce' => 'must-not-be-set'
],
false
],
'invalid-no-events' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sid' => 'fake-client-sid',
'iat' => time(),
'nonce' => 'must-not-be-set'
],
false
],
'invalid-no-backchannel-event' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sid' => 'fake-client-sid',
'iat' => time(),
'events' => (object) [],
'nonce' => 'must-not-be-set'
],
false
],
'invalid-no-iat' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sid' => 'fake-client-sid',
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
]
],
false
],
'invalid-bad-iat' => [
(object)[
'iss' => 'fake-issuer',
'aud' => [ 'fake-client-id', 'some-other-aud' ],
'sid' => 'fake-client-sid',
'iat' => time() + 301,
'events' => (object) [
'http://schemas.openid.net/event/backchannel-logout' => (object)[]
]
],
false
],
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Jumbojett\OpenIDConnectClient;
use Jumbojett\OpenIDConnectClientException;
use PHPUnit\Framework\MockObject\MockObject;
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
class TokenVerificationTest extends TestCase
{
/**
* @param $alg
* @param $jwt
* @throws OpenIDConnectClientException
* @dataProvider providesTokens
*/
public function testTokenVerification($alg, $jwt)
{
/** @var OpenIDConnectClient | MockObject $client */
$client = $this->getMockBuilder(OpenIDConnectClient::class)->setMethods(['fetchUrl'])->getMock();
$client->method('fetchUrl')->willReturn(file_get_contents(__DIR__ . "/data/jwks-$alg.json"));
$client->setProviderURL('https://jwt.io/');
$client->providerConfigParam(['jwks_uri' => 'https://jwt.io/.well-known/jwks.json']);
$verified = $client->verifyJWTSignature($jwt);
self::assertTrue($verified);
$client->setAccessToken($jwt);
}
public function providesTokens(): array
{
return [
'PS256' => ['ps256', 'eyJhbGciOiJQUzI1NiIsImtpZCI6Imtvbm5lY3RkLXRva2Vucy1zaWduaW5nLWtleSIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJrcG9wLWh0dHBzOi8va29wYW5vLmRlbW8vbWVldC8iLCJleHAiOjE1NjgzNzE0NjEsImp0aSI6IkpkR0tDbEdOTXl2VXJpcmlRRUlWUXZCVmttT2FfQkRjIiwiaWF0IjoxNTY4MzcxMjIxLCJpc3MiOiJodHRwczovL2tvcGFuby5kZW1vIiwic3ViIjoiUHpUVWp3NHBlXzctWE5rWlBILXJxVHE0MTQ1Z3lDdlRvQmk4V1E5bFBrcW5rbEc1aktvRU5LM21Qb0I1WGY1ZTM5dFRMR2RKWXBMNEJubXFnelpaX0FAa29ubmVjdCIsImtjLmlzQWNjZXNzVG9rZW4iOnRydWUsImtjLmF1dGhvcml6ZWRTY29wZXMiOlsicHJvZmlsZSIsImVtYWlsIiwia29wYW5vL2t3bSIsImtvcGFuby9nYyIsImtvcGFuby9rdnMiLCJvcGVuaWQiXSwia2MuYXV0aG9yaXplZENsYWltcyI6eyJpZF90b2tlbiI6eyJuYW1lIjpudWxsfX0sImtjLmlkZW50aXR5Ijp7ImtjLmkuZG4iOiJKb25hcyBCcmVra2UiLCJrYy5pLmlkIjoiQUFBQUFLd2hxVkJBMCs1SXN4bjdwMU13UkNVQkFBQUFCZ0FBQUJzQUFBQk5VVDA5QUFBQUFBPT0iLCJrYy5pLnVuIjoidXNlcjEiLCJrYy5pLnVzIjoiTVEifSwia2MucHJvdmlkZXIiOiJpZGVudGlmaWVyLWtjIn0.hGRuXvul2kOiALHexwYp5MBEJVwz1YV3ehyM3AOuwCoK2w5sJxdciqqY_TfXCKyO6nAEbYLK3J0CBOjfup_IG0aCZcwzjto8khYlc4ezXkGnFsbJBNQdDGkpHtWnioWx-OJ3cXvY9F8aOvjaq0gw11ZDAcqQl0g7LTbJ9-J_yx0pmy3NGai2JB30Fh1OgSDzYfxWnE0RRgZG-x68e65RXfSBaEGW85OUh4wihxO2zdTGAHJ3Iq_-QAG4yRbXZtLx3ZspG7LNmqG-YE3huy3Rd8u3xrJNhmUOfEnz3x07q7VW0cj9NedX98BAbj3iNvksQsE0oG0J_f_Tu8Ai8VbWB72sJuXZWxANDKdz0BBYLzXhsjXkNByRq9x3zqDVsX-cVHei_XudxEOVRBjhkvW2MmIjcAHNKCKsdar865-gFG9McP4PCcBlY28tC0Cvnzyi83LBfpGRXdl6MJunnUsKQ1C79iCoVI1doK1erFN959Q-TGJfJA3Tr5LNpuGawB5rpe1nDGWvmYhg3uYfNl8uTTyvNgvvejcflEb2DURuXdqABuSiP7RkDWYtzx6mq49G0tRxelBbvyjQ2id2QjmRRdQ6dHEZ2NCJ51b8OFoDJBtxN1CD62TTxa3FUqCdZAPAUR3hHn_69vYq82MR514s-Gb67A6j2PbMPFATQP2UdK8']
];
}
}

View File

@@ -0,0 +1,12 @@
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "konnectd-tokens-signing-key",
"n": "10hb3pFUVcqJcS-d1pLCkFTyTqVD1GavlAai582CoRwFcyIQxCPJz0LJVgkUNwxSRkY0g0PcgFN_MmuuzpFXMkkiMIC9O_KwnuL34FrbijZvcGpnDn7kb9KAM883OVTr_w3wFeQIyh0ksSwVQ9CxVQ-ZeCXP73CCGk99uDb8SeF8_vncXJmaak99pK6HKJteSLkA-Ywxo9HOINZK2vW06UYcSkeoQnSI27Cd5-T6GVgqKH0Su4c5Ydou_w0tL_UkbZA4fIbMZC6dtWmBQf6tyYsCM9fbWNIVOj_7WlWcAOSTFNF2We2dxJrOzt6vDND3k1nCgg_EEM6cgBO3swUCktTFuQxo1sryYX5WXz9wnJb38b9mTXhOeF0bd9y_VQq8erSlcyRu8UGzX65tIf534hLL16KQaHbjROGSQvzqFrISmSBjBTjkPedTZSYOhiVJ95-em_Y6uLi-T7V4bs4dcg3oa0H_glXltoC9JxzS6gfMGGLgh-NpGEOdC_QosyzVVfzT70TurOGnsB1_VcAm_fK-T1Zv_ztpr5OZNfXWXC3Pfq_3sxP5HDKMk8luZ7LOWk7HVSYBdCFmOM1A3KmHNS2fEs-QHIr-XjYQ7QrXsRFP3dmoEPfiYlu03m8Xs3UMB70eGeGQx7OhZSuogxV_oCfApV5EJfuz97tVmOg8iMs",
"e": "AQAB"
}
],
"kty": ""
}