diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f130243..db27e85d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,191 +1,178 @@ # Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). - -## [0.9.11] - -### Added +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). +## [unreleased] +* Update visibility of getWellKnownConfigValue to protected. #363 +* Fixed issue on authentication for php8. #354 * Enabled `client_secret_basic` authentication on `requestClientCredentialsToken()` #347 +* Support for signed and encrypted UserInfo response. #305 +* Support for signed and encrypted ID Token. #305 +* Update construct typehint in docblock. #364 +* Fixed LogoutToken verification for single value aud claims #334 -## [0.9.10] - -## Fixed +### Added +- Support for signed and encrypted UserInfo response. #305 +- Support for signed and encrypted ID Token. #305 -* `private_key_jwt` and `client_secret_jwt` need to explicitly be enabled #331 +## [0.9.10] - 2022-09-30 +### Fixed +- `private_key_jwt` and `client_secret_jwt` need to explicitly be enabled #331 -## [0.9.9] +## [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 -* 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] +### Fixed +- Harden self-signed JWK header usage. #323 -## Fixed +## [0.9.8] - 2022-08-05 -* Do not use PKCE if IdP does not support it. #317 +### Fixed +- Do not use PKCE if IdP does not support it. #317 -## [0.9.7] +## [0.9.7] - 2022-07-13 ### Added - -* Support for Self-Contained JWTs. #308 -* Support for RFC8693 Token Exchange Request. #275 +- Support for Self-Contained JWTs. #308 +- Support for RFC8693 Token Exchange Request. #275 ### Fixed +- PHP 5.4 compatibility. #304 +- Use session_status(). #306 -* PHP 5.4 compatibility. #304 -* Use session_status(). #306 - -## [0.9.6] +## [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 +- 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 -* Allow serializing `OpenIDConnectClient` using `serialize()` #295 - -## [0.9.5] +## [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 -* 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] +## [0.9.4] - 2021-11-21 ### Added +- Enabled `client_secret_basic` authentication on `refreshToken()` #215 +- Basic auth support for requestResourceOwnerToken #271 -* Enabled `client_secret_basic` authentication on `refreshToken()` #215 -* Basic auth support for requestResourceOwnerToken #271 - -## [0.9.3] +## [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 -* 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] +## [0.9.2] - 2020-11-16 ### Added -* Support for [PKCE](https://tools.ietf.org/html/rfc7636). Currently, the supported methods are 'plain' and 'S256'. +- Support for [PKCE](https://tools.ietf.org/html/rfc7636). Currently, the supported methods are 'plain' and 'S256'. -## [0.9.1] +## [0.9.1] - 2020-08-27 ### Added -* Add support for MS Azure Active Directory B2C user flows +- 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. +- 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 +- Removed explicit content-length header - caused issues with proxy servers - -## [0.9.0] +## [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 +- 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] +- 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 +- 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 +- Decouple session manipulation, it's allow use of other session libraries #134 +- Broaden version requirements of the phpseclib/phpseclib package. #144 -## [0.7.0] +## [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 +- 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 - -### Removed -* +- disabled autoload for Crypt_RSA + make refreshToken() method tolerant for errors #137 -## [0.6.0] +## [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). +- 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 +- refreshToken method update #124 -### Removed -* - -## [0.5.0] -## Added -* Implement Azure AD B2C Implicit Workflow +## [0.5.0] - 2018-04-09 -## [0.4.1] -## Changed -* Documentation updates for include path. - -## [0.4] ### 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_… +- Implement Azure AD B2C Implicit Workflow + +## [0.4.1] - 2018-02-16 ### Changed +- Documentation updates for include path. -### Removed +## [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_… diff --git a/src/OpenIDConnectClient.php b/src/OpenIDConnectClient.php index 616fc9ac..d34e51a7 100644 --- a/src/OpenIDConnectClient.php +++ b/src/OpenIDConnectClient.php @@ -145,7 +145,7 @@ class OpenIDConnectClient protected $idToken; /** - * @var string stores the token response + * @var object stores the token response */ private $tokenResponse; @@ -159,6 +159,11 @@ class OpenIDConnectClient */ private $responseCode; + /** + * @var string|null Content type from the server + */ + private $responseContentType; + /** * @var array holds response types */ @@ -267,11 +272,10 @@ class OpenIDConnectClient private $token_endpoint_auth_methods_supported = ['client_secret_basic']; /** - * @param $provider_url string optional - * - * @param $client_id string optional - * @param $client_secret string optional - * @param null $issuer + * @param string|null $provider_url optional + * @param string|null $client_id optional + * @param string|null $client_secret optional + * @param string|null $issuer */ public function __construct($provider_url = null, $client_id = null, $client_secret = null, $issuer = null) { $this->setProviderURL($provider_url); @@ -333,7 +337,7 @@ public function authenticate() { } // Do an OpenID Connect session check - if ($_REQUEST['state'] !== $this->getState()) { + if (!isset($_REQUEST['state']) || ($_REQUEST['state'] !== $this->getState())) { throw new OpenIDConnectClientException('Unable to determine state'); } @@ -344,22 +348,20 @@ public function authenticate() { throw new OpenIDConnectClientException('User did not authorize openid scope.'); } - $claims = $this->decodeJWT($token_json->id_token, 1); + $id_token = $token_json->id_token; + $idTokenHeaders = $this->decodeJWT($id_token); + if (isset($idTokenHeaders->enc)) { + // Handle JWE + $id_token = $this->handleJweResponse($id_token); + } + + $claims = $this->decodeJWT($id_token, 1); // Verify the signature - if ($this->canVerifySignatures()) { - if (!$this->getProviderConfigValue('jwks_uri')) { - throw new OpenIDConnectClientException ('Unable to verify signature due to no jwks_uri being defined'); - } - if (!$this->verifyJWTsignature($token_json->id_token)) { - throw new OpenIDConnectClientException ('Unable to verify signature'); - } - } else { - user_error('Warning: JWT signature verification unavailable.'); - } + $this->verifySignatures($id_token); // Save the id token - $this->idToken = $token_json->id_token; + $this->idToken = $id_token; // Save the access token $this->accessToken = $token_json->access_token; @@ -398,7 +400,7 @@ public function authenticate() { } // Do an OpenID Connect session check - if ($_REQUEST['state'] !== $this->getState()) { + if (!isset($_REQUEST['state']) || ($_REQUEST['state'] !== $this->getState())) { throw new OpenIDConnectClientException('Unable to determine state'); } @@ -408,16 +410,7 @@ public function authenticate() { $claims = $this->decodeJWT($id_token, 1); // Verify the signature - if ($this->canVerifySignatures()) { - if (!$this->getProviderConfigValue('jwks_uri')) { - throw new OpenIDConnectClientException ('Unable to verify signature due to no jwks_uri being defined'); - } - if (!$this->verifyJWTsignature($id_token)) { - throw new OpenIDConnectClientException ('Unable to verify signature'); - } - } else { - user_error('Warning: JWT signature verification unavailable.'); - } + $this->verifySignatures($id_token); // Save the id token $this->idToken = $id_token; @@ -570,7 +563,9 @@ public function verifyLogoutTokenClaims($claims) return false; } // Validate the aud - if ((!$claims->aud === $this->clientID) || (!in_array($this->clientID, $claims->aud, true))) { + $auds = $claims->aud; + $auds = is_array( $auds ) ? $auds : [ $auds ]; + if (!in_array($this->clientID, $auds, true)) { return false; } // Validate the iat. At this point we can return true if it is ok @@ -646,7 +641,7 @@ protected function getProviderConfigValue($param, $default = null) { * @return string * */ - private function getWellKnownConfigValue($param, $default = null) { + protected function getWellKnownConfigValue($param, $default = null) { // If the configuration value is not available, attempt to fetch it from a well known config endpoint // This is also known as auto "discovery" @@ -938,7 +933,7 @@ protected function requestTokens($code, $headers = array()) { if ($this->supportsAuthMethod('client_secret_basic', $token_endpoint_auth_methods_supported)) { $authorizationHeader = 'Authorization: Basic ' . base64_encode(urlencode($this->clientID) . ':' . urlencode($this->clientSecret)); unset($token_params['client_secret']); - unset($token_params['client_id']); + unset($token_params['client_id']); } // When there is a private key jwt generator and it is supported then use it as client authentication @@ -956,7 +951,7 @@ protected function requestTokens($code, $headers = array()) { else{ $client_assertion = $this->getJWTClientAssertion($this->getProviderConfigValue('token_endpoint')); } - + $token_params['client_assertion_type'] = $client_assertion_type; $token_params['client_assertion'] = $client_assertion; unset($token_params['client_secret']); @@ -1064,7 +1059,7 @@ public function refreshToken($refresh_token) { if ($this->supportsAuthMethod('client_secret_jwt', $token_endpoint_auth_methods_supported)) { $client_assertion_type = $this->getProviderConfigValue('client_assertion_type'); $client_assertion = $this->getJWTClientAssertion($this->getProviderConfigValue('token_endpoint')); - + $token_params["grant_type"] = "urn:ietf:params:oauth:grant-type:token-exchange"; $token_params["subject_token"] = $refresh_token; $token_params["audience"] = $this->clientID; @@ -1072,7 +1067,7 @@ public function refreshToken($refresh_token) { $token_params["requested_token_type"] = "urn:ietf:params:oauth:token-type:access_token"; $token_params['client_assertion_type']=$client_assertion_type; $token_params['client_assertion'] = $client_assertion; - + unset($token_params['client_secret']); unset($token_params['client_id']); } @@ -1188,7 +1183,7 @@ private function verifyRSAJWTsignature($hashtype, $key, $payload, $signature, $s /** * @param string $hashtype - * @param object $key + * @param string $key * @param $payload * @param $signature * @return bool @@ -1247,7 +1242,7 @@ public function verifyJWTsignature($jwt) { $jwk = $header->jwk; $this->verifyJWKHeader($jwk); } else { - $jwks = json_decode($this->fetchURL($this->getProviderConfigValue('jwks_uri'))); + $jwks = json_decode($this->fetchURL($this->getProviderConfigValue('jwks_uri')), false); if ($jwks === NULL) { throw new OpenIDConnectClientException('Error decoding JSON from jwks_uri'); } @@ -1270,6 +1265,25 @@ public function verifyJWTsignature($jwt) { return $verified; } + /** + * @param string $jwt encoded JWT + * @return void + * @throws OpenIDConnectClientException + */ + public function verifySignatures($jwt) + { + if ($this->canVerifySignatures()) { + if (!$this->getProviderConfigValue('jwks_uri')) { + throw new OpenIDConnectClientException ('Unable to verify signature due to no jwks_uri being defined'); + } + if (!$this->verifyJWTsignature($jwt)) { + throw new OpenIDConnectClientException ('Unable to verify signature'); + } + } else { + user_error('Warning: JWT signature verification unavailable.'); + } + } + /** * @param string $iss * @return bool @@ -1370,10 +1384,39 @@ public function requestUserInfo($attribute = null) { $headers = ["Authorization: Bearer {$this->accessToken}", 'Accept: application/json']; - $user_json = json_decode($this->fetchURL($user_info_endpoint,null,$headers)); + $response = $this->fetchURL($user_info_endpoint,null,$headers); if ($this->getResponseCode() <> 200) { throw new OpenIDConnectClientException('The communication to retrieve user data has failed with status code '.$this->getResponseCode()); } + + // When we receive application/jwt, the UserInfo Response is signed and/or encrypted. + if ($this->getResponseContentType() === 'application/jwt' ) { + // Check if the response is encrypted + $jwtHeaders = $this->decodeJWT($response); + if (isset($jwtHeaders->enc)) { + // Handle JWE + $jwt = $this->handleJweResponse($response); + } else { + // If the response is not encrypted then it must be signed + $jwt = $response; + } + + // Verify the signature + $this->verifySignatures($jwt); + + // Get claims from JWT + $claims = $this->decodeJWT($jwt, 1); + + // Verify the JWT claims + if (!$this->verifyJWTclaims($claims)) { + throw new OpenIDConnectClientException('Invalid JWT signature'); + } + + $user_json = $claims; + } else { + $user_json = json_decode($response); + } + $this->userInfo = $user_json; if($attribute === null) { @@ -1501,6 +1544,7 @@ protected function fetchURL($url, $post_body = null, $headers = []) { // HTTP Response code from server may be required from subclass $info = curl_getinfo($ch); $this->responseCode = $info['http_code']; + $this->responseContentType = $info['content_type']; if ($output === false) { throw new OpenIDConnectClientException('Curl error: (' . curl_errno($ch) . ') ' . curl_error($ch)); @@ -1755,7 +1799,7 @@ public function introspectToken($token, $token_type_hint = '', $clientId = null, if ($this->supportsAuthMethod('client_secret_jwt', $token_endpoint_auth_methods_supported)) { $client_assertion_type = $this->getProviderConfigValue('client_assertion_type'); $client_assertion = $this->getJWTClientAssertion($this->getProviderConfigValue('introspection_endpoint')); - + $post_data['client_assertion_type']=$client_assertion_type; $post_data['client_assertion'] = $client_assertion; $headers = ['Accept: application/json']; @@ -1893,7 +1937,7 @@ public function getIdTokenPayload() { } /** - * @return string + * @return object */ public function getTokenResponse() { return $this->tokenResponse; @@ -1995,6 +2039,16 @@ public function getResponseCode() { return $this->responseCode; } + /** + * Get the content type from last action/curl request. + * + * @return string|null + */ + public function getResponseContentType() + { + return $this->responseContentType; + } + /** * Set timeout (seconds) * @@ -2096,7 +2150,7 @@ protected function getJWTClientAssertion($aud) { ]); // Encode Header to Base64Url String $base64UrlHeader = $this->urlEncode($header); - + // Encode Payload to Base64Url String $base64UrlPayload = $this->urlEncode($payload); @@ -2111,12 +2165,12 @@ protected function getJWTClientAssertion($aud) { // Encode Signature to Base64Url String $base64UrlSignature = $this->urlEncode($signature); - + $jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; return $jwt; } - + public function setUrlEncoding($curEncoding) { switch ($curEncoding) { @@ -2199,6 +2253,16 @@ protected function verifyJWKHeader($jwk) throw new OpenIDConnectClientException('Self signed JWK header is not valid'); } + /** + * @param string $jwe The JWE to decrypt + * @return string the JWT payload + * @throws OpenIDConnectClientException + */ + protected function handleJweResponse($jwe) + { + throw new OpenIDConnectClientException('JWE response is not supported, please extend the class and implement this method'); + } + /* * @return string */ diff --git a/tests/OpenIDConnectClientTest.php b/tests/OpenIDConnectClientTest.php index dd3321bb..d82234ed 100644 --- a/tests/OpenIDConnectClientTest.php +++ b/tests/OpenIDConnectClientTest.php @@ -88,4 +88,151 @@ public function provider() ]; } + + /** + * @covers Jumbojett\\OpenIDConnectClient::verifyLogoutTokenClaims + * @dataProvider provideTestVerifyLogoutTokenClaimsData + */ + 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() { + 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 + ], + ]; + } }