From 6d95ac4c22b4a0ed3570d6231dc29a177d780a93 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Fri, 25 Nov 2022 00:15:01 +0100 Subject: [PATCH 01/39] ID Token response for OIDC implementation --- src/ClaimExtractor.php | 154 ++++++++++++++++++ src/Entities/ClaimSetEntry.php | 38 +++++ src/Entities/ClaimSetEntryInterface.php | 17 ++ src/IdTokenClaimsCreated.php | 33 ++++ .../ClaimSetRepositoryInterface.php | 17 ++ src/RequestEvent.php | 5 + src/ResponseTypes/IdTokenResponse.php | 76 +++++++++ 7 files changed, 340 insertions(+) create mode 100644 src/ClaimExtractor.php create mode 100644 src/Entities/ClaimSetEntry.php create mode 100644 src/Entities/ClaimSetEntryInterface.php create mode 100644 src/IdTokenClaimsCreated.php create mode 100644 src/Repositories/ClaimSetRepositoryInterface.php create mode 100644 src/ResponseTypes/IdTokenResponse.php diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php new file mode 100644 index 000000000..94ca660f0 --- /dev/null +++ b/src/ClaimExtractor.php @@ -0,0 +1,154 @@ + + * @author Marc Riemer + */ +class ClaimExtractor +{ + protected $claimSets = []; + + protected $protectedClaims = ['profile', 'email', 'address', 'phone']; + + /** + * ClaimExtractor constructor + * + * @param ClaimSet[] $claimSets + */ + public function __construct(array $claimSets = []) + { + // Add Default OpenID Connect Claims + // @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims + $this->addClaimSet( + new ClaimSetEntry('profile', [ + 'name', + 'family_name', + 'given_name', + 'middle_name', + 'nickname', + 'preferred_username', + 'profile', + 'picture', + 'website', + 'gender', + 'birthdate', + 'zoneinfo', + 'locale', + 'updated_at' + ]) + ); + $this->addClaimSet( + new ClaimSetEntry('email', [ + 'email', + 'email_verified' + ]) + ); + $this->addClaimSet( + new ClaimSetEntry('address', [ + 'address' + ]) + ); + $this->addClaimSet( + new ClaimSetEntry('phone', [ + 'phone_number', + 'phone_number_verified' + ]) + ); + + foreach ($claimSets as $claimSet) { + $this->addClaimSet($claimSet); + } + } + + /** + * @param ClaimSetInterface $claimSet + * @return $this + * @throws \InvalidArgumentException + */ + public function addClaimSet(ClaimSetEntryInterface $claimSet): ClaimExtractor + { + $scope = $claimSet->getScope(); + + if (in_array($scope, $this->protectedClaims) && !empty($this->claimSets[$scope])) { + throw new \InvalidArgumentException( + sprintf("%s is a protected scope and is pre-defined by the OpenID Connect specification.", $scope) + ); + } + + $this->claimSets[$scope] = $claimSet; + + return $this; + } + + /** + * @param string $scope + * @return ClaimSet|null + */ + public function getClaimSet(string$scope) + { + if (!$this->hasClaimSet($scope)) { + return null; + } + + return $this->claimSets[$scope]; + } + + /** + * @param string $scope + * @return bool + */ + public function hasClaimSet(string $scope): bool + { + return array_key_exists($scope, $this->claimSets); + } + + /** + * For given scopes and aggregated claims get all claims that have been configured on the extractor. + * + * @param array $scopes + * @param array $claims + * @return array + */ + public function extract(array $scopes, array $claims): array + { + $claimData = []; + $keys = array_keys($claims); + + foreach ($scopes as $scope) { + $scopeName = ($scope instanceof ScopeEntityInterface) ? $scope->getIdentifier() : $scope; + + $claimSet = $this->getClaimSet($scopeName); + if (null === $claimSet) { + continue; + } + + $intersected = array_intersect($claimSet->getClaims(), $keys); + + if (empty($intersected)) { + continue; + } + + $data = array_filter($claims, + function($key) use ($intersected) { + return in_array($key, $intersected); + }, + ARRAY_FILTER_USE_KEY + ); + + $claimData = array_merge($claimData, $data); + } + + return $claimData; + } +} diff --git a/src/Entities/ClaimSetEntry.php b/src/Entities/ClaimSetEntry.php new file mode 100644 index 000000000..85762a0e5 --- /dev/null +++ b/src/Entities/ClaimSetEntry.php @@ -0,0 +1,38 @@ + + * @author Marc Riemer + * @license http://opensource.org/licenses/MIT MIT + */ +class ClaimSetEntry implements ClaimSetEntryInterface +{ + public function __construct( + protected string $scope, + protected array $claims) + {} + + /** + * Get scope + * + * @return string + */ + public function getScope(): string + { + return $this->scope; + } + + /** + * Get claims + * + * @return ClaimSetInterface[] + */ + public function getClaims(): array + { + return $this->claims; + } +} diff --git a/src/Entities/ClaimSetEntryInterface.php b/src/Entities/ClaimSetEntryInterface.php new file mode 100644 index 000000000..c931165af --- /dev/null +++ b/src/Entities/ClaimSetEntryInterface.php @@ -0,0 +1,17 @@ + + * @author Marc Riemer + * @license http://opensource.org/licenses/MIT MIT + */ +interface ClaimSetEntryInterface +{ + public function getClaims(): array; + + public function getScope(): string; +} diff --git a/src/IdTokenClaimsCreated.php b/src/IdTokenClaimsCreated.php new file mode 100644 index 000000000..413dec554 --- /dev/null +++ b/src/IdTokenClaimsCreated.php @@ -0,0 +1,33 @@ + + */ +class IdTokenClaimsCreated extends RequestEvent +{ + /** + * Builder + * + * @var Builder + */ + private $builder; + + public function __construct($name, ServerRequestInterface $request, Builder $builder) + { + parent::__construct($name, $request); + $this->request = $request; + $this->builder = $builder; + } + + public function getBuilder(): Builder + { + return $this->builder; + } +} \ No newline at end of file diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php new file mode 100644 index 000000000..a475c7468 --- /dev/null +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -0,0 +1,17 @@ + + * @author Steve Rhoades + * @license http://opensource.org/licenses/MIT MIT + */ +interface ClaimSetRepositoryInterface +{ + public function getClaimSetByUserIdentifier($userIdentifyer): ClaimSetEntryInterface; +} \ No newline at end of file diff --git a/src/RequestEvent.php b/src/RequestEvent.php index b1ca3f6b8..6273f5c7a 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -21,6 +21,11 @@ class RequestEvent extends Event const REFRESH_TOKEN_ISSUED = 'refresh_token.issued'; const ACCESS_TOKEN_ISSUED = 'access_token.issued'; + /** + * This event can be used to extent claims of the id_token + */ + const ID_TOKEN_CLAIMS_CREATED = 'id_token_claims.created'; + /** * @var ServerRequestInterface */ diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php new file mode 100644 index 000000000..7ef9466f7 --- /dev/null +++ b/src/ResponseTypes/IdTokenResponse.php @@ -0,0 +1,76 @@ + + * @author Marc Riemer + */ +class IdTokenResponse extends BearerTokenResponse +{ + use EmitterAwareTrait; + + public function __construct( + protected ServerRequestInterface $request, + protected ClaimSetRepositoryInterface $claimRepository, + protected ClaimExtractor $extractor) + {} + + /** + * @param AccessTokenEntityInterface $accessToken + * @return array + */ + protected function getExtraParams(AccessTokenEntityInterface $accessToken): array + { + // Onyly add id_token to openid requests + if (!in_array("openid", $accessToken->getScopes())) { + return []; + } + + $claimSet = $this->claimRepository->getClaimSetByUserIdentifier($accessToken->getUserIdentifier()); + $builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates())) + ->permittedFor($accessToken->getClient()->getIdentifier()) + ->issuedBy(sprintf("%s://%s", + $this->request->getUri()->getScheme(), $this->request->getUri()->getHost())) + ->issuedAt(new \DateTimeImmutable()) + ->expiresAt($accessToken->getExpiryDateTime()) + ->relatedTo($accessToken->getUserIdentifier()); + + if ($claimSet instanceof ClaimSetEntryInterface) { + foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims())as $claimName => $claimValue) { + $builder->withClaim($claimName, $claimValue); + } + } + + // https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes + if (array_key_exists($nonce = "nonce", $this->request->getQueryParams())) { + $builder->withClaim($nonce, $this->request->getQueryParams()[$nonce]); + } + + $this->getEmitter()->emit( + new IdTokenClaimsCreated(RequestEvent::ID_TOKEN_CLAIMS_CREATED, $this->request, $builder)); + + return [ + 'id_token' => $builder->getToken(new Sha256(), + InMemory::file($this->privateKey->getKeyPath(), (string)$this->privateKey->getPassPhrase()))->toString() + ]; + } +} From 55e0d4e76daa6e1e21af5a91efe0c00f2540c779 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Fri, 25 Nov 2022 14:03:18 +0100 Subject: [PATCH 02/39] Finalization of IdTokenResponce implementation --- src/ClaimExtractor.php | 119 ++++++++++-------- src/Entities/ClaimSetEntryInterface.php | 4 +- src/Entities/ClaimSetInterface.php | 15 +++ src/IdTokenClaimsCreated.php | 1 - src/IdTokenIssued.php | 37 ++++++ .../ClaimSetRepositoryInterface.php | 14 ++- src/RequestEvent.php | 3 +- src/ResponseTypes/IdTokenResponse.php | 57 +++++++-- 8 files changed, 175 insertions(+), 75 deletions(-) create mode 100644 src/Entities/ClaimSetInterface.php create mode 100644 src/IdTokenIssued.php diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index 94ca660f0..640008cb2 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -17,6 +17,11 @@ */ class ClaimExtractor { + /** + * claimSets + * + * @var ClaimSetEntryInterface[] + */ protected $claimSets = []; protected $protectedClaims = ['profile', 'email', 'address', 'phone']; @@ -24,61 +29,24 @@ class ClaimExtractor /** * ClaimExtractor constructor * - * @param ClaimSet[] $claimSets + * @param ClaimSetEntryInterface[] $claimSets */ public function __construct(array $claimSets = []) { - // Add Default OpenID Connect Claims - // @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims - $this->addClaimSet( - new ClaimSetEntry('profile', [ - 'name', - 'family_name', - 'given_name', - 'middle_name', - 'nickname', - 'preferred_username', - 'profile', - 'picture', - 'website', - 'gender', - 'birthdate', - 'zoneinfo', - 'locale', - 'updated_at' - ]) - ); - $this->addClaimSet( - new ClaimSetEntry('email', [ - 'email', - 'email_verified' - ]) - ); - $this->addClaimSet( - new ClaimSetEntry('address', [ - 'address' - ]) - ); - $this->addClaimSet( - new ClaimSetEntry('phone', [ - 'phone_number', - 'phone_number_verified' - ]) - ); - + $this->claimSets = self::getDefaultClaimSetEnties(); foreach ($claimSets as $claimSet) { $this->addClaimSet($claimSet); } } /** - * @param ClaimSetInterface $claimSet + * @param ClaimSetEntryInterface $claimSetEntry * @return $this * @throws \InvalidArgumentException */ - public function addClaimSet(ClaimSetEntryInterface $claimSet): ClaimExtractor + public function addClaimSet(ClaimSetEntryInterface $claimSetEntry): ClaimExtractor { - $scope = $claimSet->getScope(); + $scope = $claimSetEntry->getScope(); if (in_array($scope, $this->protectedClaims) && !empty($this->claimSets[$scope])) { throw new \InvalidArgumentException( @@ -86,31 +54,34 @@ public function addClaimSet(ClaimSetEntryInterface $claimSet): ClaimExtractor ); } - $this->claimSets[$scope] = $claimSet; + $this->claimSets[$scope] = $claimSetEntry->getClaims(); return $this; } /** * @param string $scope - * @return ClaimSet|null + * + * @return ClaimSetEntryInterface|null */ - public function getClaimSet(string$scope) + public function getClaimSet(string $scope): ?ClaimSetEntryInterface { - if (!$this->hasClaimSet($scope)) { - return null; + foreach($this->claimSets as $set) { + if ($set->getScope() === $scope) { + return $set; + } } - - return $this->claimSets[$scope]; + return null; } /** - * @param string $scope - * @return bool - */ - public function hasClaimSet(string $scope): bool + * Get claimSets + * + * @return array + */ + public function getClaimSets(): array { - return array_key_exists($scope, $this->claimSets); + return $this->claimSets; } /** @@ -151,4 +122,44 @@ function($key) use ($intersected) { return $claimData; } + + /** + * Create a array default openID connect claims + * + * @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims + * + * @return ClaimSetEntry[] + */ + public static function getDefaultClaimSetEnties(): array + { + return [ + new ClaimSetEntry('profile', [ + 'name', + 'family_name', + 'given_name', + 'middle_name', + 'nickname', + 'preferred_username', + 'profile', + 'picture', + 'website', + 'gender', + 'birthdate', + 'zoneinfo', + 'locale', + 'updated_at' + ]), + new ClaimSetEntry('email', [ + 'email', + 'email_verified' + ]), + new ClaimSetEntry('address', [ + 'address' + ]), + new ClaimSetEntry('phone', [ + 'phone_number', + 'phone_number_verified' + ]) + ]; + } } diff --git a/src/Entities/ClaimSetEntryInterface.php b/src/Entities/ClaimSetEntryInterface.php index c931165af..101034e82 100644 --- a/src/Entities/ClaimSetEntryInterface.php +++ b/src/Entities/ClaimSetEntryInterface.php @@ -9,9 +9,7 @@ * @author Marc Riemer * @license http://opensource.org/licenses/MIT MIT */ -interface ClaimSetEntryInterface +interface ClaimSetEntryInterface extends ClaimSetInterface { - public function getClaims(): array; - public function getScope(): string; } diff --git a/src/Entities/ClaimSetInterface.php b/src/Entities/ClaimSetInterface.php new file mode 100644 index 000000000..3368bce13 --- /dev/null +++ b/src/Entities/ClaimSetInterface.php @@ -0,0 +1,15 @@ + + * @author Marc Riemer + * @license http://opensource.org/licenses/MIT MIT + */ +interface ClaimSetInterface +{ + public function getClaims(): array; +} diff --git a/src/IdTokenClaimsCreated.php b/src/IdTokenClaimsCreated.php index 413dec554..e784f68cf 100644 --- a/src/IdTokenClaimsCreated.php +++ b/src/IdTokenClaimsCreated.php @@ -22,7 +22,6 @@ class IdTokenClaimsCreated extends RequestEvent public function __construct($name, ServerRequestInterface $request, Builder $builder) { parent::__construct($name, $request); - $this->request = $request; $this->builder = $builder; } diff --git a/src/IdTokenIssued.php b/src/IdTokenIssued.php new file mode 100644 index 000000000..dac705573 --- /dev/null +++ b/src/IdTokenIssued.php @@ -0,0 +1,37 @@ + + */ +class IdTokenIssued extends RequestEvent +{ + /** + * Token + * + * @var Token + */ + private $token; + + public function __construct($name, ServerRequestInterface $request, Token $token) + { + parent::__construct($name, $request); + $this->token = $token; + } + + /** + * Get Token + * + * @return Token + */ + public function getToken(): Token + { + return $this->token; + } +} \ No newline at end of file diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php index a475c7468..35d878f9c 100644 --- a/src/Repositories/ClaimSetRepositoryInterface.php +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -2,10 +2,11 @@ namespace League\OAuth2\Server\Repositories; -use League\OAuth2\Server\Entities\ClaimSetEntryInterface; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClaimSetInterface; /** - * ClaimSetRepositoryInterface helps to resolve claims for id_token + * ClaimSetRepositoryInterface resolve claims for id_token. * * @author Marc Riemer * @author Steve Rhoades @@ -13,5 +14,12 @@ */ interface ClaimSetRepositoryInterface { - public function getClaimSetByUserIdentifier($userIdentifyer): ClaimSetEntryInterface; + /** + * Get ClaimSetEntries + * + * @param AccessTokenEntityInterface $authCode + * + * @return ClaimSetInterface + */ + public function getClaimSetEntry(AccessTokenEntityInterface $authCode): ClaimSetInterface; } \ No newline at end of file diff --git a/src/RequestEvent.php b/src/RequestEvent.php index 6273f5c7a..b0d38dcc3 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -20,11 +20,12 @@ class RequestEvent extends Event const REFRESH_TOKEN_ISSUED = 'refresh_token.issued'; const ACCESS_TOKEN_ISSUED = 'access_token.issued'; + const ID_TOKEN_ISSUED = 'id_token.issued'; /** * This event can be used to extent claims of the id_token */ - const ID_TOKEN_CLAIMS_CREATED = 'id_token_claims.created'; + const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created'; /** * @var ServerRequestInterface diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 7ef9466f7..93fc253d5 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -12,8 +12,10 @@ use Lcobucci\JWT\Signer\Key\InMemory; use League\Event\EmitterAwareTrait; use League\OAuth2\Server\ClaimExtractor; -use League\OAuth2\Server\Entities\ClaimSetEntryInterface; +use League\OAuth2\Server\Entities\ClaimSetInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\IdTokenClaimsCreated; +use League\OAuth2\Server\IdTokenIssued; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; use League\OAuth2\Server\RequestEvent; @@ -31,8 +33,12 @@ class IdTokenResponse extends BearerTokenResponse public function __construct( protected ServerRequestInterface $request, protected ClaimSetRepositoryInterface $claimRepository, - protected ClaimExtractor $extractor) - {} + protected ?ClaimExtractor $extractor = null) + { + if(!$extractor) { + $this->extractor = new ClaimExtractor(); + } + } /** * @param AccessTokenEntityInterface $accessToken @@ -40,12 +46,13 @@ public function __construct( */ protected function getExtraParams(AccessTokenEntityInterface $accessToken): array { - // Onyly add id_token to openid requests - if (!in_array("openid", $accessToken->getScopes())) { + // Onyly add id_token to openid scopes + if (!self::isOpenIDRequest($accessToken->getScopes())) { return []; } - $claimSet = $this->claimRepository->getClaimSetByUserIdentifier($accessToken->getUserIdentifier()); + $claimSet = $this->claimRepository->getClaimSetEntry($accessToken); + $builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates())) ->permittedFor($accessToken->getClient()->getIdentifier()) ->issuedBy(sprintf("%s://%s", @@ -54,23 +61,47 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra ->expiresAt($accessToken->getExpiryDateTime()) ->relatedTo($accessToken->getUserIdentifier()); - if ($claimSet instanceof ClaimSetEntryInterface) { - foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims())as $claimName => $claimValue) { + if ($claimSet instanceof ClaimSetInterface) { + foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims()) as $claimName => $claimValue) { $builder->withClaim($claimName, $claimValue); } } - + // https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes - if (array_key_exists($nonce = "nonce", $this->request->getQueryParams())) { - $builder->withClaim($nonce, $this->request->getQueryParams()[$nonce]); + if (array_key_exists($nonce = "nonce", $this->request->getParsedBody())) { + $builder->withClaim($nonce, $this->request->getParsedBody()[$nonce]); } $this->getEmitter()->emit( new IdTokenClaimsCreated(RequestEvent::ID_TOKEN_CLAIMS_CREATED, $this->request, $builder)); + + $token = $builder->getToken(new Sha256(), + InMemory::file($this->privateKey->getKeyPath(), (string)$this->privateKey->getPassPhrase())); + + $this->getEmitter()->emit( + new IdTokenIssued(RequestEvent::ID_TOKEN_ISSUED, $this->request, $token)); return [ - 'id_token' => $builder->getToken(new Sha256(), - InMemory::file($this->privateKey->getKeyPath(), (string)$this->privateKey->getPassPhrase()))->toString() + 'id_token' => $token->toString() ]; } + + /** + * Return true If this is an OpenID request + * + * @param ScopeEntityInterface[] $scopes + * + * @return bool + */ + private static function isOpenIDRequest($scopes): bool + { + foreach ($scopes as $scope) { + if ($scope instanceof ScopeEntityInterface) { + if ($scope->getIdentifier() === 'openid') { + return true; + } + } + } + return false; + } } From d965e1a736e4097407efb2200c590584a891d62e Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Fri, 25 Nov 2022 14:05:02 +0100 Subject: [PATCH 03/39] Add IdTokenResponce related tests --- .../ResponseTypes/BearerResponseTypeTest.php | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index da7242651..f4caea63d 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -6,11 +6,18 @@ use DateTimeImmutable; use Laminas\Diactoros\Response; use Laminas\Diactoros\ServerRequest; +use Lcobucci\Clock\SystemClock; +use Lcobucci\JWT as JWT; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; +use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\CryptKey; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; +use League\OAuth2\Server\ResponseTypes\IdTokenResponse; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; @@ -63,6 +70,132 @@ public function testGenerateHttpResponse() $this->assertObjectHasAttribute('refresh_token', $json); } + public function testGenerateHttpResponseWithIdToken() + { + $claimSetRepository = new class() implements ClaimSetRepositoryInterface + { + public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetInterface + { + $claimSet = new class() implements ClaimSetInterface + { + public array $claims = []; + public function getClaims(): array + { + return $this->claims; + } + }; + foreach(ClaimExtractor::getDefaultClaimSetEnties() as $setEntry) { + foreach($accessToken->getScopes() as $scope) { + if ($setEntry->getScope() === $scope->getIdentifier()) { + foreach($setEntry->getClaims() as $claim) { + $claimSet->claims[$claim] = $claim; + } + } + } + + } + return $claimSet; + } + }; + + $nonce = \uniqid(); + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code' => "code", + 'nonce' => $nonce + ] + ); + + $responseType = new IdTokenResponse($request, $claimSetRepository, $claimExtrator = new ClaimExtractor()); + + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $openidScope = new ScopeEntity(); + $openidScope->setIdentifier('openid'); + + $emailScope = new ScopeEntity(); + $emailScope->setIdentifier('email'); + + $profileScope = new ScopeEntity(); + $profileScope->setIdentifier('profile'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier(\uniqid()); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + $accessToken->setClient($client); + + $accessToken->addScope($openidScope); + $accessToken->addScope($emailScope); + $accessToken->addScope($profileScope); + + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $accessToken->setUserIdentifier(\uniqid()); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier(\uniqid()); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + + $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('no-cache', $response->getHeader('pragma')[0]); + $this->assertEquals('no-store', $response->getHeader('cache-control')[0]); + $this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); + + $response->getBody()->rewind(); + $json = \json_decode($response->getBody()->getContents()); + $this->assertEquals('Bearer', $json->token_type); + $this->assertObjectHasAttribute('expires_in', $json); + $this->assertObjectHasAttribute('access_token', $json); + $this->assertObjectHasAttribute('refresh_token', $json); + + $this->assertObjectHasAttribute('id_token', $json); + + $token = (new JWT\Token\Parser(new JWT\Encoding\JoseEncoder()))->parse($json->id_token); + $validator = new JWT\Validation\Validator(); + + $this->assertTrue($validator->validate($token, + new JWT\Validation\Constraint\SignedWith(new JWT\Signer\Rsa\Sha256(), JWT\Signer\Key\InMemory::file(__DIR__ . '/../Stubs/public.key', "")))); + + $this->assertTrue($validator->validate($token, + new JWT\Validation\Constraint\IssuedBy(sprintf("%s://%s", $request->getUri()->getScheme(), $request->getUri()->getHost())))); + + $this->assertTrue($validator->validate($token, + new JWT\Validation\Constraint\PermittedFor($client->getIdentifier()))); + + $this->assertTrue($validator->validate($token, + new JWT\Validation\Constraint\RelatedTo($accessToken->getUserIdentifier()))); + + $this->assertTrue($validator->validate($token, + new JWT\Validation\Constraint\LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())))); + + foreach($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { + $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); + } + + $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue("nonce", $nonce))); + } + public function testGenerateHttpResponseWithExtraParams() { $responseType = new BearerTokenResponseWithParams(); From f588884c5ae4e74f4d056ca34f61e15039344244 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Fri, 25 Nov 2022 14:15:35 +0100 Subject: [PATCH 04/39] Fixed Coding Style --- src/ClaimExtractor.php | 50 +++++++------ src/Entities/ClaimSetEntry.php | 10 ++- src/Entities/ClaimSetEntryInterface.php | 2 +- src/Entities/ClaimSetInterface.php | 2 +- src/IdTokenClaimsCreated.php | 6 +- src/IdTokenIssued.php | 10 +-- .../ClaimSetRepositoryInterface.php | 6 +- src/RequestEvent.php | 2 +- src/ResponseTypes/IdTokenResponse.php | 54 ++++++++------ .../ResponseTypes/BearerResponseTypeTest.php | 73 +++++++++++-------- 10 files changed, 120 insertions(+), 95 deletions(-) diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index 640008cb2..66fc27cb6 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -2,16 +2,15 @@ namespace League\OAuth2\Server; -use League\OAuth2\Server\Entities\ClaimSet; use League\OAuth2\Server\Entities\ClaimSetEntry; use League\OAuth2\Server\Entities\ClaimSetEntryInterface; -use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; /** * ClaimExtractor - * + * * @link https://github.com/steverhoades/oauth2-openid-connect-server + * * @author Steve Rhoades * @author Marc Riemer */ @@ -28,12 +27,12 @@ class ClaimExtractor /** * ClaimExtractor constructor - * + * * @param ClaimSetEntryInterface[] $claimSets */ public function __construct(array $claimSets = []) { - $this->claimSets = self::getDefaultClaimSetEnties(); + $this->claimSets = self::getDefaultClaimSetEnties(); foreach ($claimSets as $claimSet) { $this->addClaimSet($claimSet); } @@ -41,16 +40,18 @@ public function __construct(array $claimSets = []) /** * @param ClaimSetEntryInterface $claimSetEntry + * * @return $this + * * @throws \InvalidArgumentException */ public function addClaimSet(ClaimSetEntryInterface $claimSetEntry): ClaimExtractor { $scope = $claimSetEntry->getScope(); - if (in_array($scope, $this->protectedClaims) && !empty($this->claimSets[$scope])) { + if (\in_array($scope, $this->protectedClaims) && !empty($this->claimSets[$scope])) { throw new \InvalidArgumentException( - sprintf("%s is a protected scope and is pre-defined by the OpenID Connect specification.", $scope) + \sprintf('%s is a protected scope and is pre-defined by the OpenID Connect specification.', $scope) ); } @@ -61,24 +62,25 @@ public function addClaimSet(ClaimSetEntryInterface $claimSetEntry): ClaimExtract /** * @param string $scope - * + * * @return ClaimSetEntryInterface|null */ public function getClaimSet(string $scope): ?ClaimSetEntryInterface { - foreach($this->claimSets as $set) { + foreach ($this->claimSets as $set) { if ($set->getScope() === $scope) { return $set; } } + return null; } /** * Get claimSets * - * @return array - */ + * @return array + */ public function getClaimSets(): array { return $this->claimSets; @@ -89,12 +91,13 @@ public function getClaimSets(): array * * @param array $scopes * @param array $claims + * * @return array */ public function extract(array $scopes, array $claims): array { $claimData = []; - $keys = array_keys($claims); + $keys = \array_keys($claims); foreach ($scopes as $scope) { $scopeName = ($scope instanceof ScopeEntityInterface) ? $scope->getIdentifier() : $scope; @@ -104,20 +107,21 @@ public function extract(array $scopes, array $claims): array continue; } - $intersected = array_intersect($claimSet->getClaims(), $keys); + $intersected = \array_intersect($claimSet->getClaims(), $keys); if (empty($intersected)) { continue; } - $data = array_filter($claims, - function($key) use ($intersected) { - return in_array($key, $intersected); + $data = \array_filter( + $claims, + function ($key) use ($intersected) { + return \in_array($key, $intersected); }, ARRAY_FILTER_USE_KEY ); - $claimData = array_merge($claimData, $data); + $claimData = \array_merge($claimData, $data); } return $claimData; @@ -127,7 +131,7 @@ function($key) use ($intersected) { * Create a array default openID connect claims * * @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims - * + * * @return ClaimSetEntry[] */ public static function getDefaultClaimSetEnties(): array @@ -147,19 +151,19 @@ public static function getDefaultClaimSetEnties(): array 'birthdate', 'zoneinfo', 'locale', - 'updated_at' + 'updated_at', ]), new ClaimSetEntry('email', [ 'email', - 'email_verified' + 'email_verified', ]), new ClaimSetEntry('address', [ - 'address' + 'address', ]), new ClaimSetEntry('phone', [ 'phone_number', - 'phone_number_verified' - ]) + 'phone_number_verified', + ]), ]; } } diff --git a/src/Entities/ClaimSetEntry.php b/src/Entities/ClaimSetEntry.php index 85762a0e5..91830db94 100644 --- a/src/Entities/ClaimSetEntry.php +++ b/src/Entities/ClaimSetEntry.php @@ -4,7 +4,7 @@ /** * ClaimSetEntry - * + * * @author Steve Rhoades * @author Marc Riemer * @license http://opensource.org/licenses/MIT MIT @@ -12,9 +12,11 @@ class ClaimSetEntry implements ClaimSetEntryInterface { public function __construct( - protected string $scope, - protected array $claims) - {} + protected string $scope, + protected array $claims + ) + { + } /** * Get scope diff --git a/src/Entities/ClaimSetEntryInterface.php b/src/Entities/ClaimSetEntryInterface.php index 101034e82..6a710ebc8 100644 --- a/src/Entities/ClaimSetEntryInterface.php +++ b/src/Entities/ClaimSetEntryInterface.php @@ -4,7 +4,7 @@ /** * ClaimSetEntryInterface - * + * * @author Steve Rhoades * @author Marc Riemer * @license http://opensource.org/licenses/MIT MIT diff --git a/src/Entities/ClaimSetInterface.php b/src/Entities/ClaimSetInterface.php index 3368bce13..27aa81aae 100644 --- a/src/Entities/ClaimSetInterface.php +++ b/src/Entities/ClaimSetInterface.php @@ -4,7 +4,7 @@ /** * ClaimSetEntryInterface - * + * * @author Steve Rhoades * @author Marc Riemer * @license http://opensource.org/licenses/MIT MIT diff --git a/src/IdTokenClaimsCreated.php b/src/IdTokenClaimsCreated.php index e784f68cf..37e1322af 100644 --- a/src/IdTokenClaimsCreated.php +++ b/src/IdTokenClaimsCreated.php @@ -7,10 +7,10 @@ /** * IdTokenClaimsCreated Events helps to extend claims of the id_token - * + * * @author Marc Riemer */ -class IdTokenClaimsCreated extends RequestEvent +class IdTokenClaimsCreated extends RequestEvent { /** * Builder @@ -29,4 +29,4 @@ public function getBuilder(): Builder { return $this->builder; } -} \ No newline at end of file +} diff --git a/src/IdTokenIssued.php b/src/IdTokenIssued.php index dac705573..fbb41e646 100644 --- a/src/IdTokenIssued.php +++ b/src/IdTokenIssued.php @@ -7,10 +7,10 @@ /** * An id_token has been issued - * + * * @author Marc Riemer */ -class IdTokenIssued extends RequestEvent +class IdTokenIssued extends RequestEvent { /** * Token @@ -28,10 +28,10 @@ public function __construct($name, ServerRequestInterface $request, Token $token /** * Get Token * - * @return Token - */ + * @return Token + */ public function getToken(): Token { return $this->token; } -} \ No newline at end of file +} diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php index 35d878f9c..a7bca1f61 100644 --- a/src/Repositories/ClaimSetRepositoryInterface.php +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -7,7 +7,7 @@ /** * ClaimSetRepositoryInterface resolve claims for id_token. - * + * * @author Marc Riemer * @author Steve Rhoades * @license http://opensource.org/licenses/MIT MIT @@ -18,8 +18,8 @@ interface ClaimSetRepositoryInterface * Get ClaimSetEntries * * @param AccessTokenEntityInterface $authCode - * + * * @return ClaimSetInterface */ public function getClaimSetEntry(AccessTokenEntityInterface $authCode): ClaimSetInterface; -} \ No newline at end of file +} diff --git a/src/RequestEvent.php b/src/RequestEvent.php index b0d38dcc3..ddb9847b5 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -25,7 +25,7 @@ class RequestEvent extends Event /** * This event can be used to extent claims of the id_token */ - const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created'; + const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created'; /** * @var ServerRequestInterface diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 93fc253d5..a71765f45 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -2,27 +2,27 @@ namespace League\OAuth2\Server\ResponseTypes; -use Psr\Http\Message\ServerRequestInterface; -use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; -use Lcobucci\JWT\Token\Builder; -use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\Encoding\ChainedFormatter; use Lcobucci\JWT\Encoding\JoseEncoder; use Lcobucci\JWT\Signer\Key\InMemory; +use Lcobucci\JWT\Signer\Rsa\Sha256; +use Lcobucci\JWT\Token\Builder; use League\Event\EmitterAwareTrait; use League\OAuth2\Server\ClaimExtractor; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\IdTokenClaimsCreated; use League\OAuth2\Server\IdTokenIssued; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; use League\OAuth2\Server\RequestEvent; +use Psr\Http\Message\ServerRequestInterface; /** * OpenidConfigurationResponse - * + * * @link https://github.com/steverhoades/oauth2-openid-connect-server + * * @author Steve Rhoades * @author Marc Riemer */ @@ -31,17 +31,19 @@ class IdTokenResponse extends BearerTokenResponse use EmitterAwareTrait; public function __construct( - protected ServerRequestInterface $request, - protected ClaimSetRepositoryInterface $claimRepository, - protected ?ClaimExtractor $extractor = null) + protected ServerRequestInterface $request, + protected ClaimSetRepositoryInterface $claimRepository, + protected ?ClaimExtractor $extractor = null + ) { - if(!$extractor) { + if (!$extractor) { $this->extractor = new ClaimExtractor(); } } /** * @param AccessTokenEntityInterface $accessToken + * * @return array */ protected function getExtraParams(AccessTokenEntityInterface $accessToken): array @@ -55,8 +57,11 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra $builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates())) ->permittedFor($accessToken->getClient()->getIdentifier()) - ->issuedBy(sprintf("%s://%s", - $this->request->getUri()->getScheme(), $this->request->getUri()->getHost())) + ->issuedBy(\sprintf( + '%s://%s', + $this->request->getUri()->getScheme(), + $this->request->getUri()->getHost() + )) ->issuedAt(new \DateTimeImmutable()) ->expiresAt($accessToken->getExpiryDateTime()) ->relatedTo($accessToken->getUserIdentifier()); @@ -66,31 +71,35 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra $builder->withClaim($claimName, $claimValue); } } - + // https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes - if (array_key_exists($nonce = "nonce", $this->request->getParsedBody())) { + if (\array_key_exists($nonce = 'nonce', $this->request->getParsedBody())) { $builder->withClaim($nonce, $this->request->getParsedBody()[$nonce]); } $this->getEmitter()->emit( - new IdTokenClaimsCreated(RequestEvent::ID_TOKEN_CLAIMS_CREATED, $this->request, $builder)); - - $token = $builder->getToken(new Sha256(), - InMemory::file($this->privateKey->getKeyPath(), (string)$this->privateKey->getPassPhrase())); + new IdTokenClaimsCreated(RequestEvent::ID_TOKEN_CLAIMS_CREATED, $this->request, $builder) + ); + + $token = $builder->getToken( + new Sha256(), + InMemory::file($this->privateKey->getKeyPath(), (string) $this->privateKey->getPassPhrase()) + ); $this->getEmitter()->emit( - new IdTokenIssued(RequestEvent::ID_TOKEN_ISSUED, $this->request, $token)); + new IdTokenIssued(RequestEvent::ID_TOKEN_ISSUED, $this->request, $token) + ); return [ - 'id_token' => $token->toString() + 'id_token' => $token->toString(), ]; } /** * Return true If this is an OpenID request - * + * * @param ScopeEntityInterface[] $scopes - * + * * @return bool */ private static function isOpenIDRequest($scopes): bool @@ -102,6 +111,7 @@ private static function isOpenIDRequest($scopes): bool } } } + return false; } } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index f4caea63d..2b8a33d7a 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -72,28 +72,27 @@ public function testGenerateHttpResponse() public function testGenerateHttpResponseWithIdToken() { - $claimSetRepository = new class() implements ClaimSetRepositoryInterface - { + $claimSetRepository = new class() implements ClaimSetRepositoryInterface { public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetInterface { - $claimSet = new class() implements ClaimSetInterface - { + $claimSet = new class() implements ClaimSetInterface { public array $claims = []; + public function getClaims(): array { return $this->claims; } }; - foreach(ClaimExtractor::getDefaultClaimSetEnties() as $setEntry) { - foreach($accessToken->getScopes() as $scope) { + foreach (ClaimExtractor::getDefaultClaimSetEnties() as $setEntry) { + foreach ($accessToken->getScopes() as $scope) { if ($setEntry->getScope() === $scope->getIdentifier()) { - foreach($setEntry->getClaims() as $claim) { + foreach ($setEntry->getClaims() as $claim) { $claimSet->claims[$claim] = $claim; } } - } - + } } + return $claimSet; } }; @@ -112,8 +111,8 @@ public function getClaims(): array 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code' => "code", - 'nonce' => $nonce + 'code' => 'code', + 'nonce' => $nonce, ] ); @@ -168,32 +167,42 @@ public function getClaims(): array $this->assertObjectHasAttribute('expires_in', $json); $this->assertObjectHasAttribute('access_token', $json); $this->assertObjectHasAttribute('refresh_token', $json); - + $this->assertObjectHasAttribute('id_token', $json); $token = (new JWT\Token\Parser(new JWT\Encoding\JoseEncoder()))->parse($json->id_token); $validator = new JWT\Validation\Validator(); - - $this->assertTrue($validator->validate($token, - new JWT\Validation\Constraint\SignedWith(new JWT\Signer\Rsa\Sha256(), JWT\Signer\Key\InMemory::file(__DIR__ . '/../Stubs/public.key', "")))); - - $this->assertTrue($validator->validate($token, - new JWT\Validation\Constraint\IssuedBy(sprintf("%s://%s", $request->getUri()->getScheme(), $request->getUri()->getHost())))); - - $this->assertTrue($validator->validate($token, - new JWT\Validation\Constraint\PermittedFor($client->getIdentifier()))); - - $this->assertTrue($validator->validate($token, - new JWT\Validation\Constraint\RelatedTo($accessToken->getUserIdentifier()))); - - $this->assertTrue($validator->validate($token, - new JWT\Validation\Constraint\LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())))); - - foreach($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { - $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); + + $this->assertTrue($validator->validate( + $token, + new JWT\Validation\Constraint\SignedWith(new JWT\Signer\Rsa\Sha256(), JWT\Signer\Key\InMemory::file(__DIR__ . '/../Stubs/public.key', '')) + )); + + $this->assertTrue($validator->validate( + $token, + new JWT\Validation\Constraint\IssuedBy(\sprintf('%s://%s', $request->getUri()->getScheme(), $request->getUri()->getHost())) + )); + + $this->assertTrue($validator->validate( + $token, + new JWT\Validation\Constraint\PermittedFor($client->getIdentifier()) + )); + + $this->assertTrue($validator->validate( + $token, + new JWT\Validation\Constraint\RelatedTo($accessToken->getUserIdentifier()) + )); + + $this->assertTrue($validator->validate( + $token, + new JWT\Validation\Constraint\LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) + )); + + foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { + $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); } - - $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue("nonce", $nonce))); + + $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue('nonce', $nonce))); } public function testGenerateHttpResponseWithExtraParams() From a7a2c3e5f671eb73a7a0ce21b0a6b5a85a5c010e Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Fri, 25 Nov 2022 22:58:15 +0100 Subject: [PATCH 05/39] Removed ServerRequestInterface --- .../src/Repositories/IdTokenRepository.php | 40 +++++++++++ src/IdTokenClaimsCreated.php | 11 +-- src/IdTokenEvent.php | 18 +++++ src/IdTokenIssued.php | 14 ++-- .../IdTokenRepositoryInterface.php | 23 +++++++ src/ResponseTypes/IdTokenResponse.php | 30 ++------ .../ResponseTypes/BearerResponseTypeTest.php | 69 +++++++++++++------ 7 files changed, 149 insertions(+), 56 deletions(-) create mode 100644 examples/src/Repositories/IdTokenRepository.php create mode 100644 src/IdTokenEvent.php create mode 100644 src/Repositories/IdTokenRepositoryInterface.php diff --git a/examples/src/Repositories/IdTokenRepository.php b/examples/src/Repositories/IdTokenRepository.php new file mode 100644 index 000000000..9b547d4c4 --- /dev/null +++ b/examples/src/Repositories/IdTokenRepository.php @@ -0,0 +1,40 @@ + + * @license http://opensource.org/licenses/MIT MIT + */ +class IdTokenRepository implements IdTokenRepositoryInterface +{ + public function __construct(private string $issuedBy, private ?string $nonce = null) + { + } + + /** + * {@inheritdoc} + */ + public function getBuilder(AccessTokenEntityInterface $accessToken): Builder + { + $builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates())) + ->permittedFor($accessToken->getClient()->getIdentifier()) + ->issuedBy($this->issuedBy) + ->issuedAt(new \DateTimeImmutable()) + ->expiresAt($accessToken->getExpiryDateTime()) + ->relatedTo($accessToken->getUserIdentifier()); + + if ($this->nonce) { + $builder->withClaim("nonce", $this->nonce); + } + + return $builder; + } +} diff --git a/src/IdTokenClaimsCreated.php b/src/IdTokenClaimsCreated.php index 37e1322af..bb3a7accd 100644 --- a/src/IdTokenClaimsCreated.php +++ b/src/IdTokenClaimsCreated.php @@ -3,14 +3,15 @@ namespace League\OAuth2\Server; use Lcobucci\JWT\Token\Builder; -use Psr\Http\Message\ServerRequestInterface; /** - * IdTokenClaimsCreated Events helps to extend claims of the id_token + * IdTokenClaimsCreated Event helps to extend claims of the id_token + * + * A usecase is to add nonce If requested by the client * * @author Marc Riemer */ -class IdTokenClaimsCreated extends RequestEvent +final class IdTokenClaimsCreated extends IdTokenEvent { /** * Builder @@ -19,9 +20,9 @@ class IdTokenClaimsCreated extends RequestEvent */ private $builder; - public function __construct($name, ServerRequestInterface $request, Builder $builder) + public function __construct($name, Builder $builder) { - parent::__construct($name, $request); + parent::__construct($name); $this->builder = $builder; } diff --git a/src/IdTokenEvent.php b/src/IdTokenEvent.php new file mode 100644 index 000000000..5f1eb27aa --- /dev/null +++ b/src/IdTokenEvent.php @@ -0,0 +1,18 @@ + + */ +class IdTokenEvent extends Event +{ + const ID_TOKEN_ISSUED = 'id_token.issued'; + + // This event can be used to extent claims of the id_token + const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created'; +} diff --git a/src/IdTokenIssued.php b/src/IdTokenIssued.php index fbb41e646..425f5bf8d 100644 --- a/src/IdTokenIssued.php +++ b/src/IdTokenIssued.php @@ -10,7 +10,7 @@ * * @author Marc Riemer */ -class IdTokenIssued extends RequestEvent +final class IdTokenIssued extends IdTokenEvent { /** * Token @@ -19,12 +19,6 @@ class IdTokenIssued extends RequestEvent */ private $token; - public function __construct($name, ServerRequestInterface $request, Token $token) - { - parent::__construct($name, $request); - $this->token = $token; - } - /** * Get Token * @@ -34,4 +28,10 @@ public function getToken(): Token { return $this->token; } + + public function __construct($name, Token $token) + { + parent::__construct($name); + $this->token = $token; + } } diff --git a/src/Repositories/IdTokenRepositoryInterface.php b/src/Repositories/IdTokenRepositoryInterface.php new file mode 100644 index 000000000..f7d1dc1df --- /dev/null +++ b/src/Repositories/IdTokenRepositoryInterface.php @@ -0,0 +1,23 @@ + + * @license http://opensource.org/licenses/MIT MIT + */ +interface IdTokenRepositoryInterface +{ + /** + * Creates new token builder and may add some standard claims + * + * @param AccessTokenEntityInterface $token Issued access token + * @return Builder + */ + public function getBuilder(AccessTokenEntityInterface $token): Builder; +} diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index a71765f45..0305c8114 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -2,11 +2,8 @@ namespace League\OAuth2\Server\ResponseTypes; -use Lcobucci\JWT\Encoding\ChainedFormatter; -use Lcobucci\JWT\Encoding\JoseEncoder; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; -use Lcobucci\JWT\Token\Builder; use League\Event\EmitterAwareTrait; use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; @@ -15,8 +12,8 @@ use League\OAuth2\Server\IdTokenClaimsCreated; use League\OAuth2\Server\IdTokenIssued; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; +use League\OAuth2\Server\Repositories\IdTokenRepositoryInterface; use League\OAuth2\Server\RequestEvent; -use Psr\Http\Message\ServerRequestInterface; /** * OpenidConfigurationResponse @@ -31,11 +28,10 @@ class IdTokenResponse extends BearerTokenResponse use EmitterAwareTrait; public function __construct( - protected ServerRequestInterface $request, + protected IdTokenRepositoryInterface $builder, protected ClaimSetRepositoryInterface $claimRepository, protected ?ClaimExtractor $extractor = null - ) - { + ) { if (!$extractor) { $this->extractor = new ClaimExtractor(); } @@ -55,16 +51,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra $claimSet = $this->claimRepository->getClaimSetEntry($accessToken); - $builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates())) - ->permittedFor($accessToken->getClient()->getIdentifier()) - ->issuedBy(\sprintf( - '%s://%s', - $this->request->getUri()->getScheme(), - $this->request->getUri()->getHost() - )) - ->issuedAt(new \DateTimeImmutable()) - ->expiresAt($accessToken->getExpiryDateTime()) - ->relatedTo($accessToken->getUserIdentifier()); + $builder = $this->builder->getBuilder($accessToken); if ($claimSet instanceof ClaimSetInterface) { foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims()) as $claimName => $claimValue) { @@ -72,13 +59,8 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra } } - // https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes - if (\array_key_exists($nonce = 'nonce', $this->request->getParsedBody())) { - $builder->withClaim($nonce, $this->request->getParsedBody()[$nonce]); - } - $this->getEmitter()->emit( - new IdTokenClaimsCreated(RequestEvent::ID_TOKEN_CLAIMS_CREATED, $this->request, $builder) + new IdTokenClaimsCreated(RequestEvent::ID_TOKEN_CLAIMS_CREATED, $builder) ); $token = $builder->getToken( @@ -87,7 +69,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra ); $this->getEmitter()->emit( - new IdTokenIssued(RequestEvent::ID_TOKEN_ISSUED, $this->request, $token) + new IdTokenIssued(RequestEvent::ID_TOKEN_ISSUED, $token) ); return [ diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 2b8a33d7a..fc031872b 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -8,6 +8,9 @@ use Laminas\Diactoros\ServerRequest; use Lcobucci\Clock\SystemClock; use Lcobucci\JWT as JWT; +use Lcobucci\JWT\Encoding\ChainedFormatter; +use Lcobucci\JWT\Encoding\JoseEncoder; +use Lcobucci\JWT\Token\Builder; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\CryptKey; @@ -16,6 +19,7 @@ use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; +use League\OAuth2\Server\Repositories\IdTokenRepositoryInterface; use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; use League\OAuth2\Server\ResponseTypes\IdTokenResponse; use LeagueTests\Stubs\AccessTokenEntity; @@ -72,6 +76,23 @@ public function testGenerateHttpResponse() public function testGenerateHttpResponseWithIdToken() { + $request = new ServerRequest( + [], + [], + null, + 'POST', + 'php://input', + [], + [], + [], + [ + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => 'https://example.com/callback', + 'code' => 'code' + ] + ); + $claimSetRepository = new class() implements ClaimSetRepositoryInterface { public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetInterface { @@ -97,26 +118,34 @@ public function getClaims(): array } }; - $nonce = \uniqid(); - $request = new ServerRequest( - [], - [], - null, - 'POST', - 'php://input', - [], - [], - [], - [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://foo/bar', - 'code' => 'code', - 'nonce' => $nonce, - ] - ); + $IdTokenRepository = (new class() implements IdTokenRepositoryInterface + { + private $issuer; + + public function getBuilder(AccessTokenEntityInterface $accessToken): Builder + { + $builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates())) + ->permittedFor($accessToken->getClient()->getIdentifier()) + ->issuedBy($this->issuer) + ->issuedAt(new \DateTimeImmutable()) + ->expiresAt($accessToken->getExpiryDateTime()) + ->relatedTo($accessToken->getUserIdentifier()) + ->withClaim("nonce", "s6G31Kolwu9p"); + + return $builder; + } + + public function setIssuer($issuer) { + + $this->issuer = $issuer; + + return $this; + } + })->setIssuer(\sprintf('%s://%s', $request->getUri()->getScheme(), $request->getUri()->getHost())); + + - $responseType = new IdTokenResponse($request, $claimSetRepository, $claimExtrator = new ClaimExtractor()); + $responseType = new IdTokenResponse($IdTokenRepository, $claimSetRepository, $claimExtrator = new ClaimExtractor()); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); @@ -202,7 +231,7 @@ public function getClaims(): array $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); } - $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue('nonce', $nonce))); + $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue('nonce', "s6G31Kolwu9p"))); } public function testGenerateHttpResponseWithExtraParams() From 656d07e1391ba6e9fb3561e5a8f97d3164729504 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Fri, 25 Nov 2022 23:01:31 +0100 Subject: [PATCH 06/39] Fixed code quality --- examples/src/Repositories/IdTokenRepository.php | 2 +- src/IdTokenIssued.php | 1 - src/Repositories/IdTokenRepositoryInterface.php | 1 + tests/ResponseTypes/BearerResponseTypeTest.php | 17 ++++++++--------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/examples/src/Repositories/IdTokenRepository.php b/examples/src/Repositories/IdTokenRepository.php index 9b547d4c4..8fb1f7d62 100644 --- a/examples/src/Repositories/IdTokenRepository.php +++ b/examples/src/Repositories/IdTokenRepository.php @@ -32,7 +32,7 @@ public function getBuilder(AccessTokenEntityInterface $accessToken): Builder ->relatedTo($accessToken->getUserIdentifier()); if ($this->nonce) { - $builder->withClaim("nonce", $this->nonce); + $builder->withClaim('nonce', $this->nonce); } return $builder; diff --git a/src/IdTokenIssued.php b/src/IdTokenIssued.php index 425f5bf8d..74057aae6 100644 --- a/src/IdTokenIssued.php +++ b/src/IdTokenIssued.php @@ -3,7 +3,6 @@ namespace League\OAuth2\Server; use Lcobucci\JWT\Token; -use Psr\Http\Message\ServerRequestInterface; /** * An id_token has been issued diff --git a/src/Repositories/IdTokenRepositoryInterface.php b/src/Repositories/IdTokenRepositoryInterface.php index f7d1dc1df..d558c66f6 100644 --- a/src/Repositories/IdTokenRepositoryInterface.php +++ b/src/Repositories/IdTokenRepositoryInterface.php @@ -17,6 +17,7 @@ interface IdTokenRepositoryInterface * Creates new token builder and may add some standard claims * * @param AccessTokenEntityInterface $token Issued access token + * * @return Builder */ public function getBuilder(AccessTokenEntityInterface $token): Builder; diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index fc031872b..4a7331f45 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -89,7 +89,7 @@ public function testGenerateHttpResponseWithIdToken() 'grant_type' => 'authorization_code', 'client_id' => 'foo', 'redirect_uri' => 'https://example.com/callback', - 'code' => 'code' + 'code' => 'code', ] ); @@ -118,8 +118,7 @@ public function getClaims(): array } }; - $IdTokenRepository = (new class() implements IdTokenRepositoryInterface - { + $IdTokenRepository = (new class() implements IdTokenRepositoryInterface { private $issuer; public function getBuilder(AccessTokenEntityInterface $accessToken): Builder @@ -130,20 +129,20 @@ public function getBuilder(AccessTokenEntityInterface $accessToken): Builder ->issuedAt(new \DateTimeImmutable()) ->expiresAt($accessToken->getExpiryDateTime()) ->relatedTo($accessToken->getUserIdentifier()) - ->withClaim("nonce", "s6G31Kolwu9p"); + ->withClaim('nonce', 's6G31Kolwu9p'); return $builder; } - public function setIssuer($issuer) { - + public function setIssuer($issuer) + { $this->issuer = $issuer; return $this; } })->setIssuer(\sprintf('%s://%s', $request->getUri()->getScheme(), $request->getUri()->getHost())); - - + + $responseType = new IdTokenResponse($IdTokenRepository, $claimSetRepository, $claimExtrator = new ClaimExtractor()); @@ -231,7 +230,7 @@ public function setIssuer($issuer) { $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); } - $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue('nonce', "s6G31Kolwu9p"))); + $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue('nonce', 's6G31Kolwu9p'))); } public function testGenerateHttpResponseWithExtraParams() From bbe4c20ea9b3ea4ba8f1ba2e719e62656a636ade Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sat, 26 Nov 2022 00:32:04 +0100 Subject: [PATCH 07/39] Remove old ID token related const --- src/RequestEvent.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/RequestEvent.php b/src/RequestEvent.php index ddb9847b5..99b09feef 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -20,13 +20,7 @@ class RequestEvent extends Event const REFRESH_TOKEN_ISSUED = 'refresh_token.issued'; const ACCESS_TOKEN_ISSUED = 'access_token.issued'; - const ID_TOKEN_ISSUED = 'id_token.issued'; - - /** - * This event can be used to extent claims of the id_token - */ - const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created'; - + /** * @var ServerRequestInterface */ From a8c58c905cc8501b7415f28c181306e704abe9e5 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sat, 26 Nov 2022 00:34:12 +0100 Subject: [PATCH 08/39] Fixed CI issue --- src/RequestEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RequestEvent.php b/src/RequestEvent.php index 99b09feef..b1ca3f6b8 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -20,7 +20,7 @@ class RequestEvent extends Event const REFRESH_TOKEN_ISSUED = 'refresh_token.issued'; const ACCESS_TOKEN_ISSUED = 'access_token.issued'; - + /** * @var ServerRequestInterface */ From dd55a7d23119b80b8b1bed9162f9adc897916f7a Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sat, 26 Nov 2022 01:46:40 +0100 Subject: [PATCH 09/39] Fixed class name for id token events --- src/ResponseTypes/IdTokenResponse.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 0305c8114..09902d313 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -10,6 +10,7 @@ use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\IdTokenClaimsCreated; +use League\OAuth2\Server\IdTokenEvent; use League\OAuth2\Server\IdTokenIssued; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; use League\OAuth2\Server\Repositories\IdTokenRepositoryInterface; @@ -60,7 +61,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra } $this->getEmitter()->emit( - new IdTokenClaimsCreated(RequestEvent::ID_TOKEN_CLAIMS_CREATED, $builder) + new IdTokenClaimsCreated(IdTokenEvent::ID_TOKEN_CLAIMS_CREATED, $builder) ); $token = $builder->getToken( @@ -69,7 +70,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra ); $this->getEmitter()->emit( - new IdTokenIssued(RequestEvent::ID_TOKEN_ISSUED, $token) + new IdTokenIssued(IdTokenEvent::ID_TOKEN_ISSUED, $token) ); return [ From 4635e33795d89e5c2458a676ab52382ab7f17717 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sat, 26 Nov 2022 07:01:36 +0000 Subject: [PATCH 10/39] Apply fixes from StyleCI --- src/Entities/ClaimSetEntry.php | 3 +-- src/ResponseTypes/IdTokenResponse.php | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Entities/ClaimSetEntry.php b/src/Entities/ClaimSetEntry.php index 91830db94..05f48f445 100644 --- a/src/Entities/ClaimSetEntry.php +++ b/src/Entities/ClaimSetEntry.php @@ -14,8 +14,7 @@ class ClaimSetEntry implements ClaimSetEntryInterface public function __construct( protected string $scope, protected array $claims - ) - { + ) { } /** diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 09902d313..72feb054e 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -14,7 +14,6 @@ use League\OAuth2\Server\IdTokenIssued; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; use League\OAuth2\Server\Repositories\IdTokenRepositoryInterface; -use League\OAuth2\Server\RequestEvent; /** * OpenidConfigurationResponse From 8e00c4e74c9471405c76566b5b8d82e9de5161e6 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 27 Nov 2022 00:25:20 +0100 Subject: [PATCH 11/39] Fixed id token events and add ClaimExtractorIntercace --- src/ClaimExtractor.php | 9 ++------- src/ClaimExtractorIntercace.php | 18 ++++++++++++++++++ ...eated.php => IdTokenClaimsCreatedEvent.php} | 4 ++-- ...dTokenIssued.php => IdTokenIssuedEvent.php} | 2 +- src/ResponseTypes/IdTokenResponse.php | 14 +++++++++----- 5 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 src/ClaimExtractorIntercace.php rename src/{IdTokenClaimsCreated.php => IdTokenClaimsCreatedEvent.php} (78%) rename src/{IdTokenIssued.php => IdTokenIssuedEvent.php} (90%) diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index 66fc27cb6..78fc7adc1 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -14,7 +14,7 @@ * @author Steve Rhoades * @author Marc Riemer */ -class ClaimExtractor +class ClaimExtractor implements ClaimExtractorIntercace { /** * claimSets @@ -87,12 +87,7 @@ public function getClaimSets(): array } /** - * For given scopes and aggregated claims get all claims that have been configured on the extractor. - * - * @param array $scopes - * @param array $claims - * - * @return array + * {@inheritdoc} */ public function extract(array $scopes, array $claims): array { diff --git a/src/ClaimExtractorIntercace.php b/src/ClaimExtractorIntercace.php new file mode 100644 index 000000000..3a15d48b1 --- /dev/null +++ b/src/ClaimExtractorIntercace.php @@ -0,0 +1,18 @@ + */ -final class IdTokenClaimsCreated extends IdTokenEvent +final class IdTokenClaimsCreatedEvent extends IdTokenEvent { /** * Builder diff --git a/src/IdTokenIssued.php b/src/IdTokenIssuedEvent.php similarity index 90% rename from src/IdTokenIssued.php rename to src/IdTokenIssuedEvent.php index 74057aae6..ac6ea83d6 100644 --- a/src/IdTokenIssued.php +++ b/src/IdTokenIssuedEvent.php @@ -9,7 +9,7 @@ * * @author Marc Riemer */ -final class IdTokenIssued extends IdTokenEvent +final class IdTokenIssuedEvent extends IdTokenEvent { /** * Token diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 72feb054e..fd2446849 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -5,13 +5,15 @@ use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; use League\Event\EmitterAwareTrait; +use League\Event\EmitterInterface; use League\OAuth2\Server\ClaimExtractor; +use League\OAuth2\Server\ClaimExtractorIntercace; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; -use League\OAuth2\Server\IdTokenClaimsCreated; +use League\OAuth2\Server\IdTokenClaimsCreatedEvent; use League\OAuth2\Server\IdTokenEvent; -use League\OAuth2\Server\IdTokenIssued; +use League\OAuth2\Server\IdTokenIssuedEvent; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; use League\OAuth2\Server\Repositories\IdTokenRepositoryInterface; @@ -30,11 +32,13 @@ class IdTokenResponse extends BearerTokenResponse public function __construct( protected IdTokenRepositoryInterface $builder, protected ClaimSetRepositoryInterface $claimRepository, - protected ?ClaimExtractor $extractor = null + EmitterInterface $emitter, + protected ?ClaimExtractorIntercace $extractor = null ) { if (!$extractor) { $this->extractor = new ClaimExtractor(); } + $this->setEmitter($emitter); } /** @@ -60,7 +64,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra } $this->getEmitter()->emit( - new IdTokenClaimsCreated(IdTokenEvent::ID_TOKEN_CLAIMS_CREATED, $builder) + new IdTokenClaimsCreatedEvent(IdTokenEvent::ID_TOKEN_CLAIMS_CREATED, $builder) ); $token = $builder->getToken( @@ -69,7 +73,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra ); $this->getEmitter()->emit( - new IdTokenIssued(IdTokenEvent::ID_TOKEN_ISSUED, $token) + new IdTokenIssuedEvent(IdTokenEvent::ID_TOKEN_ISSUED, $token) ); return [ From 9afb766b5668256d12b9263b8010d8b1337529c0 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sat, 26 Nov 2022 23:26:47 +0000 Subject: [PATCH 12/39] Apply fixes from StyleCI --- src/ClaimExtractorIntercace.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ClaimExtractorIntercace.php b/src/ClaimExtractorIntercace.php index 3a15d48b1..dbd54a4a3 100644 --- a/src/ClaimExtractorIntercace.php +++ b/src/ClaimExtractorIntercace.php @@ -2,10 +2,9 @@ namespace League\OAuth2\Server; -interface ClaimExtractorIntercace +interface ClaimExtractorIntercace { - - /** + /** * For given scopes and aggregated claims get all claims that have been configured on the extractor. * * @param array $scopes @@ -14,5 +13,4 @@ interface ClaimExtractorIntercace * @return array */ public function extract(array $scopes, array $claims): array; - -} \ No newline at end of file +} From e7c54f3856317f579686a5da0abbe65ca300059d Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Mon, 28 Nov 2022 20:50:25 +0100 Subject: [PATCH 13/39] Add UserInfoResponse --- src/ResponseTypes/UserInfoResponse.php | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/ResponseTypes/UserInfoResponse.php diff --git a/src/ResponseTypes/UserInfoResponse.php b/src/ResponseTypes/UserInfoResponse.php new file mode 100644 index 000000000..10e7d37d5 --- /dev/null +++ b/src/ResponseTypes/UserInfoResponse.php @@ -0,0 +1,35 @@ + + */ +class UserInfoResponse extends AbstractResponseType +{ + public function __construct( + protected ClaimSetInterface $claimSet + ) { + } + + /** + * {@inheritdoc} + */ + public function generateHttpResponse(ResponseInterface $response) + { + $response = $response + ->withStatus(200) + ->withHeader('pragma', 'no-cache') + ->withHeader('cache-control', 'no-store') + ->withHeader('content-type', 'application/json; charset=UTF-8'); + + $response->getBody()->write(\json_encode($this->claimSet->getClaims())); + + return $response; + } +} From 35af257e420cda7a15185cdd7476e89ecf3e7dd1 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Mon, 28 Nov 2022 21:44:41 +0100 Subject: [PATCH 14/39] Add ServerRequestInterface to getClaimSetEntry --- src/Repositories/ClaimSetRepositoryInterface.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php index a7bca1f61..18dd955c9 100644 --- a/src/Repositories/ClaimSetRepositoryInterface.php +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -4,6 +4,7 @@ use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClaimSetInterface; +use Psr\Http\Message\ServerRequestInterface; /** * ClaimSetRepositoryInterface resolve claims for id_token. @@ -17,9 +18,11 @@ interface ClaimSetRepositoryInterface /** * Get ClaimSetEntries * - * @param AccessTokenEntityInterface $authCode + * Access AccessTokenEntityInterface and ServerRequestInterface returned by the resource server after successfull authorization + * + * @param AccessTokenEntityInterface|ServerRequestInterface $resource * * @return ClaimSetInterface */ - public function getClaimSetEntry(AccessTokenEntityInterface $authCode): ClaimSetInterface; + public function getClaimSetEntry(AccessTokenEntityInterface|ServerRequestInterface $resource): ClaimSetInterface; } From e9975a33f503ecc14e8068880e52164e96b76fe7 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 29 Nov 2022 07:55:18 +0100 Subject: [PATCH 15/39] Revert "Add ServerRequestInterface to getClaimSetEntry" This reverts commit 35af257e420cda7a15185cdd7476e89ecf3e7dd1. --- src/Repositories/ClaimSetRepositoryInterface.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php index 18dd955c9..a7bca1f61 100644 --- a/src/Repositories/ClaimSetRepositoryInterface.php +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -4,7 +4,6 @@ use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClaimSetInterface; -use Psr\Http\Message\ServerRequestInterface; /** * ClaimSetRepositoryInterface resolve claims for id_token. @@ -18,11 +17,9 @@ interface ClaimSetRepositoryInterface /** * Get ClaimSetEntries * - * Access AccessTokenEntityInterface and ServerRequestInterface returned by the resource server after successfull authorization - * - * @param AccessTokenEntityInterface|ServerRequestInterface $resource + * @param AccessTokenEntityInterface $authCode * * @return ClaimSetInterface */ - public function getClaimSetEntry(AccessTokenEntityInterface|ServerRequestInterface $resource): ClaimSetInterface; + public function getClaimSetEntry(AccessTokenEntityInterface $authCode): ClaimSetInterface; } From 5befb21def4fdbc3b77f03975e7f00bc65b54edf Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 29 Nov 2022 09:33:06 +0100 Subject: [PATCH 16/39] Fixed backwards compatibility --- src/Entities/ClaimSetEntry.php | 16 +++++++++-- src/ResponseTypes/IdTokenResponse.php | 38 ++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/Entities/ClaimSetEntry.php b/src/Entities/ClaimSetEntry.php index 05f48f445..3eab50486 100644 --- a/src/Entities/ClaimSetEntry.php +++ b/src/Entities/ClaimSetEntry.php @@ -11,10 +11,22 @@ */ class ClaimSetEntry implements ClaimSetEntryInterface { + /** + * @var string + */ + protected $scope; + + /** + * @var array + */ + protected $claims; + public function __construct( - protected string $scope, - protected array $claims + string $scope, + array $claims ) { + $this->scope = $scope; + $this->claims = $claims; } /** diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index fd2446849..99a8dc5d7 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -29,15 +29,47 @@ class IdTokenResponse extends BearerTokenResponse { use EmitterAwareTrait; + /** + * IdTokenRepositoryInterface + * + * @var IdTokenRepositoryInterface + */ + protected $builder; + + /** + * ClaimSetRepositoryInterface + * + * @var ClaimSetRepositoryInterface + */ + protected $claimRepository; + + /** + * EmitterInterface + * + * @var EmitterInterface + */ + protected $emitter; + + /** + * ClaimExtractorIntercace + * + * @var ClaimExtractorIntercace + */ + protected $extractor; + public function __construct( - protected IdTokenRepositoryInterface $builder, - protected ClaimSetRepositoryInterface $claimRepository, + IdTokenRepositoryInterface $builder, + ClaimSetRepositoryInterface $claimRepository, EmitterInterface $emitter, - protected ?ClaimExtractorIntercace $extractor = null + ?ClaimExtractorIntercace $extractor = null ) { if (!$extractor) { $this->extractor = new ClaimExtractor(); + } else { + $this->extractor = $extractor; } + $this->builder = $builder; + $this->claimRepository = $claimRepository; $this->setEmitter($emitter); } From d37193b05acc715ee90470e55d60e06c4854c1d5 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 29 Nov 2022 09:58:58 +0100 Subject: [PATCH 17/39] Fixed testGenerateHttpResponseWithIdToken test --- tests/ResponseTypes/BearerResponseTypeTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 4a7331f45..50b55e8e4 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -11,6 +11,7 @@ use Lcobucci\JWT\Encoding\ChainedFormatter; use Lcobucci\JWT\Encoding\JoseEncoder; use Lcobucci\JWT\Token\Builder; +use League\Event\EmitterInterface; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\CryptKey; @@ -142,9 +143,8 @@ public function setIssuer($issuer) } })->setIssuer(\sprintf('%s://%s', $request->getUri()->getScheme(), $request->getUri()->getHost())); - - - $responseType = new IdTokenResponse($IdTokenRepository, $claimSetRepository, $claimExtrator = new ClaimExtractor()); + $responseType = new IdTokenResponse($IdTokenRepository, $claimSetRepository, + $this->getMockBuilder(EmitterInterface::class)->getMock(), $claimExtrator = new ClaimExtractor()); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); From ffb4dc2e974cbb5f287ffd73b4927316dfa19ac0 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 29 Nov 2022 09:01:18 +0000 Subject: [PATCH 18/39] Apply fixes from StyleCI --- tests/ResponseTypes/BearerResponseTypeTest.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 50b55e8e4..c366990ff 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -143,8 +143,12 @@ public function setIssuer($issuer) } })->setIssuer(\sprintf('%s://%s', $request->getUri()->getScheme(), $request->getUri()->getHost())); - $responseType = new IdTokenResponse($IdTokenRepository, $claimSetRepository, - $this->getMockBuilder(EmitterInterface::class)->getMock(), $claimExtrator = new ClaimExtractor()); + $responseType = new IdTokenResponse( + $IdTokenRepository, + $claimSetRepository, + $this->getMockBuilder(EmitterInterface::class)->getMock(), + $claimExtrator = new ClaimExtractor() + ); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); From a3856e60d43f9cfbe675341a8f2d17fba2033f31 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 29 Nov 2022 17:16:45 +0100 Subject: [PATCH 19/39] lcobucci/jwt 3.4.6 compatibility --- src/IdTokenClaimsCreatedEvent.php | 2 +- .../IdTokenRepositoryInterface.php | 2 +- .../ResponseTypes/BearerResponseTypeTest.php | 59 +++++++++++++------ 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/IdTokenClaimsCreatedEvent.php b/src/IdTokenClaimsCreatedEvent.php index 96913a7db..c68eadb02 100644 --- a/src/IdTokenClaimsCreatedEvent.php +++ b/src/IdTokenClaimsCreatedEvent.php @@ -2,7 +2,7 @@ namespace League\OAuth2\Server; -use Lcobucci\JWT\Token\Builder; +use Lcobucci\JWT\Builder; /** * IdTokenClaimsCreatedEvent Event helps to extend claims of the id_token diff --git a/src/Repositories/IdTokenRepositoryInterface.php b/src/Repositories/IdTokenRepositoryInterface.php index d558c66f6..633288d89 100644 --- a/src/Repositories/IdTokenRepositoryInterface.php +++ b/src/Repositories/IdTokenRepositoryInterface.php @@ -2,7 +2,7 @@ namespace League\OAuth2\Server\Repositories; -use Lcobucci\JWT\Token\Builder; +use Lcobucci\JWT\Builder; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; /** diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index c366990ff..53d0d5629 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -8,9 +8,6 @@ use Laminas\Diactoros\ServerRequest; use Lcobucci\Clock\SystemClock; use Lcobucci\JWT as JWT; -use Lcobucci\JWT\Encoding\ChainedFormatter; -use Lcobucci\JWT\Encoding\JoseEncoder; -use Lcobucci\JWT\Token\Builder; use League\Event\EmitterInterface; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; use League\OAuth2\Server\ClaimExtractor; @@ -94,10 +91,10 @@ public function testGenerateHttpResponseWithIdToken() ] ); - $claimSetRepository = new class() implements ClaimSetRepositoryInterface { + $claimSetRepository = new class () implements ClaimSetRepositoryInterface { public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetInterface { - $claimSet = new class() implements ClaimSetInterface { + $claimSet = new class () implements ClaimSetInterface { public array $claims = []; public function getClaims(): array @@ -119,13 +116,21 @@ public function getClaims(): array } }; - $IdTokenRepository = (new class() implements IdTokenRepositoryInterface { + $IdTokenRepository = (new class () implements IdTokenRepositoryInterface { private $issuer; - public function getBuilder(AccessTokenEntityInterface $accessToken): Builder + public function getBuilder(AccessTokenEntityInterface $accessToken): JWT\Builder { - $builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates())) - ->permittedFor($accessToken->getClient()->getIdentifier()) + if (class_exists("\Lcobucci\JWT\Encoding\JoseEncoder")) { + $builder = (new JWT\Token\Builder( + new \Lcobucci\JWT\Encoding\JoseEncoder(), + \Lcobucci\JWT\Encoding\ChainedFormatter::withUnixTimestampDates() + )); + } else { + $builder = (new JWT\Builder(new \Lcobucci\JWT\Parsing\Encoder(), new \Lcobucci\JWT\Claim\Factory())); + } + + $builder->permittedFor($accessToken->getClient()->getIdentifier()) ->issuedBy($this->issuer) ->issuedAt(new \DateTimeImmutable()) ->expiresAt($accessToken->getExpiryDateTime()) @@ -202,7 +207,12 @@ public function setIssuer($issuer) $this->assertObjectHasAttribute('id_token', $json); - $token = (new JWT\Token\Parser(new JWT\Encoding\JoseEncoder()))->parse($json->id_token); + if (class_exists("\Lcobucci\JWT\Token\Parser")) { + $token = (new \Lcobucci\JWT\Token\Parser(new \Lcobucci\JWT\Encoding\JoseEncoder()))->parse($json->id_token); + } else { + $token = (new \Lcobucci\JWT\Parser())->parse($json->id_token); + } + $validator = new JWT\Validation\Validator(); $this->assertTrue($validator->validate( @@ -225,16 +235,29 @@ public function setIssuer($issuer) new JWT\Validation\Constraint\RelatedTo($accessToken->getUserIdentifier()) )); - $this->assertTrue($validator->validate( - $token, - new JWT\Validation\Constraint\LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) - )); - - foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { - $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); + if (class_exists("\Lcobucci\JWT\Validation\Constraint\LooseValidAt")) { + $this->assertTrue($validator->validate( + $token, + new \Lcobucci\JWT\Validation\Constraint\LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) + )); + } else { + $this->assertTrue($validator->validate( + $token, + new \Lcobucci\JWT\Validation\Constraint\ValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) + )); } - $this->assertTrue($validator->validate($token, new JWT\Validation\Constraint\HasClaimWithValue('nonce', 's6G31Kolwu9p'))); + if (class_exists("\Lcobucci\JWT\Validation\Constraint\HasClaimWithValue")) { + foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { + $this->assertTrue($validator->validate($token, new \Lcobucci\JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); + } + $this->assertTrue($validator->validate($token, new \Lcobucci\JWT\Validation\Constraint\HasClaimWithValue('nonce', 's6G31Kolwu9p'))); + } else { + foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { + $this->assertTrue(array_key_exists($claim, $token->getClaims())); + $this->assertEquals($value, $token->getClaims()[$claim]); + } + } } public function testGenerateHttpResponseWithExtraParams() From a3cb121ca6030b20fc1ce3c48980d511b383688d Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 29 Nov 2022 16:18:02 +0000 Subject: [PATCH 20/39] Apply fixes from StyleCI --- tests/ResponseTypes/BearerResponseTypeTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 53d0d5629..8737ec11c 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -91,10 +91,10 @@ public function testGenerateHttpResponseWithIdToken() ] ); - $claimSetRepository = new class () implements ClaimSetRepositoryInterface { + $claimSetRepository = new class() implements ClaimSetRepositoryInterface { public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetInterface { - $claimSet = new class () implements ClaimSetInterface { + $claimSet = new class() implements ClaimSetInterface { public array $claims = []; public function getClaims(): array @@ -116,12 +116,12 @@ public function getClaims(): array } }; - $IdTokenRepository = (new class () implements IdTokenRepositoryInterface { + $IdTokenRepository = (new class() implements IdTokenRepositoryInterface { private $issuer; public function getBuilder(AccessTokenEntityInterface $accessToken): JWT\Builder { - if (class_exists("\Lcobucci\JWT\Encoding\JoseEncoder")) { + if (\class_exists("\Lcobucci\JWT\Encoding\JoseEncoder")) { $builder = (new JWT\Token\Builder( new \Lcobucci\JWT\Encoding\JoseEncoder(), \Lcobucci\JWT\Encoding\ChainedFormatter::withUnixTimestampDates() @@ -207,7 +207,7 @@ public function setIssuer($issuer) $this->assertObjectHasAttribute('id_token', $json); - if (class_exists("\Lcobucci\JWT\Token\Parser")) { + if (\class_exists("\Lcobucci\JWT\Token\Parser")) { $token = (new \Lcobucci\JWT\Token\Parser(new \Lcobucci\JWT\Encoding\JoseEncoder()))->parse($json->id_token); } else { $token = (new \Lcobucci\JWT\Parser())->parse($json->id_token); @@ -235,7 +235,7 @@ public function setIssuer($issuer) new JWT\Validation\Constraint\RelatedTo($accessToken->getUserIdentifier()) )); - if (class_exists("\Lcobucci\JWT\Validation\Constraint\LooseValidAt")) { + if (\class_exists("\Lcobucci\JWT\Validation\Constraint\LooseValidAt")) { $this->assertTrue($validator->validate( $token, new \Lcobucci\JWT\Validation\Constraint\LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) @@ -247,14 +247,14 @@ public function setIssuer($issuer) )); } - if (class_exists("\Lcobucci\JWT\Validation\Constraint\HasClaimWithValue")) { + if (\class_exists("\Lcobucci\JWT\Validation\Constraint\HasClaimWithValue")) { foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { $this->assertTrue($validator->validate($token, new \Lcobucci\JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); } $this->assertTrue($validator->validate($token, new \Lcobucci\JWT\Validation\Constraint\HasClaimWithValue('nonce', 's6G31Kolwu9p'))); } else { foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { - $this->assertTrue(array_key_exists($claim, $token->getClaims())); + $this->assertTrue(\array_key_exists($claim, $token->getClaims())); $this->assertEquals($value, $token->getClaims()[$claim]); } } From 68e1ca8b9dea21e0820c540cd2498cc0f91a05a3 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Wed, 12 Apr 2023 16:29:07 +0200 Subject: [PATCH 21/39] Add openid default claims --- src/ClaimExtractor.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index 78fc7adc1..3799ac174 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -37,7 +37,7 @@ public function __construct(array $claimSets = []) $this->addClaimSet($claimSet); } } - + /** * @param ClaimSetEntryInterface $claimSetEntry * @@ -159,6 +159,13 @@ public static function getDefaultClaimSetEnties(): array 'phone_number', 'phone_number_verified', ]), + new ClaimSetEntry('openid', [ + 'nonce', + 'auth_time', + 'acr', + 'amr', + 'azp' + ]), ]; } } From f22eb580856a2cb5c8c24b5a0d70b893997e4ed3 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Wed, 12 Apr 2023 14:30:45 +0000 Subject: [PATCH 22/39] Apply fixes from StyleCI --- examples/public/client_credentials.php | 4 ---- examples/public/password.php | 5 ----- src/ClaimExtractor.php | 4 ++-- src/Entities/Traits/ClientTrait.php | 1 + src/RequestAccessTokenEvent.php | 1 + src/RequestEvent.php | 1 + src/RequestRefreshTokenEvent.php | 1 + 7 files changed, 6 insertions(+), 11 deletions(-) diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php index 51a1ca0b7..1e5f090d7 100644 --- a/examples/public/client_credentials.php +++ b/examples/public/client_credentials.php @@ -53,20 +53,16 @@ ]); $app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { - /* @var \League\OAuth2\Server\AuthorizationServer $server */ $server = $app->getContainer()->get(AuthorizationServer::class); try { - // Try to respond to the request return $server->respondToAccessTokenRequest($request, $response); } catch (OAuthServerException $exception) { - // All instances of OAuthServerException can be formatted into a HTTP response return $exception->generateHttpResponse($response); } catch (\Exception $exception) { - // Unknown exception $body = new Stream('php://temp', 'r+'); $body->write($exception->getMessage()); diff --git a/examples/public/password.php b/examples/public/password.php index 6857e988a..db65d7840 100644 --- a/examples/public/password.php +++ b/examples/public/password.php @@ -17,7 +17,6 @@ $app = new App([ // Add the authorization server to the DI container AuthorizationServer::class => function () { - // Setup the authorization server $server = new AuthorizationServer( new ClientRepository(), // instance of ClientRepositoryInterface @@ -46,20 +45,16 @@ $app->post( '/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { - /* @var \League\OAuth2\Server\AuthorizationServer $server */ $server = $app->getContainer()->get(AuthorizationServer::class); try { - // Try to respond to the access token request return $server->respondToAccessTokenRequest($request, $response); } catch (OAuthServerException $exception) { - // All instances of OAuthServerException can be converted to a PSR-7 response return $exception->generateHttpResponse($response); } catch (\Exception $exception) { - // Catch unexpected exceptions $body = $response->getBody(); $body->write($exception->getMessage()); diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index 3799ac174..b38b426ff 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -37,7 +37,7 @@ public function __construct(array $claimSets = []) $this->addClaimSet($claimSet); } } - + /** * @param ClaimSetEntryInterface $claimSetEntry * @@ -164,7 +164,7 @@ public static function getDefaultClaimSetEnties(): array 'auth_time', 'acr', 'amr', - 'azp' + 'azp', ]), ]; } diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php index a0078d8d7..370163c35 100644 --- a/src/Entities/Traits/ClientTrait.php +++ b/src/Entities/Traits/ClientTrait.php @@ -30,6 +30,7 @@ trait ClientTrait * Get the client's name. * * @return string + * * @codeCoverageIgnore */ public function getName() diff --git a/src/RequestAccessTokenEvent.php b/src/RequestAccessTokenEvent.php index 99d17bf36..c2f478284 100644 --- a/src/RequestAccessTokenEvent.php +++ b/src/RequestAccessTokenEvent.php @@ -31,6 +31,7 @@ public function __construct($name, ServerRequestInterface $request, AccessTokenE /** * @return AccessTokenEntityInterface + * * @codeCoverageIgnore */ public function getAccessToken() diff --git a/src/RequestEvent.php b/src/RequestEvent.php index b1ca3f6b8..4f7dad097 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -40,6 +40,7 @@ public function __construct($name, ServerRequestInterface $request) /** * @return ServerRequestInterface + * * @codeCoverageIgnore */ public function getRequest() diff --git a/src/RequestRefreshTokenEvent.php b/src/RequestRefreshTokenEvent.php index 0682e57f5..326a115ed 100644 --- a/src/RequestRefreshTokenEvent.php +++ b/src/RequestRefreshTokenEvent.php @@ -31,6 +31,7 @@ public function __construct($name, ServerRequestInterface $request, RefreshToken /** * @return RefreshTokenEntityInterface + * * @codeCoverageIgnore */ public function getRefreshToken() From ef616e4306294172067f4a1447de9737bc4f8c2c Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Wed, 6 Sep 2023 18:57:24 +0200 Subject: [PATCH 23/39] Fixed interface name of ClaimExtractorInterface --- src/ClaimExtractor.php | 2 +- ...ExtractorIntercace.php => ClaimExtractorInterface.php} | 2 +- src/ResponseTypes/IdTokenResponse.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/{ClaimExtractorIntercace.php => ClaimExtractorInterface.php} (90%) diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index b38b426ff..c3bf1adfd 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -14,7 +14,7 @@ * @author Steve Rhoades * @author Marc Riemer */ -class ClaimExtractor implements ClaimExtractorIntercace +class ClaimExtractor implements ClaimExtractorInterface { /** * claimSets diff --git a/src/ClaimExtractorIntercace.php b/src/ClaimExtractorInterface.php similarity index 90% rename from src/ClaimExtractorIntercace.php rename to src/ClaimExtractorInterface.php index dbd54a4a3..b8fccc4fb 100644 --- a/src/ClaimExtractorIntercace.php +++ b/src/ClaimExtractorInterface.php @@ -2,7 +2,7 @@ namespace League\OAuth2\Server; -interface ClaimExtractorIntercace +interface ClaimExtractorInterface { /** * For given scopes and aggregated claims get all claims that have been configured on the extractor. diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 99a8dc5d7..607e8e4ef 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -7,7 +7,7 @@ use League\Event\EmitterAwareTrait; use League\Event\EmitterInterface; use League\OAuth2\Server\ClaimExtractor; -use League\OAuth2\Server\ClaimExtractorIntercace; +use League\OAuth2\Server\ClaimExtractorInterface; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; @@ -51,9 +51,9 @@ class IdTokenResponse extends BearerTokenResponse protected $emitter; /** - * ClaimExtractorIntercace + * ClaimExtractorInterface * - * @var ClaimExtractorIntercace + * @var ClaimExtractorInterface */ protected $extractor; @@ -61,7 +61,7 @@ public function __construct( IdTokenRepositoryInterface $builder, ClaimSetRepositoryInterface $claimRepository, EmitterInterface $emitter, - ?ClaimExtractorIntercace $extractor = null + ?ClaimExtractorInterface $extractor = null ) { if (!$extractor) { $this->extractor = new ClaimExtractor(); From 5f1db1e4cc10e6ac566b888f6723b60c11b1576c Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Thu, 7 Sep 2023 11:53:38 +0200 Subject: [PATCH 24/39] Renamed parameter --- src/ResponseTypes/IdTokenResponse.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 607e8e4ef..668c0ab15 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -34,7 +34,7 @@ class IdTokenResponse extends BearerTokenResponse * * @var IdTokenRepositoryInterface */ - protected $builder; + protected $idTokenRepository; /** * ClaimSetRepositoryInterface @@ -58,7 +58,7 @@ class IdTokenResponse extends BearerTokenResponse protected $extractor; public function __construct( - IdTokenRepositoryInterface $builder, + IdTokenRepositoryInterface $idTokenRepository, ClaimSetRepositoryInterface $claimRepository, EmitterInterface $emitter, ?ClaimExtractorInterface $extractor = null @@ -68,7 +68,7 @@ public function __construct( } else { $this->extractor = $extractor; } - $this->builder = $builder; + $this->idTokenRepository = $idTokenRepository; $this->claimRepository = $claimRepository; $this->setEmitter($emitter); } @@ -87,7 +87,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra $claimSet = $this->claimRepository->getClaimSetEntry($accessToken); - $builder = $this->builder->getBuilder($accessToken); + $builder = $this->idTokenRepository->getBuilder($accessToken); if ($claimSet instanceof ClaimSetInterface) { foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims()) as $claimName => $claimValue) { From 7bc34f70feafe2b3600de7a074a0c17c28a7339c Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Thu, 26 Oct 2023 16:25:56 +0200 Subject: [PATCH 25/39] Fixed ClaimExtractor --- src/ClaimExtractor.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index c3bf1adfd..debbfaa4f 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -47,15 +47,13 @@ public function __construct(array $claimSets = []) */ public function addClaimSet(ClaimSetEntryInterface $claimSetEntry): ClaimExtractor { - $scope = $claimSetEntry->getScope(); - - if (\in_array($scope, $this->protectedClaims) && !empty($this->claimSets[$scope])) { + if (\in_array($claimSetEntry->getScope(), $this->protectedClaims) && !$this->getClaimSet($claimSetEntry->getScope())) { throw new \InvalidArgumentException( - \sprintf('%s is a protected scope and is pre-defined by the OpenID Connect specification.', $scope) + \sprintf('%s is a protected scope and is pre-defined by the OpenID Connect specification.', $claimSetEntry->getScope()) ); } - $this->claimSets[$scope] = $claimSetEntry->getClaims(); + $this->claimSets[] = $claimSetEntry; return $this; } From 2e710f985f7b70c874c25e353bb0cbdb5ff52b89 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Thu, 28 Mar 2024 10:03:40 +0100 Subject: [PATCH 26/39] Changed ClaimSetInterface into ClaimSetEntryInterface --- src/Repositories/ClaimSetRepositoryInterface.php | 6 +++--- src/ResponseTypes/IdTokenResponse.php | 4 ++-- tests/ResponseTypes/BearerResponseTypeTest.php | 14 +++++++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php index a7bca1f61..3e0e979e3 100644 --- a/src/Repositories/ClaimSetRepositoryInterface.php +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -3,7 +3,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\Entities\ClaimSetInterface; +use League\OAuth2\Server\Entities\ClaimSetEntryInterface; /** * ClaimSetRepositoryInterface resolve claims for id_token. @@ -19,7 +19,7 @@ interface ClaimSetRepositoryInterface * * @param AccessTokenEntityInterface $authCode * - * @return ClaimSetInterface + * @return ClaimSetEntryInterface */ - public function getClaimSetEntry(AccessTokenEntityInterface $authCode): ClaimSetInterface; + public function getClaimSetEntry(AccessTokenEntityInterface $authCode): ClaimSetEntryInterface; } diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 668c0ab15..5ff636beb 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -9,7 +9,7 @@ use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\ClaimExtractorInterface; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\Entities\ClaimSetInterface; +use League\OAuth2\Server\Entities\ClaimSetEntryInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\IdTokenClaimsCreatedEvent; use League\OAuth2\Server\IdTokenEvent; @@ -89,7 +89,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra $builder = $this->idTokenRepository->getBuilder($accessToken); - if ($claimSet instanceof ClaimSetInterface) { + if ($claimSet instanceof ClaimSetEntryInterface) { foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims()) as $claimName => $claimValue) { $builder->withClaim($claimName, $claimValue); } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 8737ec11c..82cc35c9c 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -13,7 +13,7 @@ use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\Entities\ClaimSetInterface; +use League\OAuth2\Server\Entities\ClaimSetEntryInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; @@ -92,11 +92,19 @@ public function testGenerateHttpResponseWithIdToken() ); $claimSetRepository = new class() implements ClaimSetRepositoryInterface { - public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetInterface + public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetEntryInterface { - $claimSet = new class() implements ClaimSetInterface { + $claimSet = new class() implements ClaimSetEntryInterface { + + public string $scope = "email"; + public array $claims = []; + public function getScope(): string + { + return $this->scope; + } + public function getClaims(): array { return $this->claims; From f3e199057d779057b5e0aeae5bf1efdf0be51c5b Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 19 May 2024 18:33:23 +0200 Subject: [PATCH 27/39] solve conflicts --- .../ResponseTypes/BearerResponseTypeTest.php | 491 +++++++++--------- 1 file changed, 244 insertions(+), 247 deletions(-) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 82cc35c9c..b9297c652 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -1,39 +1,36 @@ setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -47,6 +44,7 @@ public function testGenerateHttpResponse() $accessToken->setClient($client); $accessToken->addScope($scope); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $accessToken->setUserIdentifier('userId'); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -58,18 +56,241 @@ public function testGenerateHttpResponse() $response = $responseType->generateHttpResponse(new Response()); - $this->assertInstanceOf(ResponseInterface::class, $response); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('no-cache', $response->getHeader('pragma')[0]); - $this->assertEquals('no-store', $response->getHeader('cache-control')[0]); - $this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); + self::assertEquals(200, $response->getStatusCode()); + self::assertEquals('no-cache', $response->getHeader('pragma')[0]); + self::assertEquals('no-store', $response->getHeader('cache-control')[0]); + self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); $response->getBody()->rewind(); - $json = \json_decode($response->getBody()->getContents()); - $this->assertEquals('Bearer', $json->token_type); - $this->assertObjectHasAttribute('expires_in', $json); - $this->assertObjectHasAttribute('access_token', $json); - $this->assertObjectHasAttribute('refresh_token', $json); + $json = json_decode($response->getBody()->getContents()); + self::assertEquals('Bearer', $json->token_type); + self::assertObjectHasProperty('expires_in', $json); + self::assertObjectHasProperty('access_token', $json); + self::assertObjectHasProperty('refresh_token', $json); + } + + public function testGenerateHttpResponseWithExtraParams(): void + { + $responseType = new BearerTokenResponseWithParams(); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $scope = new ScopeEntity(); + $scope->setIdentifier('basic'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + $accessToken->setClient($client); + $accessToken->addScope($scope); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $accessToken->setUserIdentifier('userId'); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + + self::assertEquals(200, $response->getStatusCode()); + self::assertEquals('no-cache', $response->getHeader('pragma')[0]); + self::assertEquals('no-store', $response->getHeader('cache-control')[0]); + self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); + + $response->getBody()->rewind(); + $json = json_decode($response->getBody()->getContents()); + self::assertEquals('Bearer', $json->token_type); + self::assertObjectHasProperty('expires_in', $json); + self::assertObjectHasProperty('access_token', $json); + self::assertObjectHasProperty('refresh_token', $json); + + self::assertObjectHasProperty('foo', $json); + self::assertEquals('bar', $json->foo); + } + + public function testDetermineAccessTokenInHeaderValidToken(): void + { + $responseType = new BearerTokenResponse(); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setUserIdentifier('123'); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + $json = json_decode((string) $response->getBody()); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + + $request = $authorizationValidator->validateAuthorization($request); + + self::assertEquals('abcdef', $request->getAttribute('oauth_access_token_id')); + self::assertEquals('clientName', $request->getAttribute('oauth_client_id')); + self::assertEquals('123', $request->getAttribute('oauth_user_id')); + self::assertEquals([], $request->getAttribute('oauth_scopes')); + } + + public function testDetermineAccessTokenInHeaderInvalidJWT(): void + { + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $responseType = new BearerTokenResponse(); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setUserIdentifier('123'); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->sub(new DateInterval('PT1H'))); + $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + $json = json_decode((string) $response->getBody()); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + + try { + $authorizationValidator->validateAuthorization($request); + } catch (OAuthServerException $e) { + self::assertEquals( + 'Access token could not be verified', + $e->getHint() + ); + } + } + + public function testDetermineAccessTokenInHeaderRevokedToken(): void + { + $responseType = new BearerTokenResponse(); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + + $client = new ClientEntity(); + $client->setIdentifier('clientName'); + + $accessToken = new AccessTokenEntity(); + $accessToken->setIdentifier('abcdef'); + $accessToken->setUserIdentifier('123'); + $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + + $refreshToken = new RefreshTokenEntity(); + $refreshToken->setIdentifier('abcdef'); + $refreshToken->setAccessToken($accessToken); + $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); + + $responseType->setAccessToken($accessToken); + $responseType->setRefreshToken($refreshToken); + + $response = $responseType->generateHttpResponse(new Response()); + $json = json_decode((string) $response->getBody()); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + + try { + $authorizationValidator->validateAuthorization($request); + } catch (OAuthServerException $e) { + self::assertEquals( + 'Access token has been revoked', + $e->getHint() + ); + } + } + + public function testDetermineAccessTokenInHeaderInvalidToken(): void + { + $responseType = new BearerTokenResponse(); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = (new ServerRequest())->withHeader('authorization', 'Bearer blah'); + + try { + $authorizationValidator->validateAuthorization($request); + } catch (OAuthServerException $e) { + self::assertEquals( + 'The JWT string must have two dots', + $e->getHint() + ); + } + } + + public function testDetermineMissingBearerInHeader(): void + { + $responseType = new BearerTokenResponse(); + $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + + $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); + + $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); + $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + + $request = (new ServerRequest())->withHeader('authorization', 'Bearer blah.blah.blah'); + + try { + $authorizationValidator->validateAuthorization($request); + } catch (OAuthServerException $e) { + self::assertEquals( + 'Error while decoding from JSON', + $e->getHint() + ); + } } public function testGenerateHttpResponseWithIdToken() @@ -267,228 +488,4 @@ public function setIssuer($issuer) } } } - - public function testGenerateHttpResponseWithExtraParams() - { - $responseType = new BearerTokenResponseWithParams(); - $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); - - $client = new ClientEntity(); - $client->setIdentifier('clientName'); - - $scope = new ScopeEntity(); - $scope->setIdentifier('basic'); - - $accessToken = new AccessTokenEntity(); - $accessToken->setIdentifier('abcdef'); - $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); - $accessToken->setClient($client); - $accessToken->addScope($scope); - $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - - $refreshToken = new RefreshTokenEntity(); - $refreshToken->setIdentifier('abcdef'); - $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); - - $responseType->setAccessToken($accessToken); - $responseType->setRefreshToken($refreshToken); - - $response = $responseType->generateHttpResponse(new Response()); - - $this->assertInstanceOf(ResponseInterface::class, $response); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('no-cache', $response->getHeader('pragma')[0]); - $this->assertEquals('no-store', $response->getHeader('cache-control')[0]); - $this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); - - $response->getBody()->rewind(); - $json = \json_decode($response->getBody()->getContents()); - $this->assertEquals('Bearer', $json->token_type); - $this->assertObjectHasAttribute('expires_in', $json); - $this->assertObjectHasAttribute('access_token', $json); - $this->assertObjectHasAttribute('refresh_token', $json); - - $this->assertObjectHasAttribute('foo', $json); - $this->assertEquals('bar', $json->foo); - } - - public function testDetermineAccessTokenInHeaderValidToken() - { - $responseType = new BearerTokenResponse(); - $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); - - $client = new ClientEntity(); - $client->setIdentifier('clientName'); - - $accessToken = new AccessTokenEntity(); - $accessToken->setIdentifier('abcdef'); - $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); - $accessToken->setClient($client); - $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - - $refreshToken = new RefreshTokenEntity(); - $refreshToken->setIdentifier('abcdef'); - $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); - - $responseType->setAccessToken($accessToken); - $responseType->setRefreshToken($refreshToken); - - $response = $responseType->generateHttpResponse(new Response()); - $json = \json_decode((string) $response->getBody()); - - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); - - $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); - - $request = $authorizationValidator->validateAuthorization($request); - - $this->assertEquals('abcdef', $request->getAttribute('oauth_access_token_id')); - $this->assertEquals('clientName', $request->getAttribute('oauth_client_id')); - $this->assertEquals('123', $request->getAttribute('oauth_user_id')); - $this->assertEquals([], $request->getAttribute('oauth_scopes')); - } - - public function testDetermineAccessTokenInHeaderInvalidJWT() - { - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - - $responseType = new BearerTokenResponse(); - $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); - - $client = new ClientEntity(); - $client->setIdentifier('clientName'); - - $accessToken = new AccessTokenEntity(); - $accessToken->setIdentifier('abcdef'); - $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new DateTimeImmutable())->sub(new DateInterval('PT1H'))); - $accessToken->setClient($client); - $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - - $refreshToken = new RefreshTokenEntity(); - $refreshToken->setIdentifier('abcdef'); - $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); - - $responseType->setAccessToken($accessToken); - $responseType->setRefreshToken($refreshToken); - - $response = $responseType->generateHttpResponse(new Response()); - $json = \json_decode((string) $response->getBody()); - - $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); - - try { - $authorizationValidator->validateAuthorization($request); - } catch (OAuthServerException $e) { - $this->assertEquals( - 'Access token could not be verified', - $e->getHint() - ); - } - } - - public function testDetermineAccessTokenInHeaderRevokedToken() - { - $responseType = new BearerTokenResponse(); - $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); - - $client = new ClientEntity(); - $client->setIdentifier('clientName'); - - $accessToken = new AccessTokenEntity(); - $accessToken->setIdentifier('abcdef'); - $accessToken->setUserIdentifier(123); - $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); - $accessToken->setClient($client); - $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - - $refreshToken = new RefreshTokenEntity(); - $refreshToken->setIdentifier('abcdef'); - $refreshToken->setAccessToken($accessToken); - $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); - - $responseType->setAccessToken($accessToken); - $responseType->setRefreshToken($refreshToken); - - $response = $responseType->generateHttpResponse(new Response()); - $json = \json_decode((string) $response->getBody()); - - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true); - - $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); - - try { - $authorizationValidator->validateAuthorization($request); - } catch (OAuthServerException $e) { - $this->assertEquals( - 'Access token has been revoked', - $e->getHint() - ); - } - } - - public function testDetermineAccessTokenInHeaderInvalidToken() - { - $responseType = new BearerTokenResponse(); - $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); - - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - - $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - - $request = (new ServerRequest())->withHeader('authorization', 'Bearer blah'); - - try { - $authorizationValidator->validateAuthorization($request); - } catch (OAuthServerException $e) { - $this->assertEquals( - 'The JWT string must have two dots', - $e->getHint() - ); - } - } - - public function testDetermineMissingBearerInHeader() - { - $responseType = new BearerTokenResponse(); - $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); - - $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); - - $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - - $request = (new ServerRequest())->withHeader('authorization', 'Bearer blah.blah.blah'); - - try { - $authorizationValidator->validateAuthorization($request); - } catch (OAuthServerException $e) { - $this->assertEquals( - 'Error while decoding from JSON', - $e->getHint() - ); - } - } -} +} \ No newline at end of file From 0170cf206d78bd2c33f5a6fb2041cd8fa28ad4cd Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 19 May 2024 16:34:49 +0000 Subject: [PATCH 28/39] Apply fixes from StyleCI --- examples/public/api.php | 12 +- examples/public/auth_code.php | 2 +- examples/public/client_credentials.php | 2 +- examples/public/implicit.php | 2 +- examples/public/middleware_use.php | 4 +- examples/public/refresh_token.php | 2 +- .../src/Repositories/ClientRepository.php | 6 +- src/ClaimExtractor.php | 2 +- src/Exception/OAuthServerException.php | 2 +- src/Grant/AuthCodeGrant.php | 16 +- src/Grant/ImplicitGrant.php | 6 +- src/ResponseTypes/BearerTokenResponse.php | 14 +- tests/AuthorizationServerTest.php | 6 +- tests/Grant/AbstractGrantTest.php | 16 +- tests/Grant/AuthCodeGrantTest.php | 446 +++++++++--------- tests/Grant/ClientCredentialsGrantTest.php | 2 +- tests/Grant/ImplicitGrantTest.php | 26 +- tests/Grant/PasswordGrantTest.php | 18 +- tests/Grant/RefreshTokenGrantTest.php | 124 ++--- .../ResponseTypes/BearerResponseTypeTest.php | 46 +- 20 files changed, 374 insertions(+), 380 deletions(-) diff --git a/examples/public/api.php b/examples/public/api.php index 1fa9d82dc..66b0f7c41 100644 --- a/examples/public/api.php +++ b/examples/public/api.php @@ -33,18 +33,18 @@ function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { $users = [ [ - 'id' => 123, - 'name' => 'Alex', + 'id' => 123, + 'name' => 'Alex', 'email' => 'alex@thephpleague.com', ], [ - 'id' => 124, - 'name' => 'Frank', + 'id' => 124, + 'name' => 'Frank', 'email' => 'frank@thephpleague.com', ], [ - 'id' => 125, - 'name' => 'Phil', + 'id' => 125, + 'name' => 'Phil', 'email' => 'phil@thephpleague.com', ], ]; diff --git a/examples/public/auth_code.php b/examples/public/auth_code.php index c082e3b3f..4d41e4b7a 100644 --- a/examples/public/auth_code.php +++ b/examples/public/auth_code.php @@ -24,7 +24,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php index 1e5f090d7..089a336c5 100644 --- a/examples/public/client_credentials.php +++ b/examples/public/client_credentials.php @@ -20,7 +20,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/public/implicit.php b/examples/public/implicit.php index ac43f5dd1..e74f0e337 100644 --- a/examples/public/implicit.php +++ b/examples/public/implicit.php @@ -22,7 +22,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php index 9f958ed26..3e743f778 100644 --- a/examples/public/middleware_use.php +++ b/examples/public/middleware_use.php @@ -26,7 +26,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { @@ -89,7 +89,7 @@ if (\in_array('basic', $request->getAttribute('oauth_scopes', []))) { $params = [ - 'id' => 1, + 'id' => 1, 'name' => 'Alex', 'city' => 'London', ]; diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php index 39be08262..d76d421db 100644 --- a/examples/public/refresh_token.php +++ b/examples/public/refresh_token.php @@ -21,7 +21,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php index 3a398f4ed..1d6107c6e 100644 --- a/examples/src/Repositories/ClientRepository.php +++ b/examples/src/Repositories/ClientRepository.php @@ -39,9 +39,9 @@ public function validateClient($clientIdentifier, $clientSecret, $grantType) { $clients = [ 'myawesomeapp' => [ - 'secret' => \password_hash('abc123', PASSWORD_BCRYPT), - 'name' => self::CLIENT_NAME, - 'redirect_uri' => self::REDIRECT_URI, + 'secret' => \password_hash('abc123', PASSWORD_BCRYPT), + 'name' => self::CLIENT_NAME, + 'redirect_uri' => self::REDIRECT_URI, 'is_confidential' => true, ], ]; diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index debbfaa4f..b5cce7bdb 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -89,7 +89,7 @@ public function getClaimSets(): array */ public function extract(array $scopes, array $claims): array { - $claimData = []; + $claimData = []; $keys = \array_keys($claims); foreach ($scopes as $scope) { diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index a0be0a5dd..a5bb549e7 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -65,7 +65,7 @@ public function __construct($message, $code, $errorType, $httpStatusCode = 400, $this->hint = $hint; $this->redirectUri = $redirectUri; $this->payload = [ - 'error' => $errorType, + 'error' => $errorType, 'error_description' => $message, ]; if ($hint !== null) { diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 8336cf649..9d6e63dd2 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -366,13 +366,13 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization ); $payload = [ - 'client_id' => $authCode->getClient()->getIdentifier(), - 'redirect_uri' => $authCode->getRedirectUri(), - 'auth_code_id' => $authCode->getIdentifier(), - 'scopes' => $authCode->getScopes(), - 'user_id' => $authCode->getUserIdentifier(), - 'expire_time' => (new DateTimeImmutable())->add($this->authCodeTTL)->getTimestamp(), - 'code_challenge' => $authorizationRequest->getCodeChallenge(), + 'client_id' => $authCode->getClient()->getIdentifier(), + 'redirect_uri' => $authCode->getRedirectUri(), + 'auth_code_id' => $authCode->getIdentifier(), + 'scopes' => $authCode->getScopes(), + 'user_id' => $authCode->getUserIdentifier(), + 'expire_time' => (new DateTimeImmutable())->add($this->authCodeTTL)->getTimestamp(), + 'code_challenge' => $authorizationRequest->getCodeChallenge(), 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), ]; @@ -387,7 +387,7 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization $this->makeRedirectUri( $finalRedirectUri, [ - 'code' => $this->encrypt($jsonPayload), + 'code' => $this->encrypt($jsonPayload), 'state' => $authorizationRequest->getState(), ] ) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 0bd91d5ac..0229dd9ee 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -207,9 +207,9 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization $finalRedirectUri, [ 'access_token' => (string) $accessToken, - 'token_type' => 'Bearer', - 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(), - 'state' => $authorizationRequest->getState(), + 'token_type' => 'Bearer', + 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(), + 'state' => $authorizationRequest->getState(), ], $this->queryDelimiter ) diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index 33c1606e8..afafcf35a 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -26,19 +26,19 @@ public function generateHttpResponse(ResponseInterface $response) $expireDateTime = $this->accessToken->getExpiryDateTime()->getTimestamp(); $responseParams = [ - 'token_type' => 'Bearer', - 'expires_in' => $expireDateTime - \time(), + 'token_type' => 'Bearer', + 'expires_in' => $expireDateTime - \time(), 'access_token' => (string) $this->accessToken, ]; if ($this->refreshToken instanceof RefreshTokenEntityInterface) { $refreshTokenPayload = \json_encode([ - 'client_id' => $this->accessToken->getClient()->getIdentifier(), + 'client_id' => $this->accessToken->getClient()->getIdentifier(), 'refresh_token_id' => $this->refreshToken->getIdentifier(), - 'access_token_id' => $this->accessToken->getIdentifier(), - 'scopes' => $this->accessToken->getScopes(), - 'user_id' => $this->accessToken->getUserIdentifier(), - 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(), + 'access_token_id' => $this->accessToken->getIdentifier(), + 'scopes' => $this->accessToken->getScopes(), + 'user_id' => $this->accessToken->getUserIdentifier(), + 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(), ]); if ($refreshTokenPayload === false) { diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index af8c89d8a..dfdafbe9f 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -301,7 +301,7 @@ public function testValidateAuthorizationRequest() $cookies = [], $queryParams = [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -342,7 +342,7 @@ public function testValidateAuthorizationRequestWithMissingRedirectUri() $cookies = [], $queryParams = [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -369,7 +369,7 @@ public function testValidateAuthorizationRequestUnregistered() 'client_id' => 'foo', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(2); $server->validateAuthorizationRequest($request); diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 618545efe..e3a312a35 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -109,7 +109,7 @@ public function testGetClientCredentialsClientSecretNotAString() [], [], [ - 'client_id' => 'client_id', + 'client_id' => 'client_id', 'client_secret' => ['not', 'a', 'string'], ] ); @@ -161,9 +161,9 @@ public function testValidateClientConfidential() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => 'http://foo/bar', ]); $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); @@ -229,7 +229,7 @@ public function testValidateClientInvalidClientSecret() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'foo', ]); @@ -255,7 +255,7 @@ public function testValidateClientInvalidRedirectUri() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'redirect_uri' => 'http://bar/foo', ]); @@ -281,7 +281,7 @@ public function testValidateClientInvalidRedirectUriArray() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'redirect_uri' => 'http://bar/foo', ]); @@ -307,7 +307,7 @@ public function testValidateClientMalformedRedirectUri() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'redirect_uri' => ['not', 'a', 'string'], ]); @@ -331,7 +331,7 @@ public function testValidateClientBadClient() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 7c2c20d11..248a66ec4 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -76,7 +76,7 @@ public function testCanRespondToAuthorizationRequest() $cookies = [], $queryParams = [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -115,8 +115,8 @@ public function testValidateAuthorizationRequest() [], [ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ); @@ -154,8 +154,8 @@ public function testValidateAuthorizationRequestRedirectUriArray() [], [ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ); @@ -194,7 +194,7 @@ public function testValidateAuthorizationRequestWithoutRedirectUri() [], [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -234,9 +234,9 @@ public function testValidateAuthorizationRequestCodeChallenge() [], [], [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => self::CODE_CHALLENGE, ] ); @@ -260,13 +260,13 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooSho $grant->setClientRepository($clientRepositoryMock); $request = (new ServerRequest())->withQueryParams([ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => \str_repeat('A', 42), ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->validateAuthorizationRequest($request); } @@ -287,13 +287,13 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLon $grant->setClientRepository($clientRepositoryMock); $request = (new ServerRequest())->withQueryParams([ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => \str_repeat('A', 129), ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->validateAuthorizationRequest($request); } @@ -320,7 +320,7 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() 'code_challenge' => \str_repeat('A', 42) . '!', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->validateAuthorizationRequest($request); } @@ -340,7 +340,7 @@ public function testValidateAuthorizationRequestMissingClientId() 'response_type' => 'code', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); @@ -360,10 +360,10 @@ public function testValidateAuthorizationRequestInvalidClientId() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -385,11 +385,11 @@ public function testValidateAuthorizationRequestBadRedirectUriString() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -411,11 +411,11 @@ public function testValidateAuthorizationRequestBadRedirectUriArray() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -450,7 +450,7 @@ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod() 'code_challenge_method' => 'foo', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); @@ -501,7 +501,7 @@ public function testCompleteAuthorizationRequestDenied() ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(9); $grant->completeAuthorizationRequest($authRequest); @@ -551,17 +551,17 @@ public function testRespondToAccessTokenRequest() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -618,16 +618,16 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth() [], [], [ - 'grant_type' => 'authorization_code', + 'grant_type' => 'authorization_code', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), 'client_id' => 'foo', - 'expire_time' => \time() + 3600, - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -685,17 +685,17 @@ public function testRespondToAccessTokenRequestForPublicClient() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -753,17 +753,17 @@ public function testRespondToAccessTokenRequestNullRefreshToken() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -823,20 +823,20 @@ public function testRespondToAccessTokenRequestCodeChallengePlain() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => self::CODE_VERIFIER, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => self::CODE_VERIFIER, 'code_challenge_method' => 'plain', ] ) @@ -896,20 +896,20 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => self::CODE_CHALLENGE, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] ) @@ -969,26 +969,26 @@ public function testPKCEDowngradeBlocked() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, ] ) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); /* @var StubResponseType $response */ @@ -1022,22 +1022,22 @@ public function testRespondToAccessTokenRequestMissingRedirectUri() [], [], [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'grant_type' => 'authorization_code', - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); @@ -1070,23 +1070,23 @@ public function testRespondToAccessTokenRequestRedirectUriMismatch() [], [], [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'grant_type' => 'authorization_code', 'redirect_uri' => 'http://bar/foo', - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); @@ -1123,14 +1123,14 @@ public function testRespondToAccessTokenRequestMissingCode() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'redirect_uri' => self::REDIRECT_URI, + 'redirect_uri' => self::REDIRECT_URI, ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); /* @var StubResponseType $response */ @@ -1164,18 +1164,18 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ), @@ -1217,14 +1217,14 @@ public function testRespondToAccessTokenRequestWithAuthCodeNotAString() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => ['not', 'a', 'string'], + 'code' => ['not', 'a', 'string'], ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } @@ -1255,17 +1255,17 @@ public function testRespondToAccessTokenRequestExpiredCode() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() - 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() - 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -1319,17 +1319,17 @@ public function testRespondToAccessTokenRequestRevokedCode() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -1380,17 +1380,17 @@ public function testRespondToAccessTokenRequestClientMismatch() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'bar', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'bar', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -1441,10 +1441,10 @@ public function testRespondToAccessTokenRequestBadCodeEncryption() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => 'sdfsfsd', + 'code' => 'sdfsfsd', ] ); @@ -1500,20 +1500,20 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'foobar', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'foobar', 'code_challenge_method' => 'plain', ] ) @@ -1573,20 +1573,20 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'nope', - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'foobar', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'foobar', 'code_challenge_method' => 'S256', ] ) @@ -1646,20 +1646,20 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHYtytmhGTigKdZCXfxq-+xbsk9_GxUcaE', // Malformed code. Contains `+`. - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => self::CODE_CHALLENGE, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] ) @@ -1719,20 +1719,20 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHY', // Malformed code. Invalid length. - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8', 'code_challenge_method' => 'S256', ] ) @@ -1792,19 +1792,19 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'foobar', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'foobar', 'code_challenge_method' => 'plain', ] ) @@ -1875,7 +1875,7 @@ public function testAuthCodeRepositoryFailToPersist() ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(7); $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); @@ -1899,7 +1899,7 @@ public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop() new DateInterval('PT10M') ); - $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); @@ -1958,17 +1958,17 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -2026,17 +2026,17 @@ public function testRefreshTokenRepositoryFailToPersist() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -2044,7 +2044,7 @@ public function testRefreshTokenRepositoryFailToPersist() ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(7); /** @var StubResponseType $response */ @@ -2097,17 +2097,17 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -2115,7 +2115,7 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() ] ); - $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); /** @var StubResponseType $response */ @@ -2162,8 +2162,8 @@ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequired $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ]); $this->expectException(OAuthServerException::class); diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 13ea78bae..f6e67622d 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -52,7 +52,7 @@ public function testRespondToRequest() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 5f69242c7..4b8ad838e 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -71,7 +71,7 @@ public function testCanRespondToAuthorizationRequest() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'token', - 'client_id' => 'foo', + 'client_id' => 'foo', ]); $this->assertTrue($grant->canRespondToAuthorizationRequest($request)); @@ -95,8 +95,8 @@ public function testValidateAuthorizationRequest() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ]); $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); @@ -136,7 +136,7 @@ public function testValidateAuthorizationRequestMissingClientId() $request = (new ServerRequest())->withQueryParams(['response_type' => 'code']); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); @@ -152,10 +152,10 @@ public function testValidateAuthorizationRequestInvalidClientId() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -177,7 +177,7 @@ public function testValidateAuthorizationRequestBadRedirectUriString() 'redirect_uri' => 'http://bar', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -195,11 +195,11 @@ public function testValidateAuthorizationRequestBadRedirectUriArray() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -258,7 +258,7 @@ public function testCompleteAuthorizationRequestDenied() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(9); $grant->completeAuthorizationRequest($authRequest); @@ -326,7 +326,7 @@ public function testAccessTokenRepositoryFailToPersist() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(7); $grant->completeAuthorizationRequest($authRequest); @@ -353,7 +353,7 @@ public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); $grant->completeAuthorizationRequest($authRequest); diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index b53ab2357..01c7288b8 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -67,10 +67,10 @@ public function testRespondToRequest() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'username' => 'foo', - 'password' => 'bar', + 'username' => 'foo', + 'password' => 'bar', ]); $responseType = new StubResponseType(); @@ -112,10 +112,10 @@ public function testRespondToRequestNullRefreshToken() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'username' => 'foo', - 'password' => 'bar', + 'username' => 'foo', + 'password' => 'bar', ]); $responseType = new StubResponseType(); @@ -142,7 +142,7 @@ public function testRespondToRequestMissingUsername() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $serverRequest = (new ServerRequest())->withQueryParams([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); @@ -170,9 +170,9 @@ public function testRespondToRequestMissingPassword() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'username' => 'alex', + 'username' => 'alex', ]); $responseType = new StubResponseType(); diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 8f56fac4c..24bf8361a 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -74,21 +74,21 @@ public function testRespondToRequest() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scopes' => ['foo'], + 'scopes' => ['foo'], ]); $responseType = new StubResponseType(); @@ -131,21 +131,21 @@ public function testRespondToRequestNullRefreshToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scopes' => ['foo'], + 'scopes' => ['foo'], ]); $responseType = new StubResponseType(); @@ -188,21 +188,21 @@ public function testRespondToReducedScopes() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo', 'bar'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => 'foo', + 'scope' => 'foo', ]); $responseType = new StubResponseType(); @@ -242,21 +242,21 @@ public function testRespondToUnexpectedScope() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo', 'bar'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => 'foobar', + 'scope' => 'foobar', ]); $responseType = new StubResponseType(); @@ -286,7 +286,7 @@ public function testRespondToRequestMissingOldToken() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); @@ -319,7 +319,7 @@ public function testRespondToRequestInvalidOldToken() $oldRefreshToken = 'foobar'; $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -356,18 +356,18 @@ public function testRespondToRequestClientMismatch() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'bar', + 'client_id' => 'bar', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -401,18 +401,18 @@ public function testRespondToRequestExpiredToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() - 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() - 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -447,18 +447,18 @@ public function testRespondToRequestRevokedToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -500,21 +500,21 @@ public function testRevokedRefreshToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => $refreshTokenId, - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => ['foo'], + 'scope' => ['foo'], ]); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); @@ -557,21 +557,21 @@ public function testUnrevokedRefreshToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => $refreshTokenId, - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => ['foo'], + 'scope' => ['foo'], ]); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index b9297c652..3c4e6ee94 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -19,18 +19,13 @@ use LeagueTests\Stubs\ScopeEntity; use PHPUnit\Framework\TestCase; -use function base64_encode; -use function json_decode; -use function random_bytes; -use function sprintf; - class BearerResponseTypeTest extends TestCase { public function testGenerateHttpResponse(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -62,7 +57,7 @@ public function testGenerateHttpResponse(): void self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); $response->getBody()->rewind(); - $json = json_decode($response->getBody()->getContents()); + $json = \json_decode($response->getBody()->getContents()); self::assertEquals('Bearer', $json->token_type); self::assertObjectHasProperty('expires_in', $json); self::assertObjectHasProperty('access_token', $json); @@ -73,7 +68,7 @@ public function testGenerateHttpResponseWithExtraParams(): void { $responseType = new BearerTokenResponseWithParams(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -105,7 +100,7 @@ public function testGenerateHttpResponseWithExtraParams(): void self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); $response->getBody()->rewind(); - $json = json_decode($response->getBody()->getContents()); + $json = \json_decode($response->getBody()->getContents()); self::assertEquals('Bearer', $json->token_type); self::assertObjectHasProperty('expires_in', $json); self::assertObjectHasProperty('access_token', $json); @@ -119,7 +114,7 @@ public function testDetermineAccessTokenInHeaderValidToken(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -140,7 +135,7 @@ public function testDetermineAccessTokenInHeaderValidToken(): void $responseType->setRefreshToken($refreshToken); $response = $responseType->generateHttpResponse(new Response()); - $json = json_decode((string) $response->getBody()); + $json = \json_decode((string) $response->getBody()); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); @@ -148,7 +143,7 @@ public function testDetermineAccessTokenInHeaderValidToken(): void $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); $request = $authorizationValidator->validateAuthorization($request); @@ -164,7 +159,7 @@ public function testDetermineAccessTokenInHeaderInvalidJWT(): void $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -185,12 +180,12 @@ public function testDetermineAccessTokenInHeaderInvalidJWT(): void $responseType->setRefreshToken($refreshToken); $response = $responseType->generateHttpResponse(new Response()); - $json = json_decode((string) $response->getBody()); + $json = \json_decode((string) $response->getBody()); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); try { $authorizationValidator->validateAuthorization($request); @@ -206,7 +201,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -227,7 +222,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken(): void $responseType->setRefreshToken($refreshToken); $response = $responseType->generateHttpResponse(new Response()); - $json = json_decode((string) $response->getBody()); + $json = \json_decode((string) $response->getBody()); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true); @@ -235,7 +230,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken(): void $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); try { $authorizationValidator->validateAuthorization($request); @@ -251,7 +246,7 @@ public function testDetermineAccessTokenInHeaderInvalidToken(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -274,7 +269,7 @@ public function testDetermineMissingBearerInHeader(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(base64_encode(random_bytes(36))); + $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -305,10 +300,10 @@ public function testGenerateHttpResponseWithIdToken() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => 'https://example.com/callback', - 'code' => 'code', + 'code' => 'code', ] ); @@ -316,8 +311,7 @@ public function testGenerateHttpResponseWithIdToken() public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetEntryInterface { $claimSet = new class() implements ClaimSetEntryInterface { - - public string $scope = "email"; + public string $scope = 'email'; public array $claims = []; @@ -488,4 +482,4 @@ public function setIssuer($issuer) } } } -} \ No newline at end of file +} From cdc66d3d3b7ebcccab2387401cf74e3c1d4a3c18 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 19 May 2024 18:40:15 +0200 Subject: [PATCH 29/39] Revert "Apply fixes from StyleCI" --- examples/public/api.php | 12 +- examples/public/auth_code.php | 2 +- examples/public/client_credentials.php | 2 +- examples/public/implicit.php | 2 +- examples/public/middleware_use.php | 4 +- examples/public/refresh_token.php | 2 +- .../src/Repositories/ClientRepository.php | 6 +- src/ClaimExtractor.php | 2 +- src/Exception/OAuthServerException.php | 2 +- src/Grant/AuthCodeGrant.php | 16 +- src/Grant/ImplicitGrant.php | 6 +- src/ResponseTypes/BearerTokenResponse.php | 14 +- tests/AuthorizationServerTest.php | 6 +- tests/Grant/AbstractGrantTest.php | 16 +- tests/Grant/AuthCodeGrantTest.php | 446 +++++++++--------- tests/Grant/ClientCredentialsGrantTest.php | 2 +- tests/Grant/ImplicitGrantTest.php | 26 +- tests/Grant/PasswordGrantTest.php | 18 +- tests/Grant/RefreshTokenGrantTest.php | 124 ++--- .../ResponseTypes/BearerResponseTypeTest.php | 46 +- 20 files changed, 380 insertions(+), 374 deletions(-) diff --git a/examples/public/api.php b/examples/public/api.php index 66b0f7c41..1fa9d82dc 100644 --- a/examples/public/api.php +++ b/examples/public/api.php @@ -33,18 +33,18 @@ function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { $users = [ [ - 'id' => 123, - 'name' => 'Alex', + 'id' => 123, + 'name' => 'Alex', 'email' => 'alex@thephpleague.com', ], [ - 'id' => 124, - 'name' => 'Frank', + 'id' => 124, + 'name' => 'Frank', 'email' => 'frank@thephpleague.com', ], [ - 'id' => 125, - 'name' => 'Phil', + 'id' => 125, + 'name' => 'Phil', 'email' => 'phil@thephpleague.com', ], ]; diff --git a/examples/public/auth_code.php b/examples/public/auth_code.php index 4d41e4b7a..c082e3b3f 100644 --- a/examples/public/auth_code.php +++ b/examples/public/auth_code.php @@ -24,7 +24,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php index 089a336c5..1e5f090d7 100644 --- a/examples/public/client_credentials.php +++ b/examples/public/client_credentials.php @@ -20,7 +20,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/public/implicit.php b/examples/public/implicit.php index e74f0e337..ac43f5dd1 100644 --- a/examples/public/implicit.php +++ b/examples/public/implicit.php @@ -22,7 +22,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php index 3e743f778..9f958ed26 100644 --- a/examples/public/middleware_use.php +++ b/examples/public/middleware_use.php @@ -26,7 +26,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { @@ -89,7 +89,7 @@ if (\in_array('basic', $request->getAttribute('oauth_scopes', []))) { $params = [ - 'id' => 1, + 'id' => 1, 'name' => 'Alex', 'city' => 'London', ]; diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php index d76d421db..39be08262 100644 --- a/examples/public/refresh_token.php +++ b/examples/public/refresh_token.php @@ -21,7 +21,7 @@ include __DIR__ . '/../vendor/autoload.php'; $app = new App([ - 'settings' => [ + 'settings' => [ 'displayErrorDetails' => true, ], AuthorizationServer::class => function () { diff --git a/examples/src/Repositories/ClientRepository.php b/examples/src/Repositories/ClientRepository.php index 1d6107c6e..3a398f4ed 100644 --- a/examples/src/Repositories/ClientRepository.php +++ b/examples/src/Repositories/ClientRepository.php @@ -39,9 +39,9 @@ public function validateClient($clientIdentifier, $clientSecret, $grantType) { $clients = [ 'myawesomeapp' => [ - 'secret' => \password_hash('abc123', PASSWORD_BCRYPT), - 'name' => self::CLIENT_NAME, - 'redirect_uri' => self::REDIRECT_URI, + 'secret' => \password_hash('abc123', PASSWORD_BCRYPT), + 'name' => self::CLIENT_NAME, + 'redirect_uri' => self::REDIRECT_URI, 'is_confidential' => true, ], ]; diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index b5cce7bdb..debbfaa4f 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -89,7 +89,7 @@ public function getClaimSets(): array */ public function extract(array $scopes, array $claims): array { - $claimData = []; + $claimData = []; $keys = \array_keys($claims); foreach ($scopes as $scope) { diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index a5bb549e7..a0be0a5dd 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -65,7 +65,7 @@ public function __construct($message, $code, $errorType, $httpStatusCode = 400, $this->hint = $hint; $this->redirectUri = $redirectUri; $this->payload = [ - 'error' => $errorType, + 'error' => $errorType, 'error_description' => $message, ]; if ($hint !== null) { diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 9d6e63dd2..8336cf649 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -366,13 +366,13 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization ); $payload = [ - 'client_id' => $authCode->getClient()->getIdentifier(), - 'redirect_uri' => $authCode->getRedirectUri(), - 'auth_code_id' => $authCode->getIdentifier(), - 'scopes' => $authCode->getScopes(), - 'user_id' => $authCode->getUserIdentifier(), - 'expire_time' => (new DateTimeImmutable())->add($this->authCodeTTL)->getTimestamp(), - 'code_challenge' => $authorizationRequest->getCodeChallenge(), + 'client_id' => $authCode->getClient()->getIdentifier(), + 'redirect_uri' => $authCode->getRedirectUri(), + 'auth_code_id' => $authCode->getIdentifier(), + 'scopes' => $authCode->getScopes(), + 'user_id' => $authCode->getUserIdentifier(), + 'expire_time' => (new DateTimeImmutable())->add($this->authCodeTTL)->getTimestamp(), + 'code_challenge' => $authorizationRequest->getCodeChallenge(), 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), ]; @@ -387,7 +387,7 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization $this->makeRedirectUri( $finalRedirectUri, [ - 'code' => $this->encrypt($jsonPayload), + 'code' => $this->encrypt($jsonPayload), 'state' => $authorizationRequest->getState(), ] ) diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 0229dd9ee..0bd91d5ac 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -207,9 +207,9 @@ public function completeAuthorizationRequest(AuthorizationRequest $authorization $finalRedirectUri, [ 'access_token' => (string) $accessToken, - 'token_type' => 'Bearer', - 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(), - 'state' => $authorizationRequest->getState(), + 'token_type' => 'Bearer', + 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(), + 'state' => $authorizationRequest->getState(), ], $this->queryDelimiter ) diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index afafcf35a..33c1606e8 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -26,19 +26,19 @@ public function generateHttpResponse(ResponseInterface $response) $expireDateTime = $this->accessToken->getExpiryDateTime()->getTimestamp(); $responseParams = [ - 'token_type' => 'Bearer', - 'expires_in' => $expireDateTime - \time(), + 'token_type' => 'Bearer', + 'expires_in' => $expireDateTime - \time(), 'access_token' => (string) $this->accessToken, ]; if ($this->refreshToken instanceof RefreshTokenEntityInterface) { $refreshTokenPayload = \json_encode([ - 'client_id' => $this->accessToken->getClient()->getIdentifier(), + 'client_id' => $this->accessToken->getClient()->getIdentifier(), 'refresh_token_id' => $this->refreshToken->getIdentifier(), - 'access_token_id' => $this->accessToken->getIdentifier(), - 'scopes' => $this->accessToken->getScopes(), - 'user_id' => $this->accessToken->getUserIdentifier(), - 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(), + 'access_token_id' => $this->accessToken->getIdentifier(), + 'scopes' => $this->accessToken->getScopes(), + 'user_id' => $this->accessToken->getUserIdentifier(), + 'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(), ]); if ($refreshTokenPayload === false) { diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index dfdafbe9f..af8c89d8a 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -301,7 +301,7 @@ public function testValidateAuthorizationRequest() $cookies = [], $queryParams = [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -342,7 +342,7 @@ public function testValidateAuthorizationRequestWithMissingRedirectUri() $cookies = [], $queryParams = [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -369,7 +369,7 @@ public function testValidateAuthorizationRequestUnregistered() 'client_id' => 'foo', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(2); $server->validateAuthorizationRequest($request); diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index e3a312a35..618545efe 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -109,7 +109,7 @@ public function testGetClientCredentialsClientSecretNotAString() [], [], [ - 'client_id' => 'client_id', + 'client_id' => 'client_id', 'client_secret' => ['not', 'a', 'string'], ] ); @@ -161,9 +161,9 @@ public function testValidateClientConfidential() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'redirect_uri' => 'http://foo/bar', + 'redirect_uri' => 'http://foo/bar', ]); $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); @@ -229,7 +229,7 @@ public function testValidateClientInvalidClientSecret() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'foo', ]); @@ -255,7 +255,7 @@ public function testValidateClientInvalidRedirectUri() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'redirect_uri' => 'http://bar/foo', ]); @@ -281,7 +281,7 @@ public function testValidateClientInvalidRedirectUriArray() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'redirect_uri' => 'http://bar/foo', ]); @@ -307,7 +307,7 @@ public function testValidateClientMalformedRedirectUri() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'redirect_uri' => ['not', 'a', 'string'], ]); @@ -331,7 +331,7 @@ public function testValidateClientBadClient() $abstractGrantReflection = new \ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 248a66ec4..7c2c20d11 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -76,7 +76,7 @@ public function testCanRespondToAuthorizationRequest() $cookies = [], $queryParams = [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -115,8 +115,8 @@ public function testValidateAuthorizationRequest() [], [ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ); @@ -154,8 +154,8 @@ public function testValidateAuthorizationRequestRedirectUriArray() [], [ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ); @@ -194,7 +194,7 @@ public function testValidateAuthorizationRequestWithoutRedirectUri() [], [ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ] ); @@ -234,9 +234,9 @@ public function testValidateAuthorizationRequestCodeChallenge() [], [], [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => self::CODE_CHALLENGE, ] ); @@ -260,13 +260,13 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooSho $grant->setClientRepository($clientRepositoryMock); $request = (new ServerRequest())->withQueryParams([ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => \str_repeat('A', 42), ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $grant->validateAuthorizationRequest($request); } @@ -287,13 +287,13 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLon $grant->setClientRepository($clientRepositoryMock); $request = (new ServerRequest())->withQueryParams([ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_challenge' => \str_repeat('A', 129), ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $grant->validateAuthorizationRequest($request); } @@ -320,7 +320,7 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() 'code_challenge' => \str_repeat('A', 42) . '!', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $grant->validateAuthorizationRequest($request); } @@ -340,7 +340,7 @@ public function testValidateAuthorizationRequestMissingClientId() 'response_type' => 'code', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); @@ -360,10 +360,10 @@ public function testValidateAuthorizationRequestInvalidClientId() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -385,11 +385,11 @@ public function testValidateAuthorizationRequestBadRedirectUriString() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -411,11 +411,11 @@ public function testValidateAuthorizationRequestBadRedirectUriArray() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -450,7 +450,7 @@ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod() 'code_challenge_method' => 'foo', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); @@ -501,7 +501,7 @@ public function testCompleteAuthorizationRequestDenied() ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(9); $grant->completeAuthorizationRequest($authRequest); @@ -551,17 +551,17 @@ public function testRespondToAccessTokenRequest() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -618,16 +618,16 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth() [], [], [ - 'grant_type' => 'authorization_code', + 'grant_type' => 'authorization_code', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), 'client_id' => 'foo', - 'expire_time' => \time() + 3600, - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -685,17 +685,17 @@ public function testRespondToAccessTokenRequestForPublicClient() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -753,17 +753,17 @@ public function testRespondToAccessTokenRequestNullRefreshToken() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -823,20 +823,20 @@ public function testRespondToAccessTokenRequestCodeChallengePlain() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => self::CODE_VERIFIER, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => self::CODE_VERIFIER, 'code_challenge_method' => 'plain', ] ) @@ -896,20 +896,20 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => self::CODE_CHALLENGE, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] ) @@ -969,26 +969,26 @@ public function testPKCEDowngradeBlocked() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, ] ) ), ] ); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); /* @var StubResponseType $response */ @@ -1022,22 +1022,22 @@ public function testRespondToAccessTokenRequestMissingRedirectUri() [], [], [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'grant_type' => 'authorization_code', - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), ] ); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); @@ -1070,23 +1070,23 @@ public function testRespondToAccessTokenRequestRedirectUriMismatch() [], [], [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'grant_type' => 'authorization_code', 'redirect_uri' => 'http://bar/foo', - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ] ) ), ] ); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); @@ -1123,14 +1123,14 @@ public function testRespondToAccessTokenRequestMissingCode() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'redirect_uri' => self::REDIRECT_URI, + 'redirect_uri' => self::REDIRECT_URI, ] ); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); /* @var StubResponseType $response */ @@ -1164,18 +1164,18 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ), @@ -1217,14 +1217,14 @@ public function testRespondToAccessTokenRequestWithAuthCodeNotAString() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => ['not', 'a', 'string'], + 'code' => ['not', 'a', 'string'], ] ); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } @@ -1255,17 +1255,17 @@ public function testRespondToAccessTokenRequestExpiredCode() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() - 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() - 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -1319,17 +1319,17 @@ public function testRespondToAccessTokenRequestRevokedCode() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -1380,17 +1380,17 @@ public function testRespondToAccessTokenRequestClientMismatch() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'bar', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'bar', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -1441,10 +1441,10 @@ public function testRespondToAccessTokenRequestBadCodeEncryption() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => 'sdfsfsd', + 'code' => 'sdfsfsd', ] ); @@ -1500,20 +1500,20 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'foobar', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'foobar', 'code_challenge_method' => 'plain', ] ) @@ -1573,20 +1573,20 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'nope', - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'foobar', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'foobar', 'code_challenge_method' => 'S256', ] ) @@ -1646,20 +1646,20 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHYtytmhGTigKdZCXfxq-+xbsk9_GxUcaE', // Malformed code. Contains `+`. - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => self::CODE_CHALLENGE, + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => self::CODE_CHALLENGE, 'code_challenge_method' => 'S256', ] ) @@ -1719,20 +1719,20 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHY', // Malformed code. Invalid length. - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'R7T1y1HPNFvs1WDCrx4lfoBS6KD2c71pr8OHvULjvv8', 'code_challenge_method' => 'S256', ] ) @@ -1792,19 +1792,19 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => 'foobar', + 'auth_code_id' => \uniqid(), + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + 'code_challenge' => 'foobar', 'code_challenge_method' => 'plain', ] ) @@ -1875,7 +1875,7 @@ public function testAuthCodeRepositoryFailToPersist() ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(7); $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); @@ -1899,7 +1899,7 @@ public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop() new DateInterval('PT10M') ); - $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); @@ -1958,17 +1958,17 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -2026,17 +2026,17 @@ public function testRefreshTokenRepositoryFailToPersist() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -2044,7 +2044,7 @@ public function testRefreshTokenRepositoryFailToPersist() ] ); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(7); /** @var StubResponseType $response */ @@ -2097,17 +2097,17 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code' => $this->cryptStub->doEncrypt( + 'code' => $this->cryptStub->doEncrypt( \json_encode( [ 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'user_id' => 123, - 'scopes' => ['foo'], + 'expire_time' => \time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, ] ) @@ -2115,7 +2115,7 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() ] ); - $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); /** @var StubResponseType $response */ @@ -2162,8 +2162,8 @@ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequired $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ]); $this->expectException(OAuthServerException::class); diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index f6e67622d..13ea78bae 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -52,7 +52,7 @@ public function testRespondToRequest() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 4b8ad838e..5f69242c7 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -71,7 +71,7 @@ public function testCanRespondToAuthorizationRequest() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'token', - 'client_id' => 'foo', + 'client_id' => 'foo', ]); $this->assertTrue($grant->canRespondToAuthorizationRequest($request)); @@ -95,8 +95,8 @@ public function testValidateAuthorizationRequest() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, + 'client_id' => 'foo', + 'redirect_uri' => self::REDIRECT_URI, ]); $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); @@ -136,7 +136,7 @@ public function testValidateAuthorizationRequestMissingClientId() $request = (new ServerRequest())->withQueryParams(['response_type' => 'code']); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); @@ -152,10 +152,10 @@ public function testValidateAuthorizationRequestInvalidClientId() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', + 'client_id' => 'foo', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -177,7 +177,7 @@ public function testValidateAuthorizationRequestBadRedirectUriString() 'redirect_uri' => 'http://bar', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -195,11 +195,11 @@ public function testValidateAuthorizationRequestBadRedirectUriArray() $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar', + 'client_id' => 'foo', + 'redirect_uri' => 'http://bar', ]); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); @@ -258,7 +258,7 @@ public function testCompleteAuthorizationRequestDenied() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(9); $grant->completeAuthorizationRequest($authRequest); @@ -326,7 +326,7 @@ public function testAccessTokenRepositoryFailToPersist() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(OAuthServerException::class); + $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); $this->expectExceptionCode(7); $grant->completeAuthorizationRequest($authRequest); @@ -353,7 +353,7 @@ public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); $grant->completeAuthorizationRequest($authRequest); diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 01c7288b8..b53ab2357 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -67,10 +67,10 @@ public function testRespondToRequest() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'username' => 'foo', - 'password' => 'bar', + 'username' => 'foo', + 'password' => 'bar', ]); $responseType = new StubResponseType(); @@ -112,10 +112,10 @@ public function testRespondToRequestNullRefreshToken() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'username' => 'foo', - 'password' => 'bar', + 'username' => 'foo', + 'password' => 'bar', ]); $responseType = new StubResponseType(); @@ -142,7 +142,7 @@ public function testRespondToRequestMissingUsername() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $serverRequest = (new ServerRequest())->withQueryParams([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); @@ -170,9 +170,9 @@ public function testRespondToRequestMissingPassword() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', - 'username' => 'alex', + 'username' => 'alex', ]); $responseType = new StubResponseType(); diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 24bf8361a..8f56fac4c 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -74,21 +74,21 @@ public function testRespondToRequest() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scopes' => ['foo'], + 'scopes' => ['foo'], ]); $responseType = new StubResponseType(); @@ -131,21 +131,21 @@ public function testRespondToRequestNullRefreshToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scopes' => ['foo'], + 'scopes' => ['foo'], ]); $responseType = new StubResponseType(); @@ -188,21 +188,21 @@ public function testRespondToReducedScopes() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo', 'bar'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => 'foo', + 'scope' => 'foo', ]); $responseType = new StubResponseType(); @@ -242,21 +242,21 @@ public function testRespondToUnexpectedScope() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo', 'bar'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => 'foobar', + 'scope' => 'foobar', ]); $responseType = new StubResponseType(); @@ -286,7 +286,7 @@ public function testRespondToRequestMissingOldToken() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', ]); @@ -319,7 +319,7 @@ public function testRespondToRequestInvalidOldToken() $oldRefreshToken = 'foobar'; $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -356,18 +356,18 @@ public function testRespondToRequestClientMismatch() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'bar', + 'client_id' => 'bar', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -401,18 +401,18 @@ public function testRespondToRequestExpiredToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() - 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() - 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -447,18 +447,18 @@ public function testRespondToRequestRevokedToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, ]); @@ -500,21 +500,21 @@ public function testRevokedRefreshToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => $refreshTokenId, - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => ['foo'], + 'scope' => ['foo'], ]); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); @@ -557,21 +557,21 @@ public function testUnrevokedRefreshToken() $oldRefreshToken = $this->cryptStub->doEncrypt( \json_encode( [ - 'client_id' => 'foo', + 'client_id' => 'foo', 'refresh_token_id' => $refreshTokenId, - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => \time() + 3600, ] ) ); $serverRequest = (new ServerRequest())->withParsedBody([ - 'client_id' => 'foo', + 'client_id' => 'foo', 'client_secret' => 'bar', 'refresh_token' => $oldRefreshToken, - 'scope' => ['foo'], + 'scope' => ['foo'], ]); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 3c4e6ee94..b9297c652 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -19,13 +19,18 @@ use LeagueTests\Stubs\ScopeEntity; use PHPUnit\Framework\TestCase; +use function base64_encode; +use function json_decode; +use function random_bytes; +use function sprintf; + class BearerResponseTypeTest extends TestCase { public function testGenerateHttpResponse(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -57,7 +62,7 @@ public function testGenerateHttpResponse(): void self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); $response->getBody()->rewind(); - $json = \json_decode($response->getBody()->getContents()); + $json = json_decode($response->getBody()->getContents()); self::assertEquals('Bearer', $json->token_type); self::assertObjectHasProperty('expires_in', $json); self::assertObjectHasProperty('access_token', $json); @@ -68,7 +73,7 @@ public function testGenerateHttpResponseWithExtraParams(): void { $responseType = new BearerTokenResponseWithParams(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -100,7 +105,7 @@ public function testGenerateHttpResponseWithExtraParams(): void self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); $response->getBody()->rewind(); - $json = \json_decode($response->getBody()->getContents()); + $json = json_decode($response->getBody()->getContents()); self::assertEquals('Bearer', $json->token_type); self::assertObjectHasProperty('expires_in', $json); self::assertObjectHasProperty('access_token', $json); @@ -114,7 +119,7 @@ public function testDetermineAccessTokenInHeaderValidToken(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -135,7 +140,7 @@ public function testDetermineAccessTokenInHeaderValidToken(): void $responseType->setRefreshToken($refreshToken); $response = $responseType->generateHttpResponse(new Response()); - $json = \json_decode((string) $response->getBody()); + $json = json_decode((string) $response->getBody()); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); @@ -143,7 +148,7 @@ public function testDetermineAccessTokenInHeaderValidToken(): void $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); $request = $authorizationValidator->validateAuthorization($request); @@ -159,7 +164,7 @@ public function testDetermineAccessTokenInHeaderInvalidJWT(): void $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -180,12 +185,12 @@ public function testDetermineAccessTokenInHeaderInvalidJWT(): void $responseType->setRefreshToken($refreshToken); $response = $responseType->generateHttpResponse(new Response()); - $json = \json_decode((string) $response->getBody()); + $json = json_decode((string) $response->getBody()); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); try { $authorizationValidator->validateAuthorization($request); @@ -201,7 +206,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -222,7 +227,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken(): void $responseType->setRefreshToken($refreshToken); $response = $responseType->generateHttpResponse(new Response()); - $json = \json_decode((string) $response->getBody()); + $json = json_decode((string) $response->getBody()); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true); @@ -230,7 +235,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken(): void $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $json->access_token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $json->access_token)); try { $authorizationValidator->validateAuthorization($request); @@ -246,7 +251,7 @@ public function testDetermineAccessTokenInHeaderInvalidToken(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -269,7 +274,7 @@ public function testDetermineMissingBearerInHeader(): void { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -300,10 +305,10 @@ public function testGenerateHttpResponseWithIdToken() [], [], [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', + 'grant_type' => 'authorization_code', + 'client_id' => 'foo', 'redirect_uri' => 'https://example.com/callback', - 'code' => 'code', + 'code' => 'code', ] ); @@ -311,7 +316,8 @@ public function testGenerateHttpResponseWithIdToken() public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetEntryInterface { $claimSet = new class() implements ClaimSetEntryInterface { - public string $scope = 'email'; + + public string $scope = "email"; public array $claims = []; @@ -482,4 +488,4 @@ public function setIssuer($issuer) } } } -} +} \ No newline at end of file From 4219c8cb56608b48108a0329e9bad4f156a5ad8e Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 21 May 2024 14:58:52 +0000 Subject: [PATCH 30/39] Apply fixes from StyleCI --- src/IdTokenEvent.php | 4 ++-- tests/ResponseTypes/BearerResponseTypeTest.php | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/IdTokenEvent.php b/src/IdTokenEvent.php index 5f1eb27aa..9dd813a8e 100644 --- a/src/IdTokenEvent.php +++ b/src/IdTokenEvent.php @@ -11,8 +11,8 @@ */ class IdTokenEvent extends Event { - const ID_TOKEN_ISSUED = 'id_token.issued'; + public const ID_TOKEN_ISSUED = 'id_token.issued'; // This event can be used to extent claims of the id_token - const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created'; + public const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created'; } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index b9297c652..28ab8fab2 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -312,12 +312,11 @@ public function testGenerateHttpResponseWithIdToken() ] ); - $claimSetRepository = new class() implements ClaimSetRepositoryInterface { + $claimSetRepository = new class () implements ClaimSetRepositoryInterface { public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetEntryInterface { - $claimSet = new class() implements ClaimSetEntryInterface { - - public string $scope = "email"; + $claimSet = new class () implements ClaimSetEntryInterface { + public string $scope = 'email'; public array $claims = []; @@ -345,7 +344,7 @@ public function getClaims(): array } }; - $IdTokenRepository = (new class() implements IdTokenRepositoryInterface { + $IdTokenRepository = (new class () implements IdTokenRepositoryInterface { private $issuer; public function getBuilder(AccessTokenEntityInterface $accessToken): JWT\Builder @@ -488,4 +487,4 @@ public function setIssuer($issuer) } } } -} \ No newline at end of file +} From 95f6ac303eeb4953e5d3adf2687cfafca838caea Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 26 May 2024 10:06:59 +0200 Subject: [PATCH 31/39] Fix Lcobucci incompatibility and core quality --- src/ResponseTypes/IdTokenResponse.php | 37 +++++++++++---------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 5ff636beb..82bc1ca02 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -1,16 +1,18 @@ */ protected function getExtraParams(AccessTokenEntityInterface $accessToken): array { @@ -91,7 +85,7 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra if ($claimSet instanceof ClaimSetEntryInterface) { foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims()) as $claimName => $claimValue) { - $builder->withClaim($claimName, $claimValue); + $builder = $builder->withClaim($claimName, $claimValue); } } @@ -118,9 +112,8 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra * * @param ScopeEntityInterface[] $scopes * - * @return bool */ - private static function isOpenIDRequest($scopes): bool + private static function isOpenIDRequest(array $scopes): bool { foreach ($scopes as $scope) { if ($scope instanceof ScopeEntityInterface) { From 174c2b0d45eb192e5c438adac9459efe273dad73 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 26 May 2024 10:08:12 +0200 Subject: [PATCH 32/39] Fix unit test --- .../ResponseTypes/BearerResponseTypeTest.php | 159 +++++++----------- 1 file changed, 64 insertions(+), 95 deletions(-) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 28ab8fab2..8caf8ae1a 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -8,21 +8,45 @@ use DateTimeImmutable; use Laminas\Diactoros\Response; use Laminas\Diactoros\ServerRequest; +use Lcobucci\Clock\SystemClock; +use Lcobucci\JWT\Builder; +use Lcobucci\JWT\Encoding\ChainedFormatter; +use Lcobucci\JWT\Encoding\JoseEncoder; +use Lcobucci\JWT\Signer\Key\InMemory; +use Lcobucci\JWT\Signer\Rsa\Sha256; +use Lcobucci\JWT\Token\Builder as TokenBuilder; +use Lcobucci\JWT\Token\Parser; +use Lcobucci\JWT\Validation\Constraint\HasClaimWithValue; +use Lcobucci\JWT\Validation\Constraint\IssuedBy; +use Lcobucci\JWT\Validation\Constraint\LooseValidAt; +use Lcobucci\JWT\Validation\Constraint\PermittedFor; +use Lcobucci\JWT\Validation\Constraint\RelatedTo; +use Lcobucci\JWT\Validation\Constraint\SignedWith; +use Lcobucci\JWT\Validation\Validator; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; +use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\CryptKey; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClaimSetEntryInterface; +use League\OAuth2\Server\EventEmitting\EventEmitter; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use League\OAuth2\Server\Repositories\ClaimSetRepositoryInterface; +use League\OAuth2\Server\Repositories\IdTokenRepositoryInterface; use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; +use League\OAuth2\Server\ResponseTypes\IdTokenResponse; use LeagueTests\Stubs\AccessTokenEntity; use LeagueTests\Stubs\ClientEntity; use LeagueTests\Stubs\RefreshTokenEntity; use LeagueTests\Stubs\ScopeEntity; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseInterface; use function base64_encode; use function json_decode; use function random_bytes; use function sprintf; +use function uniqid; class BearerResponseTypeTest extends TestCase { @@ -293,98 +317,64 @@ public function testDetermineMissingBearerInHeader(): void } } - public function testGenerateHttpResponseWithIdToken() + public function testGenerateHttpResponseWithIdToken(): void { - $request = new ServerRequest( - [], - [], - null, - 'POST', - 'php://input', - [], - [], - [], - [ - 'grant_type' => 'authorization_code', - 'client_id' => 'foo', - 'redirect_uri' => 'https://example.com/callback', - 'code' => 'code', - ] - ); - $claimSetRepository = new class () implements ClaimSetRepositoryInterface { public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetEntryInterface { $claimSet = new class () implements ClaimSetEntryInterface { - public string $scope = 'email'; + public string $scope = 'openid'; - public array $claims = []; + /** + * @var array $claims + */ + public array $claims = ["acr" => "pop"]; public function getScope(): string { return $this->scope; } + /** + * @return array $claims + */ public function getClaims(): array { return $this->claims; } }; - foreach (ClaimExtractor::getDefaultClaimSetEnties() as $setEntry) { - foreach ($accessToken->getScopes() as $scope) { - if ($setEntry->getScope() === $scope->getIdentifier()) { - foreach ($setEntry->getClaims() as $claim) { - $claimSet->claims[$claim] = $claim; - } - } - } - } return $claimSet; } }; - $IdTokenRepository = (new class () implements IdTokenRepositoryInterface { - private $issuer; - - public function getBuilder(AccessTokenEntityInterface $accessToken): JWT\Builder + $IdTokenRepository = (new class () implements IdTokenRepositoryInterface{ + public function getBuilder(AccessTokenEntityInterface $accessToken): Builder { - if (\class_exists("\Lcobucci\JWT\Encoding\JoseEncoder")) { - $builder = (new JWT\Token\Builder( - new \Lcobucci\JWT\Encoding\JoseEncoder(), - \Lcobucci\JWT\Encoding\ChainedFormatter::withUnixTimestampDates() - )); - } else { - $builder = (new JWT\Builder(new \Lcobucci\JWT\Parsing\Encoder(), new \Lcobucci\JWT\Claim\Factory())); - } - - $builder->permittedFor($accessToken->getClient()->getIdentifier()) - ->issuedBy($this->issuer) - ->issuedAt(new \DateTimeImmutable()) + $builder = (new TokenBuilder( + new JoseEncoder(), + ChainedFormatter::withUnixTimestampDates() + )) + ->permittedFor($accessToken->getClient()->getIdentifier()) + ->issuedBy("https://example.com") + ->issuedAt(new DateTimeImmutable()) ->expiresAt($accessToken->getExpiryDateTime()) ->relatedTo($accessToken->getUserIdentifier()) ->withClaim('nonce', 's6G31Kolwu9p'); return $builder; } - - public function setIssuer($issuer) - { - $this->issuer = $issuer; - - return $this; - } - })->setIssuer(\sprintf('%s://%s', $request->getUri()->getScheme(), $request->getUri()->getHost())); + }); $responseType = new IdTokenResponse( $IdTokenRepository, $claimSetRepository, - $this->getMockBuilder(EmitterInterface::class)->getMock(), + new EventEmitter(), $claimExtrator = new ClaimExtractor() ); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setEncryptionKey(\base64_encode(\random_bytes(36))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -399,7 +389,7 @@ public function setIssuer($issuer) $profileScope->setIdentifier('profile'); $accessToken = new AccessTokenEntity(); - $accessToken->setIdentifier(\uniqid()); + $accessToken->setIdentifier(uniqid()); $accessToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); $accessToken->setClient($client); @@ -408,10 +398,10 @@ public function setIssuer($issuer) $accessToken->addScope($profileScope); $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $accessToken->setUserIdentifier(\uniqid()); + $accessToken->setUserIdentifier(uniqid()); $refreshToken = new RefreshTokenEntity(); - $refreshToken->setIdentifier(\uniqid()); + $refreshToken->setIdentifier(uniqid()); $refreshToken->setAccessToken($accessToken); $refreshToken->setExpiryDateTime((new DateTimeImmutable())->add(new DateInterval('PT1H'))); @@ -427,64 +417,43 @@ public function setIssuer($issuer) $this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]); $response->getBody()->rewind(); - $json = \json_decode($response->getBody()->getContents()); + $json = json_decode($response->getBody()->getContents()); $this->assertEquals('Bearer', $json->token_type); - $this->assertObjectHasAttribute('expires_in', $json); - $this->assertObjectHasAttribute('access_token', $json); - $this->assertObjectHasAttribute('refresh_token', $json); - $this->assertObjectHasAttribute('id_token', $json); - - if (\class_exists("\Lcobucci\JWT\Token\Parser")) { - $token = (new \Lcobucci\JWT\Token\Parser(new \Lcobucci\JWT\Encoding\JoseEncoder()))->parse($json->id_token); - } else { - $token = (new \Lcobucci\JWT\Parser())->parse($json->id_token); + foreach (['expires_in', "access_token", "refresh_token", "id_token"] as $claim) { + self::assertTrue(property_exists($json, $claim)); } - $validator = new JWT\Validation\Validator(); + $token = (new Parser(new JoseEncoder()))->parse($json->id_token); + + $validator = new Validator(); $this->assertTrue($validator->validate( $token, - new JWT\Validation\Constraint\SignedWith(new JWT\Signer\Rsa\Sha256(), JWT\Signer\Key\InMemory::file(__DIR__ . '/../Stubs/public.key', '')) + new SignedWith(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/public.key', '')) )); $this->assertTrue($validator->validate( $token, - new JWT\Validation\Constraint\IssuedBy(\sprintf('%s://%s', $request->getUri()->getScheme(), $request->getUri()->getHost())) + new IssuedBy("https://example.com") )); $this->assertTrue($validator->validate( $token, - new JWT\Validation\Constraint\PermittedFor($client->getIdentifier()) + new PermittedFor($client->getIdentifier()) )); $this->assertTrue($validator->validate( $token, - new JWT\Validation\Constraint\RelatedTo($accessToken->getUserIdentifier()) + new RelatedTo($accessToken->getUserIdentifier()) )); - if (\class_exists("\Lcobucci\JWT\Validation\Constraint\LooseValidAt")) { - $this->assertTrue($validator->validate( - $token, - new \Lcobucci\JWT\Validation\Constraint\LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) - )); - } else { - $this->assertTrue($validator->validate( - $token, - new \Lcobucci\JWT\Validation\Constraint\ValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) - )); - } + $this->assertTrue($validator->validate( + $token, + new LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) + )); - if (\class_exists("\Lcobucci\JWT\Validation\Constraint\HasClaimWithValue")) { - foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { - $this->assertTrue($validator->validate($token, new \Lcobucci\JWT\Validation\Constraint\HasClaimWithValue($claim, $value))); - } - $this->assertTrue($validator->validate($token, new \Lcobucci\JWT\Validation\Constraint\HasClaimWithValue('nonce', 's6G31Kolwu9p'))); - } else { - foreach ($claimExtrator->extract($accessToken->getScopes(), $claimSetRepository->getClaimSetEntry($accessToken)->getClaims()) as $claim => $value) { - $this->assertTrue(\array_key_exists($claim, $token->getClaims())); - $this->assertEquals($value, $token->getClaims()[$claim]); - } - } + $this->assertTrue($validator->validate($token, new HasClaimWithValue("acr", "pop"))); + $this->assertTrue($validator->validate($token, new HasClaimWithValue('nonce', 's6G31Kolwu9p'))); } } From 58cbe2a269c9248080ab4a8a86d4dcfb721443ff Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 26 May 2024 10:08:31 +0200 Subject: [PATCH 33/39] Fix code quality issues --- .../src/Repositories/IdTokenRepository.php | 5 ++- src/ClaimExtractor.php | 44 ++++++++++++------- src/ClaimExtractorInterface.php | 10 +++-- src/Entities/ClaimSetEntry.php | 24 ++++------ src/Entities/ClaimSetEntryInterface.php | 2 + src/Entities/ClaimSetInterface.php | 7 +++ src/IdTokenClaimsCreatedEvent.php | 7 +-- src/IdTokenEvent.php | 6 ++- src/IdTokenIssuedEvent.php | 8 ++-- .../ClaimSetRepositoryInterface.php | 9 +--- .../IdTokenRepositoryInterface.php | 4 +- src/ResponseTypes/UserInfoResponse.php | 8 +++- 12 files changed, 78 insertions(+), 56 deletions(-) diff --git a/examples/src/Repositories/IdTokenRepository.php b/examples/src/Repositories/IdTokenRepository.php index 8fb1f7d62..849e4dd84 100644 --- a/examples/src/Repositories/IdTokenRepository.php +++ b/examples/src/Repositories/IdTokenRepository.php @@ -1,7 +1,10 @@ permittedFor($accessToken->getClient()->getIdentifier()) ->issuedBy($this->issuedBy) - ->issuedAt(new \DateTimeImmutable()) + ->issuedAt(new DateTimeImmutable()) ->expiresAt($accessToken->getExpiryDateTime()) ->relatedTo($accessToken->getUserIdentifier()); diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index debbfaa4f..0af434b42 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -1,11 +1,22 @@ getScope(), $this->protectedClaims) && !$this->getClaimSet($claimSetEntry->getScope())) { - throw new \InvalidArgumentException( - \sprintf('%s is a protected scope and is pre-defined by the OpenID Connect specification.', $claimSetEntry->getScope()) + if (in_array($claimSetEntry->getScope(), $this->protectedClaims) && !$this->getClaimSet($claimSetEntry->getScope())) { + throw new InvalidArgumentException( + sprintf('%s is a protected scope and is pre-defined by the OpenID Connect specification.', $claimSetEntry->getScope()) ); } @@ -58,11 +73,6 @@ public function addClaimSet(ClaimSetEntryInterface $claimSetEntry): ClaimExtract return $this; } - /** - * @param string $scope - * - * @return ClaimSetEntryInterface|null - */ public function getClaimSet(string $scope): ?ClaimSetEntryInterface { foreach ($this->claimSets as $set) { @@ -77,7 +87,7 @@ public function getClaimSet(string $scope): ?ClaimSetEntryInterface /** * Get claimSets * - * @return array + * @return ClaimSetInterface[] */ public function getClaimSets(): array { @@ -90,7 +100,7 @@ public function getClaimSets(): array public function extract(array $scopes, array $claims): array { $claimData = []; - $keys = \array_keys($claims); + $keys = array_keys($claims); foreach ($scopes as $scope) { $scopeName = ($scope instanceof ScopeEntityInterface) ? $scope->getIdentifier() : $scope; @@ -100,21 +110,21 @@ public function extract(array $scopes, array $claims): array continue; } - $intersected = \array_intersect($claimSet->getClaims(), $keys); + $intersected = array_intersect($claimSet->getClaims(), $keys); if (empty($intersected)) { continue; } - $data = \array_filter( + $data = array_filter( $claims, function ($key) use ($intersected) { - return \in_array($key, $intersected); + return in_array($key, $intersected); }, ARRAY_FILTER_USE_KEY ); - $claimData = \array_merge($claimData, $data); + $claimData = array_merge($claimData, $data); } return $claimData; diff --git a/src/ClaimExtractorInterface.php b/src/ClaimExtractorInterface.php index b8fccc4fb..13b3c40c1 100644 --- a/src/ClaimExtractorInterface.php +++ b/src/ClaimExtractorInterface.php @@ -1,16 +1,20 @@ $scopes + * @param array $claims * - * @return array + * @return array */ public function extract(array $scopes, array $claims): array; } diff --git a/src/Entities/ClaimSetEntry.php b/src/Entities/ClaimSetEntry.php index 3eab50486..cce28a89e 100644 --- a/src/Entities/ClaimSetEntry.php +++ b/src/Entities/ClaimSetEntry.php @@ -1,5 +1,7 @@ scope = $scope; - $this->claims = $claims; } /** * Get scope - * - * @return string */ public function getScope(): string { @@ -42,7 +36,7 @@ public function getScope(): string /** * Get claims * - * @return ClaimSetInterface[] + * @return string[] */ public function getClaims(): array { diff --git a/src/Entities/ClaimSetEntryInterface.php b/src/Entities/ClaimSetEntryInterface.php index 6a710ebc8..29878194b 100644 --- a/src/Entities/ClaimSetEntryInterface.php +++ b/src/Entities/ClaimSetEntryInterface.php @@ -1,5 +1,7 @@ builder = $builder; diff --git a/src/IdTokenEvent.php b/src/IdTokenEvent.php index 9dd813a8e..baa3dcb38 100644 --- a/src/IdTokenEvent.php +++ b/src/IdTokenEvent.php @@ -1,15 +1,17 @@ */ -class IdTokenEvent extends Event +class IdTokenEvent extends AbstractEvent { public const ID_TOKEN_ISSUED = 'id_token.issued'; diff --git a/src/IdTokenIssuedEvent.php b/src/IdTokenIssuedEvent.php index ac6ea83d6..477f828cb 100644 --- a/src/IdTokenIssuedEvent.php +++ b/src/IdTokenIssuedEvent.php @@ -1,5 +1,7 @@ token; } - public function __construct($name, Token $token) + public function __construct(mixed $name, Token $token) { parent::__construct($name); $this->token = $token; diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php index 3e0e979e3..1a41d5309 100644 --- a/src/Repositories/ClaimSetRepositoryInterface.php +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -1,5 +1,7 @@ withStatus(200) @@ -28,7 +32,7 @@ public function generateHttpResponse(ResponseInterface $response) ->withHeader('cache-control', 'no-store') ->withHeader('content-type', 'application/json; charset=UTF-8'); - $response->getBody()->write(\json_encode($this->claimSet->getClaims())); + $response->getBody()->write(json_encode($this->claimSet->getClaims())); return $response; } From 9113224c403187073dc166c6936f2c27c94aa52a Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 26 May 2024 08:09:24 +0000 Subject: [PATCH 34/39] Apply fixes from StyleCI --- src/ClaimExtractor.php | 1 - src/ClaimExtractorInterface.php | 2 +- src/Entities/ClaimSetEntry.php | 2 +- src/IdTokenClaimsCreatedEvent.php | 1 - src/IdTokenIssuedEvent.php | 2 -- src/ResponseTypes/IdTokenResponse.php | 4 ---- tests/ResponseTypes/BearerResponseTypeTest.php | 14 +++++++------- 7 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index 0af434b42..50118841e 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -55,7 +55,6 @@ public function __construct(array $claimSets = []) } /** - * * @return $this * * @throws \InvalidArgumentException diff --git a/src/ClaimExtractorInterface.php b/src/ClaimExtractorInterface.php index 13b3c40c1..220ca138c 100644 --- a/src/ClaimExtractorInterface.php +++ b/src/ClaimExtractorInterface.php @@ -12,7 +12,7 @@ interface ClaimExtractorInterface * For given scopes and aggregated claims get all claims that have been configured on the extractor. * * @param array $scopes - * @param array $claims + * @param array $claims * * @return array */ diff --git a/src/Entities/ClaimSetEntry.php b/src/Entities/ClaimSetEntry.php index cce28a89e..c32aa3e7f 100644 --- a/src/Entities/ClaimSetEntry.php +++ b/src/Entities/ClaimSetEntry.php @@ -16,7 +16,7 @@ class ClaimSetEntry implements ClaimSetEntryInterface /** * Summary of __construct * - * @param string $scope Scope of the claimset + * @param string $scope Scope of the claimset * @param string[] $claims The claims */ public function __construct( diff --git a/src/IdTokenClaimsCreatedEvent.php b/src/IdTokenClaimsCreatedEvent.php index e06398b96..34fe5312b 100644 --- a/src/IdTokenClaimsCreatedEvent.php +++ b/src/IdTokenClaimsCreatedEvent.php @@ -17,7 +17,6 @@ final class IdTokenClaimsCreatedEvent extends IdTokenEvent { /** * Builder - * */ private Builder $builder; diff --git a/src/IdTokenIssuedEvent.php b/src/IdTokenIssuedEvent.php index 477f828cb..abe3e173b 100644 --- a/src/IdTokenIssuedEvent.php +++ b/src/IdTokenIssuedEvent.php @@ -15,13 +15,11 @@ final class IdTokenIssuedEvent extends IdTokenEvent { /** * Token - * */ private Token $token; /** * Get Token - * */ public function getToken(): Token { diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index 82bc1ca02..f70b6f954 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -33,19 +33,16 @@ class IdTokenResponse extends BearerTokenResponse /** * IdTokenRepositoryInterface - * */ protected IdTokenRepositoryInterface $idTokenRepository; /** * ClaimSetRepositoryInterface - * */ protected ClaimSetRepositoryInterface $claimRepository; /** * ClaimExtractorInterface - * */ protected ClaimExtractorInterface $extractor; @@ -111,7 +108,6 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra * Return true If this is an OpenID request * * @param ScopeEntityInterface[] $scopes - * */ private static function isOpenIDRequest(array $scopes): bool { diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 8caf8ae1a..da116e77d 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -326,9 +326,9 @@ public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): Claim public string $scope = 'openid'; /** - * @var array $claims + * @var array */ - public array $claims = ["acr" => "pop"]; + public array $claims = ['acr' => 'pop']; public function getScope(): string { @@ -348,7 +348,7 @@ public function getClaims(): array } }; - $IdTokenRepository = (new class () implements IdTokenRepositoryInterface{ + $IdTokenRepository = (new class () implements IdTokenRepositoryInterface { public function getBuilder(AccessTokenEntityInterface $accessToken): Builder { $builder = (new TokenBuilder( @@ -356,7 +356,7 @@ public function getBuilder(AccessTokenEntityInterface $accessToken): Builder ChainedFormatter::withUnixTimestampDates() )) ->permittedFor($accessToken->getClient()->getIdentifier()) - ->issuedBy("https://example.com") + ->issuedBy('https://example.com') ->issuedAt(new DateTimeImmutable()) ->expiresAt($accessToken->getExpiryDateTime()) ->relatedTo($accessToken->getUserIdentifier()) @@ -420,7 +420,7 @@ public function getBuilder(AccessTokenEntityInterface $accessToken): Builder $json = json_decode($response->getBody()->getContents()); $this->assertEquals('Bearer', $json->token_type); - foreach (['expires_in', "access_token", "refresh_token", "id_token"] as $claim) { + foreach (['expires_in', 'access_token', 'refresh_token', 'id_token'] as $claim) { self::assertTrue(property_exists($json, $claim)); } @@ -435,7 +435,7 @@ public function getBuilder(AccessTokenEntityInterface $accessToken): Builder $this->assertTrue($validator->validate( $token, - new IssuedBy("https://example.com") + new IssuedBy('https://example.com') )); $this->assertTrue($validator->validate( @@ -453,7 +453,7 @@ public function getBuilder(AccessTokenEntityInterface $accessToken): Builder new LooseValidAt(new SystemClock($accessToken->getExpiryDateTime()->getTimezone())) )); - $this->assertTrue($validator->validate($token, new HasClaimWithValue("acr", "pop"))); + $this->assertTrue($validator->validate($token, new HasClaimWithValue('acr', 'pop'))); $this->assertTrue($validator->validate($token, new HasClaimWithValue('nonce', 's6G31Kolwu9p'))); } } From 4ab66a7f23c924d21cd4bdd17bdf81cb3d8fcfe7 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 22 Oct 2024 20:06:36 +0200 Subject: [PATCH 35/39] ClaimSetRepositoryInterface::getClaimSet returns a ClaimSetInterface --- src/Repositories/ClaimSetRepositoryInterface.php | 4 ++-- src/ResponseTypes/IdTokenResponse.php | 6 +++--- tests/ResponseTypes/BearerResponseTypeTest.php | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Repositories/ClaimSetRepositoryInterface.php b/src/Repositories/ClaimSetRepositoryInterface.php index 1a41d5309..3b4280d69 100644 --- a/src/Repositories/ClaimSetRepositoryInterface.php +++ b/src/Repositories/ClaimSetRepositoryInterface.php @@ -5,7 +5,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\Entities\ClaimSetEntryInterface; +use League\OAuth2\Server\Entities\ClaimSetInterface; /** * ClaimSetRepositoryInterface resolve claims for id_token. @@ -16,5 +16,5 @@ */ interface ClaimSetRepositoryInterface { - public function getClaimSetEntry(AccessTokenEntityInterface $authCode): ClaimSetEntryInterface; + public function getClaimSet(AccessTokenEntityInterface $authCode): ClaimSetInterface; } diff --git a/src/ResponseTypes/IdTokenResponse.php b/src/ResponseTypes/IdTokenResponse.php index f70b6f954..508bf93a5 100644 --- a/src/ResponseTypes/IdTokenResponse.php +++ b/src/ResponseTypes/IdTokenResponse.php @@ -9,7 +9,7 @@ use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\ClaimExtractorInterface; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\Entities\ClaimSetEntryInterface; +use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\EventEmitting\EmitterAwarePolyfill; use League\OAuth2\Server\EventEmitting\EventEmitter; @@ -76,11 +76,11 @@ protected function getExtraParams(AccessTokenEntityInterface $accessToken): arra return []; } - $claimSet = $this->claimRepository->getClaimSetEntry($accessToken); + $claimSet = $this->claimRepository->getClaimSet($accessToken); $builder = $this->idTokenRepository->getBuilder($accessToken); - if ($claimSet instanceof ClaimSetEntryInterface) { + if ($claimSet instanceof ClaimSetInterface) { foreach ($this->extractor->extract($accessToken->getScopes(), $claimSet->getClaims()) as $claimName => $claimValue) { $builder = $builder->withClaim($claimName, $claimValue); } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index da116e77d..62146fd7a 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -27,7 +27,7 @@ use League\OAuth2\Server\ClaimExtractor; use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\Entities\ClaimSetEntryInterface; +use League\OAuth2\Server\Entities\ClaimSetInterface; use League\OAuth2\Server\EventEmitting\EventEmitter; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; @@ -320,9 +320,9 @@ public function testDetermineMissingBearerInHeader(): void public function testGenerateHttpResponseWithIdToken(): void { $claimSetRepository = new class () implements ClaimSetRepositoryInterface { - public function getClaimSetEntry(AccessTokenEntityInterface $accessToken): ClaimSetEntryInterface + public function getClaimSet(AccessTokenEntityInterface $accessToken): ClaimSetInterface { - $claimSet = new class () implements ClaimSetEntryInterface { + $claimSet = new class() implements ClaimSetInterface { public string $scope = 'openid'; /** From 66e4dc6bfa5bbabb66edec5a7779c1ba7d39f1de Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 22 Oct 2024 18:07:45 +0000 Subject: [PATCH 36/39] Apply fixes from StyleCI --- tests/ResponseTypes/BearerResponseTypeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 62146fd7a..7ca51431e 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -322,7 +322,7 @@ public function testGenerateHttpResponseWithIdToken(): void $claimSetRepository = new class () implements ClaimSetRepositoryInterface { public function getClaimSet(AccessTokenEntityInterface $accessToken): ClaimSetInterface { - $claimSet = new class() implements ClaimSetInterface { + $claimSet = new class () implements ClaimSetInterface { public string $scope = 'openid'; /** From 799455d19a826d29aef419632a5ab6c2c11d8b70 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 22 Oct 2024 21:50:22 +0200 Subject: [PATCH 37/39] fixed doc --- src/Entities/ClaimSetEntry.php | 2 +- src/Entities/ClaimSetInterface.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entities/ClaimSetEntry.php b/src/Entities/ClaimSetEntry.php index c32aa3e7f..db273f36e 100644 --- a/src/Entities/ClaimSetEntry.php +++ b/src/Entities/ClaimSetEntry.php @@ -36,7 +36,7 @@ public function getScope(): string /** * Get claims * - * @return string[] + * @return array */ public function getClaims(): array { diff --git a/src/Entities/ClaimSetInterface.php b/src/Entities/ClaimSetInterface.php index c050d0558..724872eb6 100644 --- a/src/Entities/ClaimSetInterface.php +++ b/src/Entities/ClaimSetInterface.php @@ -16,7 +16,7 @@ interface ClaimSetInterface /** * Get Claims * - * @return string[] + * @return array */ public function getClaims(): array; } From b1400a1fe0b09d8978270c4b94ef63a0331d0f24 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 22 Oct 2024 21:50:50 +0200 Subject: [PATCH 38/39] Fixed ClaimExtractor --- src/ClaimExtractor.php | 38 +++++++++---------- .../ResponseTypes/BearerResponseTypeTest.php | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ClaimExtractor.php b/src/ClaimExtractor.php index 50118841e..c50b11653 100644 --- a/src/ClaimExtractor.php +++ b/src/ClaimExtractor.php @@ -28,11 +28,11 @@ class ClaimExtractor implements ClaimExtractorInterface { /** - * claimSets + * claimSetEntries * * @var ClaimSetEntryInterface[] */ - protected array $claimSets = []; + protected array $claimSetEntries = []; /** * Protected claims @@ -44,13 +44,13 @@ class ClaimExtractor implements ClaimExtractorInterface /** * ClaimExtractor constructor * - * @param ClaimSetEntryInterface[] $claimSets + * @param array $claimSetEntries */ - public function __construct(array $claimSets = []) + public function __construct(array $claimSetEntries = []) { - $this->claimSets = self::getDefaultClaimSetEnties(); - foreach ($claimSets as $claimSet) { - $this->addClaimSet($claimSet); + $this->claimSetEntries = self::getDefaultClaimSetEnties(); + foreach ($claimSetEntries as $claimSetEntry) { + $this->addClaimSetEntry($claimSetEntry); } } @@ -59,24 +59,24 @@ public function __construct(array $claimSets = []) * * @throws \InvalidArgumentException */ - public function addClaimSet(ClaimSetEntryInterface $claimSetEntry): ClaimExtractor + public function addClaimSetEntry(ClaimSetEntryInterface $claimSetEntry): ClaimExtractor { - if (in_array($claimSetEntry->getScope(), $this->protectedClaims) && !$this->getClaimSet($claimSetEntry->getScope())) { + if (in_array($claimSetEntry->getScope(), $this->protectedClaims) && !$this->getClaimSetEntry($claimSetEntry->getScope())) { throw new InvalidArgumentException( sprintf('%s is a protected scope and is pre-defined by the OpenID Connect specification.', $claimSetEntry->getScope()) ); } - $this->claimSets[] = $claimSetEntry; + $this->claimSetEntries[] = $claimSetEntry; return $this; } - public function getClaimSet(string $scope): ?ClaimSetEntryInterface + public function getClaimSetEntry(string $scope): ?ClaimSetEntryInterface { - foreach ($this->claimSets as $set) { - if ($set->getScope() === $scope) { - return $set; + foreach ($this->claimSetEntries as $entry) { + if ($entry->getScope() === $scope) { + return $entry; } } @@ -84,13 +84,13 @@ public function getClaimSet(string $scope): ?ClaimSetEntryInterface } /** - * Get claimSets + * Get claimSetEntries * - * @return ClaimSetInterface[] + * @return array */ - public function getClaimSets(): array + public function getClaimSetEntries(): array { - return $this->claimSets; + return $this->claimSetEntries; } /** @@ -104,7 +104,7 @@ public function extract(array $scopes, array $claims): array foreach ($scopes as $scope) { $scopeName = ($scope instanceof ScopeEntityInterface) ? $scope->getIdentifier() : $scope; - $claimSet = $this->getClaimSet($scopeName); + $claimSet = $this->getClaimSetEntry($scopeName); if (null === $claimSet) { continue; } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 7ca51431e..b0b29e6d3 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -319,7 +319,7 @@ public function testDetermineMissingBearerInHeader(): void public function testGenerateHttpResponseWithIdToken(): void { - $claimSetRepository = new class () implements ClaimSetRepositoryInterface { + $claimSetRepository = new class() implements ClaimSetRepositoryInterface { public function getClaimSet(AccessTokenEntityInterface $accessToken): ClaimSetInterface { $claimSet = new class () implements ClaimSetInterface { From 9b3082332da11eff89cc8a2a5d8df6e777039ebc Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Tue, 22 Oct 2024 19:52:09 +0000 Subject: [PATCH 39/39] Apply fixes from StyleCI --- tests/ResponseTypes/BearerResponseTypeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index b0b29e6d3..7ca51431e 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -319,7 +319,7 @@ public function testDetermineMissingBearerInHeader(): void public function testGenerateHttpResponseWithIdToken(): void { - $claimSetRepository = new class() implements ClaimSetRepositoryInterface { + $claimSetRepository = new class () implements ClaimSetRepositoryInterface { public function getClaimSet(AccessTokenEntityInterface $accessToken): ClaimSetInterface { $claimSet = new class () implements ClaimSetInterface {