diff --git a/CHANGELOG.md b/CHANGELOG.md index d25446e25..2d4a4dfde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added +- GrantTypeInterface has a new function, `revokeRefreshTokens()` for enabling or disabling refresh tokens after use (PR #1375) - A CryptKeyInterface to allow developers to change the CryptKey implementation with greater ease (PR #1044) - The authorization server can now finalize scopes when a client uses a refresh token (PR #1094) - An AuthorizationRequestInterface to make it easier to extend the AuthorizationRequest (PR #1110) +- Added function `getKeyContents()` to the `CryptKeyInterface` (PR #1375) ### Fixed - If a refresh token has expired, been revoked, cannot be decrypted, or does not belong to the correct client, the server will now issue an `invalid_grant` error and a HTTP 400 response. In previous versions the server incorrectly issued an `invalid_request` and HTTP 401 response (PR #1042) (PR #1082) @@ -17,6 +19,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Authorization Request objects are now created through the factory method, `createAuthorizationRequest()` (PR #1111) - Changed parameters for `finalizeScopes()` to allow a reference to an auth code ID (PR #1112) +### Removed +- Removed message property from OAuthException HTTP response. Now just use error_description as per the OAuth 2 spec (PR #1375) + ## [8.5.4] - released 2023-08-25 ### Added - Support for league/uri ^7.0 (PR #1367) diff --git a/composer.json b/composer.json index 5c9dfb398..4f6012f0e 100644 --- a/composer.json +++ b/composer.json @@ -10,15 +10,21 @@ "league/uri": "^6.7 || ^7.0", "lcobucci/jwt": "^4.3 || ^5.0", "psr/http-message": "^1.0.1 || ^2.0", - "defuse/php-encryption": "^2.3", + "defuse/php-encryption": "^2.3.1", "lcobucci/clock": "^2.2 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^9.6.6", + "phpunit/phpunit": "^9.6.11", "laminas/laminas-diactoros": "^3.0.0", - "phpstan/phpstan": "^0.12.57", - "phpstan/phpstan-phpunit": "^0.12.16", - "roave/security-advisories": "dev-master" + "phpstan/phpstan": "^1.10.26", + "phpstan/phpstan-phpunit": "^1.3.14", + "roave/security-advisories": "dev-master", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-strict-rules": "^1.5", + "slevomat/coding-standard": "^8.13", + "php-parallel-lint/php-parallel-lint": "^1.3", + "squizlabs/php_codesniffer": "^3.7" }, "repositories": [ { @@ -69,5 +75,12 @@ "psr-4": { "LeagueTests\\": "tests/" } + }, + "config": { + "allow-plugins": { + "ocramius/package-versions": true, + "phpstan/extension-installer": true, + "dealerdirect/phpcodesniffer-composer-installer": false + } } } diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index ba1fb4915..000000000 --- a/phpstan.neon +++ /dev/null @@ -1,8 +0,0 @@ -includes: - - vendor/phpstan/phpstan-phpunit/extension.neon - - vendor/phpstan/phpstan-phpunit/rules.neon -services: - - - class: LeagueTests\PHPStan\AbstractGrantExtension - tags: - - phpstan.broker.dynamicMethodReturnTypeExtension diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 000000000..2473d6a93 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,10 @@ +parameters: + level: 8 + paths: + - src + - tests + ignoreErrors: + - + message: '#Call to an undefined method League\\OAuth2\\Server\\ResponseTypes\\ResponseTypeInterface::getAccessToken\(\)\.#' + path: tests/Grant/ClientCredentialsGrantTest.php + - '#Return type \(League\\Event\\EmitterInterface\|null\) of method LeagueTests\\Stubs\\GrantType::getEmitter\(\) should be covariant with return type \(League\\Event\\EmitterInterface\) of method League\\Event\\EmitterAwareInterface::getEmitter\(\)#' diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 6089adbf0..fbdf99228 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server; use DateInterval; @@ -32,79 +35,36 @@ class AuthorizationServer implements EmitterAwareInterface /** * @var GrantTypeInterface[] */ - protected $enabledGrantTypes = []; + protected array $enabledGrantTypes = []; /** * @var DateInterval[] */ - protected $grantTypeAccessTokenTTL = []; + protected array $grantTypeAccessTokenTTL = []; - /** - * @var CryptKeyInterface - */ - protected $privateKey; + protected CryptKeyInterface $privateKey; - /** - * @var CryptKeyInterface - */ - protected $publicKey; + protected CryptKeyInterface $publicKey; - /** - * @var ResponseTypeInterface - */ - protected $responseType; + protected ResponseTypeInterface $responseType; - /** - * @var ClientRepositoryInterface - */ - private $clientRepository; + private string|Key $encryptionKey; - /** - * @var AccessTokenRepositoryInterface - */ - private $accessTokenRepository; + private string $defaultScope = ''; - /** - * @var ScopeRepositoryInterface - */ - private $scopeRepository; + private bool $revokeRefreshTokens = true; /** - * @var string|Key - */ - private $encryptionKey; - - /** - * @var string - */ - private $defaultScope = ''; - - /** - * @var bool - */ - private $revokeRefreshTokens = true; - - /** - * New server instance. - * - * @param ClientRepositoryInterface $clientRepository - * @param AccessTokenRepositoryInterface $accessTokenRepository - * @param ScopeRepositoryInterface $scopeRepository - * @param CryptKeyInterface|string $privateKey - * @param string|Key $encryptionKey - * @param null|ResponseTypeInterface $responseType + * New server instance */ public function __construct( - ClientRepositoryInterface $clientRepository, - AccessTokenRepositoryInterface $accessTokenRepository, - ScopeRepositoryInterface $scopeRepository, - $privateKey, - $encryptionKey, - ResponseTypeInterface $responseType = null + private ClientRepositoryInterface $clientRepository, + private AccessTokenRepositoryInterface $accessTokenRepository, + private ScopeRepositoryInterface $scopeRepository, + CryptKeyInterface|string $privateKey, + Key|string $encryptionKey, + ResponseTypeInterface|null $responseType = null ) { - $this->clientRepository = $clientRepository; - $this->accessTokenRepository = $accessTokenRepository; - $this->scopeRepository = $scopeRepository; if ($privateKey instanceof CryptKeyInterface === false) { $privateKey = new CryptKey($privateKey); @@ -123,12 +83,9 @@ public function __construct( } /** - * Enable a grant type on the server. - * - * @param GrantTypeInterface $grantType - * @param null|DateInterval $accessTokenTTL + * Enable a grant type on the server */ - public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null) + public function enableGrantType(GrantTypeInterface $grantType, DateInterval|null $accessTokenTTL = null): void { if ($accessTokenTTL === null) { $accessTokenTTL = new DateInterval('PT1H'); @@ -150,13 +107,9 @@ public function enableGrantType(GrantTypeInterface $grantType, DateInterval $acc /** * Validate an authorization request * - * @param ServerRequestInterface $request - * * @throws OAuthServerException - * - * @return AuthorizationRequestInterface */ - public function validateAuthorizationRequest(ServerRequestInterface $request) + public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface { foreach ($this->enabledGrantTypes as $grantType) { if ($grantType->canRespondToAuthorizationRequest($request)) { @@ -169,16 +122,11 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) /** * Complete an authorization request - * - * @param AuthorizationRequestInterface $authRequest - * @param ResponseInterface $response - * - * @return ResponseInterface */ public function completeAuthorizationRequest( AuthorizationRequestInterface $authRequest, ResponseInterface $response - ) { + ): ResponseInterface { return $this->enabledGrantTypes[$authRequest->getGrantTypeId()] ->completeAuthorizationRequest($authRequest) ->generateHttpResponse($response); @@ -187,28 +135,22 @@ public function completeAuthorizationRequest( /** * Return an access token response. * - * @param ServerRequestInterface $request - * @param ResponseInterface $response - * * @throws OAuthServerException - * - * @return ResponseInterface */ - public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response) + public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { foreach ($this->enabledGrantTypes as $grantType) { if (!$grantType->canRespondToAccessTokenRequest($request)) { continue; } + $tokenResponse = $grantType->respondToAccessTokenRequest( $request, $this->getResponseType(), $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] ); - if ($tokenResponse instanceof ResponseTypeInterface) { - return $tokenResponse->generateHttpResponse($response); - } + return $tokenResponse->generateHttpResponse($response); } throw OAuthServerException::unsupportedGrantType(); @@ -216,10 +158,8 @@ public function respondToAccessTokenRequest(ServerRequestInterface $request, Res /** * Get the token type that grants will return in the HTTP response. - * - * @return ResponseTypeInterface */ - protected function getResponseType() + protected function getResponseType(): ResponseTypeInterface { $responseType = clone $this->responseType; @@ -234,18 +174,14 @@ protected function getResponseType() /** * Set the default scope for the authorization server. - * - * @param string $defaultScope */ - public function setDefaultScope($defaultScope) + public function setDefaultScope(string $defaultScope): void { $this->defaultScope = $defaultScope; } /** * Sets whether to revoke refresh tokens or not (for all grant types). - * - * @param bool $revokeRefreshTokens */ public function revokeRefreshTokens(bool $revokeRefreshTokens): void { diff --git a/src/AuthorizationValidators/AuthorizationValidatorInterface.php b/src/AuthorizationValidators/AuthorizationValidatorInterface.php index 7e49f8477..05d9fb88c 100644 --- a/src/AuthorizationValidators/AuthorizationValidatorInterface.php +++ b/src/AuthorizationValidators/AuthorizationValidatorInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\AuthorizationValidators; use Psr\Http\Message\ServerRequestInterface; @@ -14,12 +17,8 @@ interface AuthorizationValidatorInterface { /** - * Determine the access token in the authorization header and append OAUth properties to the request - * as attributes. - * - * @param ServerRequestInterface $request - * - * @return ServerRequestInterface + * Determine the access token in the authorization header and append OAUth + * properties to the request as attributes. */ - public function validateAuthorization(ServerRequestInterface $request); + public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface; } diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index f95c206d7..84b7ba484 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,13 +8,18 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\AuthorizationValidators; +use DateInterval; use DateTimeZone; use Lcobucci\Clock\SystemClock; use Lcobucci\JWT\Configuration; +use Lcobucci\JWT\Exception; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; +use Lcobucci\JWT\UnencryptedToken; use Lcobucci\JWT\Validation\Constraint\LooseValidAt; use Lcobucci\JWT\Validation\Constraint\SignedWith; use Lcobucci\JWT\Validation\RequiredConstraintsViolated; @@ -22,47 +28,30 @@ use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use Psr\Http\Message\ServerRequestInterface; +use RuntimeException; + +use function count; +use function date_default_timezone_get; +use function is_array; +use function preg_replace; +use function trim; class BearerTokenValidator implements AuthorizationValidatorInterface { use CryptTrait; - /** - * @var AccessTokenRepositoryInterface - */ - private $accessTokenRepository; - - /** - * @var CryptKeyInterface - */ - protected $publicKey; - - /** - * @var Configuration - */ - private $jwtConfiguration; + protected CryptKeyInterface $publicKey; - /** - * @var \DateInterval|null - */ - private $jwtValidAtDateLeeway; + private Configuration $jwtConfiguration; - /** - * @param AccessTokenRepositoryInterface $accessTokenRepository - * @param \DateInterval|null $jwtValidAtDateLeeway - */ - public function __construct(AccessTokenRepositoryInterface $accessTokenRepository, \DateInterval $jwtValidAtDateLeeway = null) + public function __construct(private AccessTokenRepositoryInterface $accessTokenRepository, private ?DateInterval $jwtValidAtDateLeeway = null) { - $this->accessTokenRepository = $accessTokenRepository; - $this->jwtValidAtDateLeeway = $jwtValidAtDateLeeway; } /** * Set the public key - * - * @param CryptKeyInterface $key */ - public function setPublicKey(CryptKeyInterface $key) + public function setPublicKey(CryptKeyInterface $key): void { $this->publicKey = $key; @@ -72,19 +61,26 @@ public function setPublicKey(CryptKeyInterface $key) /** * Initialise the JWT configuration. */ - private function initJwtConfiguration() + private function initJwtConfiguration(): void { $this->jwtConfiguration = Configuration::forSymmetricSigner( new Sha256(), InMemory::plainText('empty', 'empty') ); - $clock = new SystemClock(new DateTimeZone(\date_default_timezone_get())); + $clock = new SystemClock(new DateTimeZone(date_default_timezone_get())); + + $publicKeyContents = $this->publicKey->getKeyContents(); + + if ($publicKeyContents === '') { + throw new RuntimeException('Public key is empty'); + } + $this->jwtConfiguration->setValidationConstraints( new LooseValidAt($clock, $this->jwtValidAtDateLeeway), new SignedWith( new Sha256(), - InMemory::plainText($this->publicKey->getKeyContents(), $this->publicKey->getPassPhrase() ?? '') + InMemory::plainText($publicKeyContents, $this->publicKey->getPassPhrase() ?? '') ) ); } @@ -92,19 +88,19 @@ private function initJwtConfiguration() /** * {@inheritdoc} */ - public function validateAuthorization(ServerRequestInterface $request) + public function validateAuthorization(ServerRequestInterface $request): ServerRequestInterface { if ($request->hasHeader('authorization') === false) { throw OAuthServerException::accessDenied('Missing "Authorization" header'); } $header = $request->getHeader('authorization'); - $jwt = \trim((string) \preg_replace('/^\s*Bearer\s/', '', $header[0])); + $jwt = trim((string) preg_replace('/^\s*Bearer\s/', '', $header[0])); try { // Attempt to parse the JWT $token = $this->jwtConfiguration->parser()->parse($jwt); - } catch (\Lcobucci\JWT\Exception $exception) { + } catch (Exception $exception) { throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception); } @@ -116,6 +112,10 @@ public function validateAuthorization(ServerRequestInterface $request) throw OAuthServerException::accessDenied('Access token could not be verified', null, $exception); } + if (!$token instanceof UnencryptedToken) { + throw OAuthServerException::accessDenied('Access token is not an instance of UnencryptedToken'); + } + $claims = $token->claims(); // Check if token has been revoked @@ -134,12 +134,12 @@ public function validateAuthorization(ServerRequestInterface $request) /** * Convert single record arrays into strings to ensure backwards compatibility between v4 and v3.x of lcobucci/jwt * - * @param mixed $aud + * TODO: Investigate as I don't think we need this any more * - * @return array|string + * @return array|string */ - private function convertSingleRecordAudToString($aud) + private function convertSingleRecordAudToString(mixed $aud): array|string { - return \is_array($aud) && \count($aud) === 1 ? $aud[0] : $aud; + return is_array($aud) && count($aud) === 1 ? $aud[0] : $aud; } } diff --git a/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php b/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php index 3d7ad59c5..1417acf10 100644 --- a/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php +++ b/src/CodeChallengeVerifiers/CodeChallengeVerifierInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Lukáš Unger @@ -7,24 +8,19 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\CodeChallengeVerifiers; interface CodeChallengeVerifierInterface { /** * Return code challenge method. - * - * @return string */ - public function getMethod(); + public function getMethod(): string; /** * Verify the code challenge. - * - * @param string $codeVerifier - * @param string $codeChallenge - * - * @return bool */ - public function verifyCodeChallenge($codeVerifier, $codeChallenge); + public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool; } diff --git a/src/CodeChallengeVerifiers/PlainVerifier.php b/src/CodeChallengeVerifiers/PlainVerifier.php index cf6b70af1..53d669459 100644 --- a/src/CodeChallengeVerifiers/PlainVerifier.php +++ b/src/CodeChallengeVerifiers/PlainVerifier.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Lukáš Unger @@ -7,30 +8,27 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\CodeChallengeVerifiers; +use function hash_equals; + class PlainVerifier implements CodeChallengeVerifierInterface { /** * Return code challenge method. - * - * @return string */ - public function getMethod() + public function getMethod(): string { return 'plain'; } /** * Verify the code challenge. - * - * @param string $codeVerifier - * @param string $codeChallenge - * - * @return bool */ - public function verifyCodeChallenge($codeVerifier, $codeChallenge) + public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool { - return \hash_equals($codeVerifier, $codeChallenge); + return hash_equals($codeVerifier, $codeChallenge); } } diff --git a/src/CodeChallengeVerifiers/S256Verifier.php b/src/CodeChallengeVerifiers/S256Verifier.php index f70790a3c..7f99ead5f 100644 --- a/src/CodeChallengeVerifiers/S256Verifier.php +++ b/src/CodeChallengeVerifiers/S256Verifier.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Lukáš Unger @@ -7,32 +8,33 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\CodeChallengeVerifiers; +use function base64_encode; +use function hash; +use function hash_equals; +use function rtrim; +use function strtr; + class S256Verifier implements CodeChallengeVerifierInterface { /** * Return code challenge method. - * - * @return string */ - public function getMethod() + public function getMethod(): string { return 'S256'; } /** * Verify the code challenge. - * - * @param string $codeVerifier - * @param string $codeChallenge - * - * @return bool */ - public function verifyCodeChallenge($codeVerifier, $codeChallenge) + public function verifyCodeChallenge(string $codeVerifier, string $codeChallenge): bool { - return \hash_equals( - \strtr(\rtrim(\base64_encode(\hash('sha256', $codeVerifier, true)), '='), '+/', '-_'), + return hash_equals( + strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'), $codeChallenge ); } diff --git a/src/CryptKey.php b/src/CryptKey.php index 95f6cd613..e8ce076f7 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -1,4 +1,5 @@ passPhrase = $passPhrase; - if (\strpos($keyPath, self::FILE_PREFIX) !== 0 && $this->isValidKey($keyPath, $this->passPhrase ?? '')) { + if (strpos($keyPath, self::FILE_PREFIX) !== 0 && $this->isValidKey($keyPath, $this->passPhrase ?? '')) { $this->keyContents = $keyPath; $this->keyPath = ''; // There's no file, so no need for permission check. $keyPermissionsCheck = false; - } elseif (\is_file($keyPath)) { - if (\strpos($keyPath, self::FILE_PREFIX) !== 0) { + } elseif (is_file($keyPath)) { + if (strpos($keyPath, self::FILE_PREFIX) !== 0) { $keyPath = self::FILE_PREFIX . $keyPath; } - if (!\is_readable($keyPath)) { - throw new LogicException(\sprintf('Key path "%s" does not exist or is not readable', $keyPath)); + if (!is_readable($keyPath)) { + throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); } - $this->keyContents = \file_get_contents($keyPath); + + $keyContents = file_get_contents($keyPath); + + if ($keyContents === false) { + throw new LogicException('Unable to read key from file ' . $keyPath); + } + + $this->keyContents = $keyContents; $this->keyPath = $keyPath; + if (!$this->isValidKey($this->keyContents, $this->passPhrase ?? '')) { throw new LogicException('Unable to read key from file ' . $keyPath); } @@ -69,10 +76,10 @@ public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = if ($keyPermissionsCheck === true) { // Verify the permissions of the key - $keyPathPerms = \decoct(\fileperms($this->keyPath) & 0777); - if (\in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) { - \trigger_error( - \sprintf( + $keyPathPerms = decoct(fileperms($this->keyPath) & 0777); + if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) { + trigger_error( + sprintf( 'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s', $this->keyPath, $keyPathPerms @@ -84,9 +91,7 @@ public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = } /** - * Get key contents - * - * @return string Key contents + * {@inheritdoc} */ public function getKeyContents(): string { @@ -95,21 +100,20 @@ public function getKeyContents(): string /** * Validate key contents. - * - * @param string $contents - * @param string $passPhrase - * - * @return bool */ - private function isValidKey($contents, $passPhrase) + private function isValidKey(string $contents, string $passPhrase): bool { - $pkey = \openssl_pkey_get_private($contents, $passPhrase) ?: \openssl_pkey_get_public($contents); - if ($pkey === false) { + $privateKey = openssl_pkey_get_private($contents, $passPhrase); + + $key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents); + + if ($key === false) { return false; } - $details = \openssl_pkey_get_details($pkey); - return $details !== false && \in_array( + $details = openssl_pkey_get_details($key); + + return $details !== false && in_array( $details['type'] ?? -1, [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC], true @@ -119,7 +123,7 @@ private function isValidKey($contents, $passPhrase) /** * {@inheritdoc} */ - public function getKeyPath() + public function getKeyPath(): string { return $this->keyPath; } @@ -127,7 +131,7 @@ public function getKeyPath() /** * {@inheritdoc} */ - public function getPassPhrase() + public function getPassPhrase(): ?string { return $this->passPhrase; } diff --git a/src/CryptKeyInterface.php b/src/CryptKeyInterface.php index 86c7b0446..993ab4d52 100644 --- a/src/CryptKeyInterface.php +++ b/src/CryptKeyInterface.php @@ -1,5 +1,7 @@ encryptionKey instanceof Key) { return Crypto::encrypt($unencryptedData, $this->encryptionKey); } - if (\is_string($this->encryptionKey)) { + if (is_string($this->encryptionKey)) { return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); } @@ -52,20 +50,16 @@ protected function encrypt($unencryptedData) /** * Decrypt data with encryptionKey. * - * @param string $encryptedData - * * @throws LogicException - * - * @return string */ - protected function decrypt($encryptedData) + protected function decrypt(string $encryptedData): string { try { if ($this->encryptionKey instanceof Key) { return Crypto::decrypt($encryptedData, $this->encryptionKey); } - if (\is_string($this->encryptionKey)) { + if (is_string($this->encryptionKey)) { return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); } @@ -75,12 +69,7 @@ protected function decrypt($encryptedData) } } - /** - * Set the encryption key - * - * @param string|Key $key - */ - public function setEncryptionKey($key = null) + public function setEncryptionKey(Key|string|null $key = null): void { $this->encryptionKey = $key; } diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index 041d528b3..6eb3e7f6d 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities; use League\OAuth2\Server\CryptKeyInterface; @@ -16,10 +19,10 @@ interface AccessTokenEntityInterface extends TokenInterface /** * Set a private key used to encrypt the access token. */ - public function setPrivateKey(CryptKeyInterface $privateKey); + public function setPrivateKey(CryptKeyInterface $privateKey): void; /** * Generate a string representation of the access token. */ - public function __toString(); + public function __toString(): string; } diff --git a/src/Entities/AuthCodeEntityInterface.php b/src/Entities/AuthCodeEntityInterface.php index 00e939c2c..68c6a2f5b 100644 --- a/src/Entities/AuthCodeEntityInterface.php +++ b/src/Entities/AuthCodeEntityInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,17 +8,13 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities; interface AuthCodeEntityInterface extends TokenInterface { - /** - * @return string|null - */ - public function getRedirectUri(); + public function getRedirectUri(): string|null; - /** - * @param string $uri - */ - public function setRedirectUri($uri); + public function setRedirectUri(string $uri): void; } diff --git a/src/Entities/ClientEntityInterface.php b/src/Entities/ClientEntityInterface.php index 971c61244..9b1656679 100644 --- a/src/Entities/ClientEntityInterface.php +++ b/src/Entities/ClientEntityInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,37 +8,32 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities; interface ClientEntityInterface { /** * Get the client's identifier. - * - * @return string */ - public function getIdentifier(); + public function getIdentifier(): string; /** * Get the client's name. - * - * @return string */ - public function getName(); + public function getName(): string; /** - * Returns the registered redirect URI (as a string). - * - * Alternatively return an indexed array of redirect URIs. + * Returns the registered redirect URI (as a string). Alternatively return + * an indexed array of redirect URIs. * * @return string|string[] */ - public function getRedirectUri(); + public function getRedirectUri(): string|array; /** * Returns true if the client is confidential. - * - * @return bool */ - public function isConfidential(); + public function isConfidential(): bool; } diff --git a/src/Entities/RefreshTokenEntityInterface.php b/src/Entities/RefreshTokenEntityInterface.php index 551ad0527..f377cd9e1 100644 --- a/src/Entities/RefreshTokenEntityInterface.php +++ b/src/Entities/RefreshTokenEntityInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities; use DateTimeImmutable; @@ -15,43 +18,31 @@ interface RefreshTokenEntityInterface { /** * Get the token's identifier. - * - * @return string */ - public function getIdentifier(); + public function getIdentifier(): string; /** * Set the token's identifier. - * - * @param mixed $identifier */ - public function setIdentifier($identifier); + public function setIdentifier(mixed $identifier): void; /** * Get the token's expiry date time. - * - * @return DateTimeImmutable */ - public function getExpiryDateTime(); + public function getExpiryDateTime(): DateTimeImmutable; /** * Set the date time when the token expires. - * - * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(DateTimeImmutable $dateTime); + public function setExpiryDateTime(DateTimeImmutable $dateTime): void; /** * Set the access token that the refresh token was associated with. - * - * @param AccessTokenEntityInterface $accessToken */ - public function setAccessToken(AccessTokenEntityInterface $accessToken); + public function setAccessToken(AccessTokenEntityInterface $accessToken): void; /** * Get the access token that the refresh token was originally associated with. - * - * @return AccessTokenEntityInterface */ - public function getAccessToken(); + public function getAccessToken(): AccessTokenEntityInterface; } diff --git a/src/Entities/ScopeEntityInterface.php b/src/Entities/ScopeEntityInterface.php index 26748e0c0..37c40bf55 100644 --- a/src/Entities/ScopeEntityInterface.php +++ b/src/Entities/ScopeEntityInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities; use JsonSerializable; @@ -15,8 +18,6 @@ interface ScopeEntityInterface extends JsonSerializable { /** * Get the scope's identifier. - * - * @return string */ - public function getIdentifier(); + public function getIdentifier(): string; } diff --git a/src/Entities/TokenInterface.php b/src/Entities/TokenInterface.php index 7b063e138..5434ff00e 100644 --- a/src/Entities/TokenInterface.php +++ b/src/Entities/TokenInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities; use DateTimeImmutable; @@ -15,71 +18,53 @@ interface TokenInterface { /** * Get the token's identifier. - * - * @return string */ - public function getIdentifier(); + public function getIdentifier(): string; /** * Set the token's identifier. - * - * @param mixed $identifier */ - public function setIdentifier($identifier); + public function setIdentifier(mixed $identifier): void; /** * Get the token's expiry date time. - * - * @return DateTimeImmutable */ - public function getExpiryDateTime(); + public function getExpiryDateTime(): DateTimeImmutable; /** * Set the date time when the token expires. - * - * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(DateTimeImmutable $dateTime); + public function setExpiryDateTime(DateTimeImmutable $dateTime): void; /** * Set the identifier of the user associated with the token. - * - * @param string|int|null $identifier The identifier of the user */ - public function setUserIdentifier($identifier); + public function setUserIdentifier(string|int|null $identifier): void; /** * Get the token user's identifier. - * - * @return string|int|null */ - public function getUserIdentifier(); + public function getUserIdentifier(): string|int|null; /** * Get the client that the token was issued to. - * - * @return ClientEntityInterface */ - public function getClient(); + public function getClient(): ClientEntityInterface; /** * Set the client that the token was issued to. - * - * @param ClientEntityInterface $client */ - public function setClient(ClientEntityInterface $client); + public function setClient(ClientEntityInterface $client): void; /** * Associate a scope with the token. - * - * @param ScopeEntityInterface $scope */ - public function addScope(ScopeEntityInterface $scope); + public function addScope(ScopeEntityInterface $scope): void; /** * Return an array of scopes associated with the token. * * @return ScopeEntityInterface[] */ - public function getScopes(); + public function getScopes(): array; } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 65537ac9b..9d9fe4a65 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities\Traits; use DateTimeImmutable; @@ -17,23 +20,18 @@ use League\OAuth2\Server\CryptKeyInterface; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; +use RuntimeException; trait AccessTokenTrait { - /** - * @var CryptKeyInterface - */ - private $privateKey; + private CryptKeyInterface $privateKey; - /** - * @var Configuration - */ - private $jwtConfiguration; + private Configuration $jwtConfiguration; /** * Set the private key used to encrypt this access token. */ - public function setPrivateKey(CryptKeyInterface $privateKey) + public function setPrivateKey(CryptKeyInterface $privateKey): void { $this->privateKey = $privateKey; } @@ -41,21 +39,25 @@ public function setPrivateKey(CryptKeyInterface $privateKey) /** * Initialise the JWT Configuration. */ - public function initJwtConfiguration() + public function initJwtConfiguration(): void { + $privateKeyContents = $this->privateKey->getKeyContents(); + + if ($privateKeyContents === '') { + throw new RuntimeException('Private key is empty'); + } + $this->jwtConfiguration = Configuration::forAsymmetricSigner( new Sha256(), - InMemory::plainText($this->privateKey->getKeyContents(), $this->privateKey->getPassPhrase() ?? ''), + InMemory::plainText($privateKeyContents, $this->privateKey->getPassPhrase() ?? ''), InMemory::plainText('empty', 'empty') ); } /** * Generate a JWT from the access token - * - * @return Token */ - private function convertToJWT() + private function convertToJWT(): Token { $this->initJwtConfiguration(); @@ -72,34 +74,24 @@ private function convertToJWT() /** * Generate a string representation from the access token + * + * TODO: Want to remove this function. */ - public function __toString() + public function __toString(): string { return $this->convertToJWT()->toString(); } - /** - * @return ClientEntityInterface - */ - abstract public function getClient(); + abstract public function getClient(): ClientEntityInterface; - /** - * @return DateTimeImmutable - */ - abstract public function getExpiryDateTime(); + abstract public function getExpiryDateTime(): DateTimeImmutable; - /** - * @return string|int - */ - abstract public function getUserIdentifier(); + abstract public function getUserIdentifier(): string|int|null; /** * @return ScopeEntityInterface[] */ - abstract public function getScopes(); + abstract public function getScopes(): array; - /** - * @return string - */ - abstract public function getIdentifier(); + abstract public function getIdentifier(): string; } diff --git a/src/Entities/Traits/AuthCodeTrait.php b/src/Entities/Traits/AuthCodeTrait.php index 542d36789..403500b62 100644 --- a/src/Entities/Traits/AuthCodeTrait.php +++ b/src/Entities/Traits/AuthCodeTrait.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,27 +8,20 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities\Traits; trait AuthCodeTrait { - /** - * @var null|string - */ - protected $redirectUri; + protected ?string $redirectUri = null; - /** - * @return string|null - */ - public function getRedirectUri() + public function getRedirectUri(): string|null { return $this->redirectUri; } - /** - * @param string $uri - */ - public function setRedirectUri($uri) + public function setRedirectUri(string $uri): void { $this->redirectUri = $uri; } diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php index 370163c35..b179cfac4 100644 --- a/src/Entities/Traits/ClientTrait.php +++ b/src/Entities/Traits/ClientTrait.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,55 +8,47 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities\Traits; trait ClientTrait { - /** - * @var string - */ - protected $name; + protected string $name; /** * @var string|string[] */ - protected $redirectUri; + protected string|array $redirectUri; - /** - * @var bool - */ - protected $isConfidential = false; + protected bool $isConfidential = false; /** * Get the client's name. * - * @return string * * @codeCoverageIgnore */ - public function getName() + public function getName(): string { return $this->name; } /** - * Returns the registered redirect URI (as a string). - * - * Alternatively return an indexed array of redirect URIs. + * Returns the registered redirect URI (as a string). Alternatively return + * an indexed array of redirect URIs. * * @return string|string[] */ - public function getRedirectUri() + public function getRedirectUri(): string|array { return $this->redirectUri; } /** * Returns true if the client is confidential. - * - * @return bool */ - public function isConfidential() + public function isConfidential(): bool { return $this->isConfidential; } diff --git a/src/Entities/Traits/EntityTrait.php b/src/Entities/Traits/EntityTrait.php index 05452923a..8b3db91d4 100644 --- a/src/Entities/Traits/EntityTrait.php +++ b/src/Entities/Traits/EntityTrait.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,27 +8,20 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities\Traits; trait EntityTrait { - /** - * @var string - */ - protected $identifier; + protected string $identifier; - /** - * @return mixed - */ - public function getIdentifier() + public function getIdentifier(): string { return $this->identifier; } - /** - * @param mixed $identifier - */ - public function setIdentifier($identifier) + public function setIdentifier(mixed $identifier): void { $this->identifier = $identifier; } diff --git a/src/Entities/Traits/RefreshTokenTrait.php b/src/Entities/Traits/RefreshTokenTrait.php index f0f15444c..a0d4c8885 100644 --- a/src/Entities/Traits/RefreshTokenTrait.php +++ b/src/Entities/Traits/RefreshTokenTrait.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities\Traits; use DateTimeImmutable; @@ -14,20 +17,14 @@ trait RefreshTokenTrait { - /** - * @var AccessTokenEntityInterface - */ - protected $accessToken; + protected AccessTokenEntityInterface $accessToken; - /** - * @var DateTimeImmutable - */ - protected $expiryDateTime; + protected DateTimeImmutable $expiryDateTime; /** * {@inheritdoc} */ - public function setAccessToken(AccessTokenEntityInterface $accessToken) + public function setAccessToken(AccessTokenEntityInterface $accessToken): void { $this->accessToken = $accessToken; } @@ -35,27 +32,23 @@ public function setAccessToken(AccessTokenEntityInterface $accessToken) /** * {@inheritdoc} */ - public function getAccessToken() + public function getAccessToken(): AccessTokenEntityInterface { return $this->accessToken; } /** * Get the token's expiry date time. - * - * @return DateTimeImmutable */ - public function getExpiryDateTime() + public function getExpiryDateTime(): DateTimeImmutable { return $this->expiryDateTime; } /** * Set the date time when the token expires. - * - * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(DateTimeImmutable $dateTime) + public function setExpiryDateTime(DateTimeImmutable $dateTime): void { $this->expiryDateTime = $dateTime; } diff --git a/src/Entities/Traits/ScopeTrait.php b/src/Entities/Traits/ScopeTrait.php index 7eacc3359..71bedac76 100644 --- a/src/Entities/Traits/ScopeTrait.php +++ b/src/Entities/Traits/ScopeTrait.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Andrew Millington @@ -7,23 +8,19 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities\Traits; trait ScopeTrait { /** * Serialize the object to the scopes string identifier when using json_encode(). - * - * @return string */ - #[\ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): string { return $this->getIdentifier(); } - /** - * @return string - */ - abstract public function getIdentifier(); + abstract public function getIdentifier(): string; } diff --git a/src/Entities/Traits/TokenEntityTrait.php b/src/Entities/Traits/TokenEntityTrait.php index 83b172322..06ef15fb1 100644 --- a/src/Entities/Traits/TokenEntityTrait.php +++ b/src/Entities/Traits/TokenEntityTrait.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,40 +8,33 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities\Traits; use DateTimeImmutable; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; +use function array_values; + trait TokenEntityTrait { /** * @var ScopeEntityInterface[] */ - protected $scopes = []; + protected array $scopes = []; - /** - * @var DateTimeImmutable - */ - protected $expiryDateTime; + protected DateTimeImmutable $expiryDateTime; - /** - * @var string|int|null - */ - protected $userIdentifier; + protected string|int|null $userIdentifier = null; - /** - * @var ClientEntityInterface - */ - protected $client; + protected ClientEntityInterface $client; /** * Associate a scope with the token. - * - * @param ScopeEntityInterface $scope */ - public function addScope(ScopeEntityInterface $scope) + public function addScope(ScopeEntityInterface $scope): void { $this->scopes[$scope->getIdentifier()] = $scope; } @@ -50,27 +44,23 @@ public function addScope(ScopeEntityInterface $scope) * * @return ScopeEntityInterface[] */ - public function getScopes() + public function getScopes(): array { - return \array_values($this->scopes); + return array_values($this->scopes); } /** * Get the token's expiry date time. - * - * @return DateTimeImmutable */ - public function getExpiryDateTime() + public function getExpiryDateTime(): DateTimeImmutable { return $this->expiryDateTime; } /** * Set the date time when the token expires. - * - * @param DateTimeImmutable $dateTime */ - public function setExpiryDateTime(DateTimeImmutable $dateTime) + public function setExpiryDateTime(DateTimeImmutable $dateTime): void { $this->expiryDateTime = $dateTime; } @@ -80,37 +70,31 @@ public function setExpiryDateTime(DateTimeImmutable $dateTime) * * @param string|int|null $identifier The identifier of the user */ - public function setUserIdentifier($identifier) + public function setUserIdentifier(string|int|null $identifier): void { $this->userIdentifier = $identifier; } /** * Get the token user's identifier. - * - * @return string|int|null */ - public function getUserIdentifier() + public function getUserIdentifier(): string|int|null { return $this->userIdentifier; } /** * Get the client that the token was issued to. - * - * @return ClientEntityInterface */ - public function getClient() + public function getClient(): ClientEntityInterface { return $this->client; } /** * Set the client that the token was issued to. - * - * @param ClientEntityInterface $client */ - public function setClient(ClientEntityInterface $client) + public function setClient(ClientEntityInterface $client): void { $this->client = $client; } diff --git a/src/Entities/UserEntityInterface.php b/src/Entities/UserEntityInterface.php index c71cb9c52..ef88a84d2 100644 --- a/src/Entities/UserEntityInterface.php +++ b/src/Entities/UserEntityInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,14 +8,14 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Entities; interface UserEntityInterface { /** * Return the user's identifier. - * - * @return mixed */ - public function getIdentifier(); + public function getIdentifier(): mixed; } diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index fc1b9f022..08bbb9538 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Exception; use Exception; @@ -14,60 +17,32 @@ use Psr\Http\Message\ServerRequestInterface; use Throwable; +use function htmlspecialchars; +use function http_build_query; +use function sprintf; +use function strpos; +use function strstr; + class OAuthServerException extends Exception { /** - * @var int + * @var array */ - private $httpStatusCode; + private array $payload; - /** - * @var string - */ - private $errorType; - - /** - * @var null|string - */ - private $hint; - - /** - * @var null|string - */ - private $redirectUri; - - /** - * @var array - */ - private $payload; - - /** - * @var ServerRequestInterface - */ - private $serverRequest; + private ServerRequestInterface $serverRequest; /** * Throw a new exception. - * - * @param string $message Error message - * @param int $code Error code - * @param string $errorType Error type - * @param int $httpStatusCode HTTP status code to send (default = 400) - * @param null|string $hint A helper hint - * @param null|string $redirectUri A HTTP URI to redirect the user back to - * @param Throwable $previous Previous exception */ - public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, Throwable $previous = null) + final public function __construct(string $message, int $code, private string $errorType, private int $httpStatusCode = 400, private ?string $hint = null, private ?string $redirectUri = null, Throwable $previous = null) { parent::__construct($message, $code, $previous); - $this->httpStatusCode = $httpStatusCode; - $this->errorType = $errorType; - $this->hint = $hint; - $this->redirectUri = $redirectUri; $this->payload = [ 'error' => $errorType, 'error_description' => $message, ]; + if ($hint !== null) { $this->payload['hint'] = $hint; } @@ -76,47 +51,35 @@ public function __construct($message, $code, $errorType, $httpStatusCode = 400, /** * Returns the current payload. * - * @return array + * @return array */ - public function getPayload() + public function getPayload(): array { - $payload = $this->payload; - - // The "message" property is deprecated and replaced by "error_description" - // TODO: remove "message" property - if (isset($payload['error_description']) && !isset($payload['message'])) { - $payload['message'] = $payload['error_description']; - } - - return $payload; + return $this->payload; } /** * Updates the current payload. * - * @param array $payload + * @param array $payload */ - public function setPayload(array $payload) + public function setPayload(array $payload): void { $this->payload = $payload; } /** * Set the server request that is responsible for generating the exception - * - * @param ServerRequestInterface $serverRequest */ - public function setServerRequest(ServerRequestInterface $serverRequest) + public function setServerRequest(ServerRequestInterface $serverRequest): void { $this->serverRequest = $serverRequest; } /** * Unsupported grant type error. - * - * @return static */ - public static function unsupportedGrantType() + public static function unsupportedGrantType(): static { $errorMessage = 'The authorization grant type is not supported by the authorization server.'; $hint = 'Check that all required parameters have been provided'; @@ -126,30 +89,20 @@ public static function unsupportedGrantType() /** * Invalid request error. - * - * @param string $parameter The invalid parameter - * @param null|string $hint - * @param Throwable $previous Previous exception - * - * @return static */ - public static function invalidRequest($parameter, $hint = null, Throwable $previous = null) + public static function invalidRequest(string $parameter, ?string $hint = null, Throwable $previous = null): static { $errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' . 'includes a parameter more than once, or is otherwise malformed.'; - $hint = ($hint === null) ? \sprintf('Check the `%s` parameter', $parameter) : $hint; + $hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint; return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous); } /** * Invalid client error. - * - * @param ServerRequestInterface $serverRequest - * - * @return static */ - public static function invalidClient(ServerRequestInterface $serverRequest) + public static function invalidClient(ServerRequestInterface $serverRequest): static { $exception = new static('Client authentication failed', 4, 'invalid_client', 401); @@ -159,23 +112,18 @@ public static function invalidClient(ServerRequestInterface $serverRequest) } /** - * Invalid scope error. - * - * @param string $scope The bad scope - * @param null|string $redirectUri A HTTP URI to redirect the user back to - * - * @return static + * Invalid scope error */ - public static function invalidScope($scope, $redirectUri = null) + public static function invalidScope(string $scope, string|null $redirectUri = null): static { $errorMessage = 'The requested scope is invalid, unknown, or malformed'; - if (empty($scope)) { + if ($scope === '') { $hint = 'Specify a scope in the request or set a default scope'; } else { - $hint = \sprintf( + $hint = sprintf( 'Check the `%s` scope', - \htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false) + htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false) ); } @@ -184,10 +132,8 @@ public static function invalidScope($scope, $redirectUri = null) /** * Invalid credentials error. - * - * @return static */ - public static function invalidCredentials() + public static function invalidCredentials(): static { return new static('The user credentials were incorrect.', 6, 'invalid_grant', 400); } @@ -195,14 +141,9 @@ public static function invalidCredentials() /** * Server error. * - * @param string $hint - * @param Throwable $previous - * - * @return static - * * @codeCoverageIgnore */ - public static function serverError($hint, Throwable $previous = null) + public static function serverError(string $hint, Throwable $previous = null): static { return new static( 'The authorization server encountered an unexpected condition which prevented it from fulfilling' @@ -218,27 +159,16 @@ public static function serverError($hint, Throwable $previous = null) /** * Invalid refresh token. - * - * @param null|string $hint - * @param Throwable $previous - * - * @return static */ - public static function invalidRefreshToken($hint = null, Throwable $previous = null) + public static function invalidRefreshToken(?string $hint = null, Throwable $previous = null): static { return new static('The refresh token is invalid.', 8, 'invalid_grant', 400, $hint, null, $previous); } /** * Access denied. - * - * @param null|string $hint - * @param null|string $redirectUri - * @param Throwable $previous - * - * @return static */ - public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null) + public static function accessDenied(?string $hint = null, ?string $redirectUri = null, Throwable $previous = null): static { return new static( 'The resource owner or authorization server denied the request.', @@ -253,12 +183,8 @@ public static function accessDenied($hint = null, $redirectUri = null, Throwable /** * Invalid grant. - * - * @param string $hint - * - * @return static */ - public static function invalidGrant($hint = '') + public static function invalidGrant(string $hint = ''): static { return new static( 'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token ' @@ -271,24 +197,15 @@ public static function invalidGrant($hint = '') ); } - /** - * @return string - */ - public function getErrorType() + public function getErrorType(): string { return $this->errorType; } /** * Generate a HTTP response. - * - * @param ResponseInterface $response - * @param bool $useFragment True if errors should be in the URI fragment instead of query string - * @param int $jsonOptions options passed to json_encode - * - * @return ResponseInterface */ - public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0) + public function generateHttpResponse(ResponseInterface $response, bool $useFragment = false, int $jsonOptions = 0): ResponseInterface { $headers = $this->getHttpHeaders(); @@ -296,19 +213,21 @@ public function generateHttpResponse(ResponseInterface $response, $useFragment = if ($this->redirectUri !== null) { if ($useFragment === true) { - $this->redirectUri .= (\strstr($this->redirectUri, '#') === false) ? '#' : '&'; + $this->redirectUri .= (strstr($this->redirectUri, '#') === false) ? '#' : '&'; } else { - $this->redirectUri .= (\strstr($this->redirectUri, '?') === false) ? '?' : '&'; + $this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&'; } - return $response->withStatus(302)->withHeader('Location', $this->redirectUri . \http_build_query($payload)); + return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload)); } foreach ($headers as $header => $content) { $response = $response->withHeader($header, $content); } - $responseBody = \json_encode($payload, $jsonOptions) ?: 'JSON encoding of payload failed'; + $jsonEncodedPayload = json_encode($payload, $jsonOptions); + + $responseBody = $jsonEncodedPayload === false ? 'JSON encoding of payload failed' : $jsonEncodedPayload; $response->getBody()->write($responseBody); @@ -318,9 +237,9 @@ public function generateHttpResponse(ResponseInterface $response, $useFragment = /** * Get all headers that have to be send with the error response. * - * @return array Array with header values + * @return array Array with header values */ - public function getHttpHeaders() + public function getHttpHeaders(): array { $headers = [ 'Content-type' => 'application/json', @@ -335,7 +254,7 @@ public function getHttpHeaders() // include the "WWW-Authenticate" response header field // matching the authentication scheme used by the client. if ($this->errorType === 'invalid_client' && $this->requestHasAuthorizationHeader()) { - $authScheme = \strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic'; + $authScheme = strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic'; $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"'; } @@ -350,38 +269,29 @@ public function getHttpHeaders() * getHttpStatusCode() doesn't return a 302 when there's a * redirect enabled. This helps when you want to override local * error pages but want to let redirects through. - * - * @return bool */ - public function hasRedirect() + public function hasRedirect(): bool { return $this->redirectUri !== null; } /** * Returns the Redirect URI used for redirecting. - * - * @return string|null */ - public function getRedirectUri() + public function getRedirectUri(): ?string { return $this->redirectUri; } /** * Returns the HTTP status code to send when the exceptions is output. - * - * @return int */ - public function getHttpStatusCode() + public function getHttpStatusCode(): int { return $this->httpStatusCode; } - /** - * @return null|string - */ - public function getHint() + public function getHint(): ?string { return $this->hint; } @@ -392,9 +302,8 @@ public function getHint() * Returns true if the header is present and not an empty string, false * otherwise. * - * @return bool */ - private function requestHasAuthorizationHeader() + private function requestHasAuthorizationHeader(): bool { if (!$this->serverRequest->hasHeader('Authorization')) { return false; @@ -407,7 +316,7 @@ private function requestHasAuthorizationHeader() // For practical purposes that case should be treated as though the // header isn't present. // See https://github.com/thephpleague/oauth2-server/issues/1162 - if (empty($authorizationHeader) || empty($authorizationHeader[0])) { + if ($authorizationHeader === [] || $authorizationHeader[0] === '') { return false; } diff --git a/src/Exception/UniqueTokenIdentifierConstraintViolationException.php b/src/Exception/UniqueTokenIdentifierConstraintViolationException.php index 175ab3a88..4c2f281f6 100644 --- a/src/Exception/UniqueTokenIdentifierConstraintViolationException.php +++ b/src/Exception/UniqueTokenIdentifierConstraintViolationException.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,14 +8,13 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Exception; class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException { - /** - * @return UniqueTokenIdentifierConstraintViolationException - */ - public static function create() + public static function create(): UniqueTokenIdentifierConstraintViolationException { $errorMessage = 'Could not create unique access token identifier'; diff --git a/src/Grant/AbstractAuthorizeGrant.php b/src/Grant/AbstractAuthorizeGrant.php index 3a8bb4074..27d39982b 100644 --- a/src/Grant/AbstractAuthorizeGrant.php +++ b/src/Grant/AbstractAuthorizeGrant.php @@ -1,4 +1,5 @@ clientRepository = $clientRepository; } - /** - * @param AccessTokenRepositoryInterface $accessTokenRepository - */ - public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository) + public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository): void { $this->accessTokenRepository = $accessTokenRepository; } - /** - * @param ScopeRepositoryInterface $scopeRepository - */ - public function setScopeRepository(ScopeRepositoryInterface $scopeRepository) + public function setScopeRepository(ScopeRepositoryInterface $scopeRepository): void { $this->scopeRepository = $scopeRepository; } - /** - * @param RefreshTokenRepositoryInterface $refreshTokenRepository - */ - public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository) + public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository): void { $this->refreshTokenRepository = $refreshTokenRepository; } - /** - * @param AuthCodeRepositoryInterface $authCodeRepository - */ - public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository) + public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository): void { $this->authCodeRepository = $authCodeRepository; } - /** - * @param UserRepositoryInterface $userRepository - */ - public function setUserRepository(UserRepositoryInterface $userRepository) + public function setUserRepository(UserRepositoryInterface $userRepository): void { $this->userRepository = $userRepository; } @@ -149,33 +120,25 @@ public function setUserRepository(UserRepositoryInterface $userRepository) /** * {@inheritdoc} */ - public function setRefreshTokenTTL(DateInterval $refreshTokenTTL) + public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void { $this->refreshTokenTTL = $refreshTokenTTL; } /** * Set the private key - * - * @param CryptKeyInterface $key */ - public function setPrivateKey(CryptKeyInterface $key) + public function setPrivateKey(CryptKeyInterface $key): void { $this->privateKey = $key; } - /** - * @param string $scope - */ - public function setDefaultScope($scope) + public function setDefaultScope(string $scope): void { $this->defaultScope = $scope; } - /** - * @param bool $revokeRefreshTokens - */ - public function revokeRefreshTokens(bool $revokeRefreshTokens) + public function revokeRefreshTokens(bool $revokeRefreshTokens): void { $this->revokeRefreshTokens = $revokeRefreshTokens; } @@ -183,13 +146,9 @@ public function revokeRefreshTokens(bool $revokeRefreshTokens) /** * Validate the client. * - * @param ServerRequestInterface $request - * * @throws OAuthServerException - * - * @return ClientEntityInterface */ - protected function validateClient(ServerRequestInterface $request) + protected function validateClient(ServerRequestInterface $request): ClientEntityInterface { [$clientId, $clientSecret] = $this->getClientCredentials($request); @@ -205,7 +164,7 @@ protected function validateClient(ServerRequestInterface $request) $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); if ($redirectUri !== null) { - if (!\is_string($redirectUri)) { + if (!is_string($redirectUri)) { throw OAuthServerException::invalidRequest('redirect_uri'); } @@ -225,12 +184,9 @@ protected function validateClient(ServerRequestInterface $request) * getClientEntity might return null. By contrast, this method will * always either return a ClientEntityInterface or throw. * - * @param string $clientId - * @param ServerRequestInterface $request - * - * @return ClientEntityInterface + * TODO: Check if we still need this */ - protected function getClientEntityOrFail($clientId, ServerRequestInterface $request) + protected function getClientEntityOrFail(string $clientId, ServerRequestInterface $request): ClientEntityInterface { $client = $this->clientRepository->getClientEntity($clientId); @@ -246,23 +202,21 @@ protected function getClientEntityOrFail($clientId, ServerRequestInterface $requ * Gets the client credentials from the request from the request body or * the Http Basic Authorization header * - * @param ServerRequestInterface $request - * - * @return array + * @return string[] */ - protected function getClientCredentials(ServerRequestInterface $request) + protected function getClientCredentials(ServerRequestInterface $request): array { [$basicAuthUser, $basicAuthPassword] = $this->getBasicAuthCredentials($request); $clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser); - if (\is_null($clientId)) { + if (is_null($clientId)) { throw OAuthServerException::invalidRequest('client_id'); } $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword); - if ($clientSecret !== null && !\is_string($clientSecret)) { + if ($clientSecret !== null && !is_string($clientSecret)) { throw OAuthServerException::invalidRequest('client_secret'); } @@ -270,12 +224,8 @@ protected function getClientCredentials(ServerRequestInterface $request) } /** - * Validate redirectUri from the request. - * If a redirect URI is provided ensure it matches what is pre-registered - * - * @param string $redirectUri - * @param ClientEntityInterface $client - * @param ServerRequestInterface $request + * Validate redirectUri from the request. If a redirect URI is provided + * ensure it matches what is pre-registered * * @throws OAuthServerException */ @@ -283,8 +233,9 @@ protected function validateRedirectUri( string $redirectUri, ClientEntityInterface $client, ServerRequestInterface $request - ) { + ): void { $validator = new RedirectUriValidator($client->getRedirectUri()); + if (!$validator->validateRedirectUri($redirectUri)) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient($request); @@ -294,25 +245,20 @@ protected function validateRedirectUri( /** * Validate scopes in the request. * - * @param string|array|null $scopes - * @param string $redirectUri + * @param null|string|string[] $scopes * * @throws OAuthServerException * * @return ScopeEntityInterface[] */ - public function validateScopes($scopes, $redirectUri = null) + public function validateScopes(string|array|null $scopes, string $redirectUri = null): array { if ($scopes === null) { $scopes = []; - } elseif (\is_string($scopes)) { + } elseif (is_string($scopes)) { $scopes = $this->convertScopesQueryStringToArray($scopes); } - if (!\is_array($scopes)) { - throw OAuthServerException::invalidRequest('scope'); - } - $validScopes = []; foreach ($scopes as $scopeItem) { @@ -331,27 +277,19 @@ public function validateScopes($scopes, $redirectUri = null) /** * Converts a scopes query string to an array to easily iterate for validation. * - * @param string $scopes - * - * @return array + * @return string[] */ - private function convertScopesQueryStringToArray(string $scopes) + private function convertScopesQueryStringToArray(string $scopes): array { - return \array_filter(\explode(self::SCOPE_DELIMITER_STRING, \trim($scopes)), function ($scope) { + return array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) { return $scope !== ''; }); } /** * Retrieve request parameter. - * - * @param string $parameter - * @param ServerRequestInterface $request - * @param mixed $default - * - * @return null|string */ - protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null) + protected function getRequestParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): mixed { $requestParameters = (array) $request->getParsedBody(); @@ -365,70 +303,52 @@ protected function getRequestParameter($parameter, ServerRequestInterface $reque * not exist, or is otherwise an invalid HTTP Basic header, return * [null, null]. * - * @param ServerRequestInterface $request - * * @return string[]|null[] */ - protected function getBasicAuthCredentials(ServerRequestInterface $request) + protected function getBasicAuthCredentials(ServerRequestInterface $request): array { if (!$request->hasHeader('Authorization')) { return [null, null]; } $header = $request->getHeader('Authorization')[0]; - if (\strpos($header, 'Basic ') !== 0) { + if (strpos($header, 'Basic ') !== 0) { return [null, null]; } - if (!($decoded = \base64_decode(\substr($header, 6)))) { + $decoded = base64_decode(substr($header, 6), true); + + if ($decoded === false) { return [null, null]; } - if (\strpos($decoded, ':') === false) { + if (strpos($decoded, ':') === false) { return [null, null]; // HTTP Basic header without colon isn't valid } - return \explode(':', $decoded, 2); + return explode(':', $decoded, 2); } /** * Retrieve query string parameter. - * - * @param string $parameter - * @param ServerRequestInterface $request - * @param mixed $default - * - * @return null|string */ - protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null) + protected function getQueryStringParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): ?string { return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default; } /** * Retrieve cookie parameter. - * - * @param string $parameter - * @param ServerRequestInterface $request - * @param mixed $default - * - * @return null|string */ - protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null) + protected function getCookieParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): ?string { return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default; } /** * Retrieve server parameter. - * - * @param string $parameter - * @param ServerRequestInterface $request - * @param mixed $default - * - * @return null|string */ - protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null) + protected function getServerParameter(string $parameter, ServerRequestInterface $request, mixed $default = null): ?string { return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default; } @@ -436,22 +356,18 @@ protected function getServerParameter($parameter, ServerRequestInterface $reques /** * Issue an access token. * - * @param DateInterval $accessTokenTTL - * @param ClientEntityInterface $client - * @param string|null $userIdentifier * @param ScopeEntityInterface[] $scopes * * @throws OAuthServerException * @throws UniqueTokenIdentifierConstraintViolationException * - * @return AccessTokenEntityInterface */ protected function issueAccessToken( DateInterval $accessTokenTTL, ClientEntityInterface $client, - $userIdentifier, + string|int|null $userIdentifier, array $scopes = [] - ) { + ): AccessTokenEntityInterface { $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; $accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier); @@ -470,29 +386,26 @@ protected function issueAccessToken( } } } + + // This should never be hit. It is here to work around a PHPStan false error + return $accessToken; } /** * Issue an auth code. * - * @param DateInterval $authCodeTTL - * @param ClientEntityInterface $client - * @param string $userIdentifier - * @param string|null $redirectUri * @param ScopeEntityInterface[] $scopes * * @throws OAuthServerException * @throws UniqueTokenIdentifierConstraintViolationException - * - * @return AuthCodeEntityInterface */ protected function issueAuthCode( DateInterval $authCodeTTL, ClientEntityInterface $client, - $userIdentifier, - $redirectUri, + string $userIdentifier, + ?string $redirectUri, array $scopes = [] - ) { + ): AuthCodeEntityInterface { $maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS; $authCode = $this->authCodeRepository->getNewAuthCode(); @@ -520,17 +433,16 @@ protected function issueAuthCode( } } } + + // This should never be hit. It is here to work around a PHPStan false error + return $authCode; } /** - * @param AccessTokenEntityInterface $accessToken - * * @throws OAuthServerException * @throws UniqueTokenIdentifierConstraintViolationException - * - * @return RefreshTokenEntityInterface|null */ - protected function issueRefreshToken(AccessTokenEntityInterface $accessToken) + protected function issueRefreshToken(AccessTokenEntityInterface $accessToken): ?RefreshTokenEntityInterface { $refreshToken = $this->refreshTokenRepository->getNewRefreshToken(); @@ -555,25 +467,26 @@ protected function issueRefreshToken(AccessTokenEntityInterface $accessToken) } } } + + // This should never be hit. It is here to work around a PHPStan false error + return $refreshToken; } /** * Generate a new unique identifier. * - * @param int $length - * * @throws OAuthServerException - * - * @return string */ - protected function generateUniqueIdentifier($length = 40) + protected function generateUniqueIdentifier(int $length = 40): string { try { - return \bin2hex(\random_bytes($length)); + if ($length < 1) { + throw new DomainException('Length must be a positive integer'); + } + + return bin2hex(random_bytes($length)); // @codeCoverageIgnoreStart - } catch (TypeError $e) { - throw OAuthServerException::serverError('An unexpected error has occurred', $e); - } catch (Error $e) { + } catch (TypeError | Error $e) { throw OAuthServerException::serverError('An unexpected error has occurred', $e); } catch (Exception $e) { // If you get this message, the CSPRNG failed hard. @@ -585,12 +498,12 @@ protected function generateUniqueIdentifier($length = 40) /** * {@inheritdoc} */ - public function canRespondToAccessTokenRequest(ServerRequestInterface $request) + public function canRespondToAccessTokenRequest(ServerRequestInterface $request): bool { $requestParameters = (array) $request->getParsedBody(); return ( - \array_key_exists('grant_type', $requestParameters) + array_key_exists('grant_type', $requestParameters) && $requestParameters['grant_type'] === $this->getIdentifier() ); } @@ -598,7 +511,7 @@ public function canRespondToAccessTokenRequest(ServerRequestInterface $request) /** * {@inheritdoc} */ - public function canRespondToAuthorizationRequest(ServerRequestInterface $request) + public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool { return false; } @@ -606,7 +519,7 @@ public function canRespondToAuthorizationRequest(ServerRequestInterface $request /** * {@inheritdoc} */ - public function validateAuthorizationRequest(ServerRequestInterface $request) + public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface { throw new LogicException('This grant cannot validate an authorization request'); } @@ -614,7 +527,7 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) /** * {@inheritdoc} */ - public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest) + public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): ResponseTypeInterface { throw new LogicException('This grant cannot complete an authorization request'); } diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index 01b463ddb..83bddac18 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Grant; use DateInterval; @@ -24,48 +27,50 @@ use League\OAuth2\Server\RequestEvent; use League\OAuth2\Server\RequestRefreshTokenEvent; use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface; -use League\OAuth2\Server\RequestTypes\AuthorizationRequest; use League\OAuth2\Server\ResponseTypes\RedirectResponse; use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; use LogicException; use Psr\Http\Message\ServerRequestInterface; use stdClass; +use function array_key_exists; +use function array_keys; +use function array_map; +use function count; +use function hash_algos; +use function implode; +use function in_array; +use function is_array; +use function is_string; +use function json_decode; +use function json_encode; +use function preg_match; +use function property_exists; +use function sprintf; +use function time; + class AuthCodeGrant extends AbstractAuthorizeGrant { - /** - * @var DateInterval - */ - private $authCodeTTL; - - /** - * @var bool - */ - private $requireCodeChallengeForPublicClients = true; + private bool $requireCodeChallengeForPublicClients = true; /** * @var CodeChallengeVerifierInterface[] */ - private $codeChallengeVerifiers = []; + private array $codeChallengeVerifiers = []; /** - * @param AuthCodeRepositoryInterface $authCodeRepository - * @param RefreshTokenRepositoryInterface $refreshTokenRepository - * @param DateInterval $authCodeTTL - * * @throws Exception */ public function __construct( AuthCodeRepositoryInterface $authCodeRepository, RefreshTokenRepositoryInterface $refreshTokenRepository, - DateInterval $authCodeTTL + private DateInterval $authCodeTTL ) { $this->setAuthCodeRepository($authCodeRepository); $this->setRefreshTokenRepository($refreshTokenRepository); - $this->authCodeTTL = $authCodeTTL; $this->refreshTokenTTL = new DateInterval('P1M'); - if (\in_array('sha256', \hash_algos(), true)) { + if (in_array('sha256', hash_algos(), true)) { $s256Verifier = new S256Verifier(); $this->codeChallengeVerifiers[$s256Verifier->getMethod()] = $s256Verifier; } @@ -77,7 +82,7 @@ public function __construct( /** * Disable the requirement for a code challenge for public clients. */ - public function disableRequireCodeChallengeForPublicClients() + public function disableRequireCodeChallengeForPublicClients(): void { $this->requireCodeChallengeForPublicClients = false; } @@ -85,19 +90,13 @@ public function disableRequireCodeChallengeForPublicClients() /** * Respond to an access token request. * - * @param ServerRequestInterface $request - * @param ResponseTypeInterface $responseType - * @param DateInterval $accessTokenTTL - * * @throws OAuthServerException - * - * @return ResponseTypeInterface */ public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, DateInterval $accessTokenTTL - ) { + ): ResponseTypeInterface { list($clientId) = $this->getClientCredentials($request); $client = $this->getClientEntityOrFail($clientId, $request); @@ -109,12 +108,12 @@ public function respondToAccessTokenRequest( $encryptedAuthCode = $this->getRequestParameter('code', $request, null); - if (!\is_string($encryptedAuthCode)) { + if (!is_string($encryptedAuthCode)) { throw OAuthServerException::invalidRequest('code'); } try { - $authCodePayload = \json_decode($this->decrypt($encryptedAuthCode)); + $authCodePayload = json_decode($this->decrypt($encryptedAuthCode)); $this->validateAuthorizationCode($authCodePayload, $client, $request); @@ -132,14 +131,14 @@ public function respondToAccessTokenRequest( $codeVerifier = $this->getRequestParameter('code_verifier', $request, null); // If a code challenge isn't present but a code verifier is, reject the request to block PKCE downgrade attack - if (empty($authCodePayload->code_challenge) && $codeVerifier !== null) { + if (!isset($authCodePayload->code_challenge) && $codeVerifier !== null) { throw OAuthServerException::invalidRequest( 'code_challenge', 'code_verifier received when no code_challenge is present' ); } - if (!empty($authCodePayload->code_challenge)) { + if (isset($authCodePayload->code_challenge)) { $this->validateCodeChallenge($authCodePayload, $codeVerifier); } @@ -162,7 +161,7 @@ public function respondToAccessTokenRequest( return $responseType; } - private function validateCodeChallenge($authCodePayload, $codeVerifier) + private function validateCodeChallenge(object $authCodePayload, ?string $codeVerifier): void { if ($codeVerifier === null) { throw OAuthServerException::invalidRequest('code_verifier'); @@ -170,23 +169,23 @@ private function validateCodeChallenge($authCodePayload, $codeVerifier) // Validate code_verifier according to RFC-7636 // @see: https://tools.ietf.org/html/rfc7636#section-4.1 - if (\preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) { + if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) { throw OAuthServerException::invalidRequest( 'code_verifier', 'Code Verifier must follow the specifications of RFC-7636.' ); } - if (\property_exists($authCodePayload, 'code_challenge_method')) { + if (property_exists($authCodePayload, 'code_challenge_method')) { if (isset($this->codeChallengeVerifiers[$authCodePayload->code_challenge_method])) { $codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method]; - if ($codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false) { + if (!isset($authCodePayload->code_challenge) || $codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false) { throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.'); } } else { throw OAuthServerException::serverError( - \sprintf( + sprintf( 'Unsupported code challenge method `%s`', $authCodePayload->code_challenge_method ) @@ -197,21 +196,17 @@ private function validateCodeChallenge($authCodePayload, $codeVerifier) /** * Validate the authorization code. - * - * @param stdClass $authCodePayload - * @param ClientEntityInterface $client - * @param ServerRequestInterface $request */ private function validateAuthorizationCode( - $authCodePayload, + stdClass $authCodePayload, ClientEntityInterface $client, ServerRequestInterface $request - ) { - if (!\property_exists($authCodePayload, 'auth_code_id')) { + ): void { + if (!property_exists($authCodePayload, 'auth_code_id')) { throw OAuthServerException::invalidRequest('code', 'Authorization code malformed'); } - if (\time() > $authCodePayload->expire_time) { + if (time() > $authCodePayload->expire_time) { throw OAuthServerException::invalidRequest('code', 'Authorization code has expired'); } @@ -225,7 +220,7 @@ private function validateAuthorizationCode( // The redirect URI is required in this request $redirectUri = $this->getRequestParameter('redirect_uri', $request, null); - if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) { + if ($authCodePayload->redirect_uri !== '' && $redirectUri === null) { throw OAuthServerException::invalidRequest('redirect_uri'); } @@ -236,10 +231,8 @@ private function validateAuthorizationCode( /** * Return the grant identifier that can be used in matching up requests. - * - * @return string */ - public function getIdentifier() + public function getIdentifier(): string { return 'authorization_code'; } @@ -247,10 +240,10 @@ public function getIdentifier() /** * {@inheritdoc} */ - public function canRespondToAuthorizationRequest(ServerRequestInterface $request) + public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool { return ( - \array_key_exists('response_type', $request->getQueryParams()) + array_key_exists('response_type', $request->getQueryParams()) && $request->getQueryParams()['response_type'] === 'code' && isset($request->getQueryParams()['client_id']) ); @@ -259,7 +252,7 @@ public function canRespondToAuthorizationRequest(ServerRequestInterface $request /** * {@inheritdoc} */ - public function validateAuthorizationRequest(ServerRequestInterface $request) + public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface { $clientId = $this->getQueryStringParameter( 'client_id', @@ -276,19 +269,17 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); if ($redirectUri !== null) { - if (!\is_string($redirectUri)) { - throw OAuthServerException::invalidRequest('redirect_uri'); - } - $this->validateRedirectUri($redirectUri, $client, $request); - } elseif (empty($client->getRedirectUri()) || - (\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1)) { + } elseif ( + $client->getRedirectUri() === '' || + (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1) + ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient($request); } - $defaultClientRedirectUri = \is_array($client->getRedirectUri()) + $defaultClientRedirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri(); @@ -315,21 +306,28 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) if ($codeChallenge !== null) { $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); - if (\array_key_exists($codeChallengeMethod, $this->codeChallengeVerifiers) === false) { + if ($codeChallengeMethod === null) { + throw OAuthServerException::invalidRequest( + 'code_challenge_method', + 'Code challenge method must be provided when `code_challenge` is set.' + ); + } + + if (array_key_exists($codeChallengeMethod, $this->codeChallengeVerifiers) === false) { throw OAuthServerException::invalidRequest( 'code_challenge_method', - 'Code challenge method must be one of ' . \implode(', ', \array_map( + 'Code challenge method must be one of ' . implode(', ', array_map( function ($method) { return '`' . $method . '`'; }, - \array_keys($this->codeChallengeVerifiers) + array_keys($this->codeChallengeVerifiers) )) ); } // Validate code_challenge according to RFC-7636 // @see: https://tools.ietf.org/html/rfc7636#section-4.2 - if (\preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) { + if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) { throw OAuthServerException::invalidRequest( 'code_challenge', 'Code challenge must follow the specifications of RFC-7636.' @@ -348,7 +346,7 @@ function ($method) { /** * {@inheritdoc} */ - public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest) + public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): ResponseTypeInterface { if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) { throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest'); @@ -378,7 +376,7 @@ public function completeAuthorizationRequest(AuthorizationRequestInterface $auth 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), ]; - $jsonPayload = \json_encode($payload); + $jsonPayload = json_encode($payload); if ($jsonPayload === false) { throw new LogicException('An error was encountered when JSON encoding the authorization request response'); @@ -412,14 +410,10 @@ public function completeAuthorizationRequest(AuthorizationRequestInterface $auth /** * Get the client redirect URI if not set in the request. - * - * @param AuthorizationRequestInterface $authorizationRequest - * - * @return string */ - private function getClientRedirectUri(AuthorizationRequestInterface $authorizationRequest) + private function getClientRedirectUri(AuthorizationRequestInterface $authorizationRequest): string { - return \is_array($authorizationRequest->getClient()->getRedirectUri()) + return is_array($authorizationRequest->getClient()->getRedirectUri()) ? $authorizationRequest->getClient()->getRedirectUri()[0] : $authorizationRequest->getClient()->getRedirectUri(); } diff --git a/src/Grant/ClientCredentialsGrant.php b/src/Grant/ClientCredentialsGrant.php index d342b269f..bee6abaa1 100644 --- a/src/Grant/ClientCredentialsGrant.php +++ b/src/Grant/ClientCredentialsGrant.php @@ -1,4 +1,5 @@ getClientCredentials($request); $client = $this->getClientEntityOrFail($clientId, $request); @@ -64,7 +67,7 @@ public function respondToAccessTokenRequest( /** * {@inheritdoc} */ - public function getIdentifier() + public function getIdentifier(): string { return 'client_credentials'; } diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index bf1dd792f..a2fda3734 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Grant; use DateInterval; @@ -20,44 +23,29 @@ use LogicException; use Psr\Http\Message\ServerRequestInterface; +use function count; +use function is_array; +use function is_null; +use function time; + class ImplicitGrant extends AbstractAuthorizeGrant { - /** - * @var DateInterval - */ - private $accessTokenTTL; - - /** - * @var string - */ - private $queryDelimiter; - - /** - * @param DateInterval $accessTokenTTL - * @param string $queryDelimiter - */ - public function __construct(DateInterval $accessTokenTTL, $queryDelimiter = '#') + public function __construct(private DateInterval $accessTokenTTL, private string $queryDelimiter = '#') { - $this->accessTokenTTL = $accessTokenTTL; - $this->queryDelimiter = $queryDelimiter; } /** - * @param DateInterval $refreshTokenTTL - * - * @throw LogicException + * @throws LogicException */ - public function setRefreshTokenTTL(DateInterval $refreshTokenTTL) + public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void { throw new LogicException('The Implicit Grant does not return refresh tokens'); } /** - * @param RefreshTokenRepositoryInterface $refreshTokenRepository - * - * @throw LogicException + * @throws LogicException */ - public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository) + public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository): void { throw new LogicException('The Implicit Grant does not return refresh tokens'); } @@ -65,42 +53,34 @@ public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refre /** * {@inheritdoc} */ - public function canRespondToAccessTokenRequest(ServerRequestInterface $request) + public function canRespondToAccessTokenRequest(ServerRequestInterface $request): bool { return false; } /** * Return the grant identifier that can be used in matching up requests. - * - * @return string */ - public function getIdentifier() + public function getIdentifier(): string { return 'implicit'; } /** * Respond to an incoming request. - * - * @param ServerRequestInterface $request - * @param ResponseTypeInterface $responseType - * @param DateInterval $accessTokenTTL - * - * @return ResponseTypeInterface */ public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, DateInterval $accessTokenTTL - ) { + ): ResponseTypeInterface { throw new LogicException('This grant does not used this method'); } /** * {@inheritdoc} */ - public function canRespondToAuthorizationRequest(ServerRequestInterface $request) + public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool { return ( isset($request->getQueryParams()['response_type']) @@ -112,7 +92,7 @@ public function canRespondToAuthorizationRequest(ServerRequestInterface $request /** * {@inheritdoc} */ - public function validateAuthorizationRequest(ServerRequestInterface $request) + public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequestInterface { $clientId = $this->getQueryStringParameter( 'client_id', @@ -120,7 +100,7 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) $this->getServerParameter('PHP_AUTH_USER', $request) ); - if (\is_null($clientId)) { + if (is_null($clientId)) { throw OAuthServerException::invalidRequest('client_id'); } @@ -129,17 +109,15 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) $redirectUri = $this->getQueryStringParameter('redirect_uri', $request); if ($redirectUri !== null) { - if (!\is_string($redirectUri)) { - throw OAuthServerException::invalidRequest('redirect_uri'); - } - $this->validateRedirectUri($redirectUri, $client, $request); - } elseif (\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1 - || empty($client->getRedirectUri())) { + } elseif ( + is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1 + || $client->getRedirectUri() === '' + ) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); throw OAuthServerException::invalidClient($request); } else { - $redirectUri = \is_array($client->getRedirectUri()) + $redirectUri = is_array($client->getRedirectUri()) ? $client->getRedirectUri()[0] : $client->getRedirectUri(); } @@ -151,10 +129,6 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) $stateParameter = $this->getQueryStringParameter('state', $request); - if ($stateParameter !== null && !\is_string($stateParameter)) { - throw OAuthServerException::invalidRequest('state'); - } - $authorizationRequest = $this->createAuthorizationRequest(); $authorizationRequest->setGrantTypeId($this->getIdentifier()); $authorizationRequest->setClient($client); @@ -172,17 +146,17 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) /** * {@inheritdoc} */ - public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest) + public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): ResponseTypeInterface { if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) { throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest'); } - $finalRedirectUri = ($authorizationRequest->getRedirectUri() === null) - ? \is_array($authorizationRequest->getClient()->getRedirectUri()) + $clientRegisteredRedirectUri = is_array($authorizationRequest->getClient()->getRedirectUri()) ? $authorizationRequest->getClient()->getRedirectUri()[0] - : $authorizationRequest->getClient()->getRedirectUri() - : $authorizationRequest->getRedirectUri(); + : $authorizationRequest->getClient()->getRedirectUri(); + + $finalRedirectUri = $authorizationRequest->getRedirectUri() ?? $clientRegisteredRedirectUri; // The user approved the client, redirect them back with an access token if ($authorizationRequest->isAuthorizationApproved() === true) { @@ -208,7 +182,7 @@ public function completeAuthorizationRequest(AuthorizationRequestInterface $auth [ 'access_token' => (string) $accessToken, 'token_type' => 'Bearer', - 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(), + 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - time(), 'state' => $authorizationRequest->getState(), ], $this->queryDelimiter diff --git a/src/Grant/PasswordGrant.php b/src/Grant/PasswordGrant.php index 80f442616..1b92dc1da 100644 --- a/src/Grant/PasswordGrant.php +++ b/src/Grant/PasswordGrant.php @@ -1,4 +1,5 @@ validateClient($request); $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); @@ -59,7 +60,8 @@ public function respondToAccessTokenRequest( $scopes, $this->getIdentifier(), $client, - $user->getIdentifier()); + $user->getIdentifier() + ); // Issue and persist new access token $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes); @@ -78,24 +80,19 @@ public function respondToAccessTokenRequest( } /** - * @param ServerRequestInterface $request - * @param ClientEntityInterface $client - * * @throws OAuthServerException - * - * @return UserEntityInterface */ - protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client) + protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client): UserEntityInterface { $username = $this->getRequestParameter('username', $request); - if (!\is_string($username)) { + if (!is_string($username)) { throw OAuthServerException::invalidRequest('username'); } $password = $this->getRequestParameter('password', $request); - if (!\is_string($password)) { + if (!is_string($password)) { throw OAuthServerException::invalidRequest('password'); } @@ -118,7 +115,7 @@ protected function validateUser(ServerRequestInterface $request, ClientEntityInt /** * {@inheritdoc} */ - public function getIdentifier() + public function getIdentifier(): string { return 'password'; } diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index e8248b242..5a5b55e30 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -1,4 +1,5 @@ setRefreshTokenRepository($refreshTokenRepository); @@ -43,7 +49,7 @@ public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, DateInterval $accessTokenTTL - ) { + ): ResponseTypeInterface { // Validate request $client = $this->validateClient($request); $oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier()); @@ -51,14 +57,14 @@ public function respondToAccessTokenRequest( $this->getRequestParameter( 'scope', $request, - \implode(self::SCOPE_DELIMITER_STRING, $oldRefreshToken['scopes']) + implode(self::SCOPE_DELIMITER_STRING, $oldRefreshToken['scopes']) ) ); // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure // the request doesn't include any new scopes foreach ($scopes as $scope) { - if (\in_array($scope->getIdentifier(), $oldRefreshToken['scopes'], true) === false) { + if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes'], true) === false) { throw OAuthServerException::invalidScope($scope->getIdentifier()); } } @@ -90,17 +96,14 @@ public function respondToAccessTokenRequest( } /** - * @param ServerRequestInterface $request - * @param string $clientId - * * @throws OAuthServerException * - * @return array + * @return array */ - protected function validateOldRefreshToken(ServerRequestInterface $request, $clientId) + protected function validateOldRefreshToken(ServerRequestInterface $request, string $clientId): array { $encryptedRefreshToken = $this->getRequestParameter('refresh_token', $request); - if (!\is_string($encryptedRefreshToken)) { + if (!is_string($encryptedRefreshToken)) { throw OAuthServerException::invalidRequest('refresh_token'); } @@ -111,13 +114,13 @@ protected function validateOldRefreshToken(ServerRequestInterface $request, $cli throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token', $e); } - $refreshTokenData = \json_decode($refreshToken, true); + $refreshTokenData = json_decode($refreshToken, true); if ($refreshTokenData['client_id'] !== $clientId) { $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request)); throw OAuthServerException::invalidRefreshToken('Token is not linked to client'); } - if ($refreshTokenData['expire_time'] < \time()) { + if ($refreshTokenData['expire_time'] < time()) { throw OAuthServerException::invalidRefreshToken('Token has expired'); } @@ -131,7 +134,7 @@ protected function validateOldRefreshToken(ServerRequestInterface $request, $cli /** * {@inheritdoc} */ - public function getIdentifier() + public function getIdentifier(): string { return 'refresh_token'; } diff --git a/src/Middleware/AuthorizationServerMiddleware.php b/src/Middleware/AuthorizationServerMiddleware.php index 9b78b4585..e59d35590 100644 --- a/src/Middleware/AuthorizationServerMiddleware.php +++ b/src/Middleware/AuthorizationServerMiddleware.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,9 +8,10 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Middleware; -use Exception; use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\Exception\OAuthServerException; use Psr\Http\Message\ResponseInterface; @@ -17,37 +19,16 @@ class AuthorizationServerMiddleware { - /** - * @var AuthorizationServer - */ - private $server; - - /** - * @param AuthorizationServer $server - */ - public function __construct(AuthorizationServer $server) + public function __construct(private AuthorizationServer $server) { - $this->server = $server; } - /** - * @param ServerRequestInterface $request - * @param ResponseInterface $response - * @param callable $next - * - * @return ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface { try { $response = $this->server->respondToAccessTokenRequest($request, $response); } catch (OAuthServerException $exception) { return $exception->generateHttpResponse($response); - // @codeCoverageIgnoreStart - } catch (Exception $exception) { - return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500)) - ->generateHttpResponse($response); - // @codeCoverageIgnoreEnd } // Pass the request and response on to the next responder in the chain diff --git a/src/Middleware/ResourceServerMiddleware.php b/src/Middleware/ResourceServerMiddleware.php index e152a9999..460e77712 100644 --- a/src/Middleware/ResourceServerMiddleware.php +++ b/src/Middleware/ResourceServerMiddleware.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,9 +8,10 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Middleware; -use Exception; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\ResourceServer; use Psr\Http\Message\ResponseInterface; @@ -17,37 +19,16 @@ class ResourceServerMiddleware { - /** - * @var ResourceServer - */ - private $server; - - /** - * @param ResourceServer $server - */ - public function __construct(ResourceServer $server) + public function __construct(private ResourceServer $server) { - $this->server = $server; } - /** - * @param ServerRequestInterface $request - * @param ResponseInterface $response - * @param callable $next - * - * @return ResponseInterface - */ - public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) + public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface { try { $request = $this->server->validateAuthenticatedRequest($request); } catch (OAuthServerException $exception) { return $exception->generateHttpResponse($response); - // @codeCoverageIgnoreStart - } catch (Exception $exception) { - return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500)) - ->generateHttpResponse($response); - // @codeCoverageIgnoreEnd } // Pass the request and response on to the next responder in the chain diff --git a/src/RedirectUriValidators/RedirectUriValidator.php b/src/RedirectUriValidators/RedirectUriValidator.php index c758ad954..cbc530adf 100644 --- a/src/RedirectUriValidators/RedirectUriValidator.php +++ b/src/RedirectUriValidators/RedirectUriValidator.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,42 +8,43 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\RedirectUriValidators; use League\Uri\Exceptions\SyntaxError; use League\Uri\Uri; +use function in_array; +use function is_string; + class RedirectUriValidator implements RedirectUriValidatorInterface { /** - * @var array + * @var string[] */ - private $allowedRedirectUris; + private array $allowedRedirectUris; /** * New validator instance for the given uri * - * @param string|array $allowedRedirectUris + * @param string[]|string $allowedRedirectUris */ - public function __construct($allowedRedirectUri) + public function __construct(array|string $allowedRedirectUris) { - if (\is_string($allowedRedirectUri)) { - $this->allowedRedirectUris = [$allowedRedirectUri]; - } elseif (\is_array($allowedRedirectUri)) { - $this->allowedRedirectUris = $allowedRedirectUri; + if (is_string($allowedRedirectUris)) { + $this->allowedRedirectUris = [$allowedRedirectUris]; } else { - $this->allowedRedirectUris = []; + $this->allowedRedirectUris = $allowedRedirectUris; } } /** * Validates the redirect uri. * - * @param string $redirectUri - * * @return bool Return true if valid, false otherwise */ - public function validateRedirectUri($redirectUri) + public function validateRedirectUri(string $redirectUri): bool { if ($this->isLoopbackUri($redirectUri)) { return $this->matchUriExcludingPort($redirectUri); @@ -55,12 +57,8 @@ public function validateRedirectUri($redirectUri) * According to section 7.3 of rfc8252, loopback uris are: * - "http://127.0.0.1:{port}/{path}" for IPv4 * - "http://[::1]:{port}/{path}" for IPv6 - * - * @param string $redirectUri - * - * @return bool */ - private function isLoopbackUri($redirectUri) + private function isLoopbackUri(string $redirectUri): bool { try { $uri = Uri::createFromString($redirectUri); @@ -69,29 +67,21 @@ private function isLoopbackUri($redirectUri) } return $uri->getScheme() === 'http' - && (\in_array($uri->getHost(), ['127.0.0.1', '[::1]'], true)); + && (in_array($uri->getHost(), ['127.0.0.1', '[::1]'], true)); } /** * Find an exact match among allowed uris - * - * @param string $redirectUri - * - * @return bool Return true if an exact match is found, false otherwise */ - private function matchExactUri($redirectUri) + private function matchExactUri(string $redirectUri): bool { - return \in_array($redirectUri, $this->allowedRedirectUris, true); + return in_array($redirectUri, $this->allowedRedirectUris, true); } /** * Find a match among allowed uris, allowing for different port numbers - * - * @param string $redirectUri - * - * @return bool Return true if a match is found, false otherwise */ - private function matchUriExcludingPort($redirectUri) + private function matchUriExcludingPort(string $redirectUri): bool { $parsedUrl = $this->parseUrlAndRemovePort($redirectUri); @@ -106,12 +96,8 @@ private function matchUriExcludingPort($redirectUri) /** * Parse an url like \parse_url, excluding the port - * - * @param string $url - * - * @return array */ - private function parseUrlAndRemovePort($url) + private function parseUrlAndRemovePort(string $url): string { $uri = Uri::createFromString($url); diff --git a/src/RedirectUriValidators/RedirectUriValidatorInterface.php b/src/RedirectUriValidators/RedirectUriValidatorInterface.php index d039085ab..df64a219c 100644 --- a/src/RedirectUriValidators/RedirectUriValidatorInterface.php +++ b/src/RedirectUriValidators/RedirectUriValidatorInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,16 +8,14 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\RedirectUriValidators; interface RedirectUriValidatorInterface { /** * Validates the redirect uri. - * - * @param string $redirectUri - * - * @return bool Return true if valid, false otherwise */ - public function validateRedirectUri($redirectUri); + public function validateRedirectUri(string $redirectUri): bool; } diff --git a/src/Repositories/AccessTokenRepositoryInterface.php b/src/Repositories/AccessTokenRepositoryInterface.php index 72ddf1f4c..d392716ed 100644 --- a/src/Repositories/AccessTokenRepositoryInterface.php +++ b/src/Repositories/AccessTokenRepositoryInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; @@ -22,36 +25,20 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface /** * Create a new access token * - * @param ClientEntityInterface $clientEntity * @param ScopeEntityInterface[] $scopes - * @param mixed $userIdentifier - * - * @return AccessTokenEntityInterface */ - public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null); + public function getNewToken( + ClientEntityInterface $clientEntity, + array $scopes, + mixed $userIdentifier = null + ): AccessTokenEntityInterface; /** - * Persists a new access token to permanent storage. - * - * @param AccessTokenEntityInterface $accessTokenEntity - * * @throws UniqueTokenIdentifierConstraintViolationException */ - public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity); + public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity): void; - /** - * Revoke an access token. - * - * @param string $tokenId - */ - public function revokeAccessToken($tokenId); + public function revokeAccessToken(string $tokenId): void; - /** - * Check if the access token has been revoked. - * - * @param string $tokenId - * - * @return bool Return true if this token has been revoked - */ - public function isAccessTokenRevoked($tokenId); + public function isAccessTokenRevoked(string $tokenId): bool; } diff --git a/src/Repositories/AuthCodeRepositoryInterface.php b/src/Repositories/AuthCodeRepositoryInterface.php index 2dc285b83..89ff86b87 100644 --- a/src/Repositories/AuthCodeRepositoryInterface.php +++ b/src/Repositories/AuthCodeRepositoryInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; @@ -17,35 +20,14 @@ */ interface AuthCodeRepositoryInterface extends RepositoryInterface { - /** - * Creates a new AuthCode - * - * @return AuthCodeEntityInterface - */ - public function getNewAuthCode(); + public function getNewAuthCode(): AuthCodeEntityInterface; /** - * Persists a new auth code to permanent storage. - * - * @param AuthCodeEntityInterface $authCodeEntity - * * @throws UniqueTokenIdentifierConstraintViolationException */ - public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity); + public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity): void; - /** - * Revoke an auth code. - * - * @param string $codeId - */ - public function revokeAuthCode($codeId); + public function revokeAuthCode(string $codeId): void; - /** - * Check if the auth code has been revoked. - * - * @param string $codeId - * - * @return bool Return true if this code has been revoked - */ - public function isAuthCodeRevoked($codeId); + public function isAuthCodeRevoked(string $codeId): bool; } diff --git a/src/Repositories/ClientRepositoryInterface.php b/src/Repositories/ClientRepositoryInterface.php index 7eef494f4..63134ca9d 100644 --- a/src/Repositories/ClientRepositoryInterface.php +++ b/src/Repositories/ClientRepositoryInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\ClientEntityInterface; @@ -18,21 +21,11 @@ interface ClientRepositoryInterface extends RepositoryInterface { /** * Get a client. - * - * @param string $clientIdentifier The client's identifier - * - * @return ClientEntityInterface|null */ - public function getClientEntity($clientIdentifier); + public function getClientEntity(string $clientIdentifier): ?ClientEntityInterface; /** * Validate a client's secret. - * - * @param string $clientIdentifier The client's identifier - * @param null|string $clientSecret The client's secret (if sent) - * @param null|string $grantType The type of grant the client is using (if sent) - * - * @return bool */ - public function validateClient($clientIdentifier, $clientSecret, $grantType); + public function validateClient(string $clientIdentifier, ?string $clientSecret, ?string $grantType): bool; } diff --git a/src/Repositories/RefreshTokenRepositoryInterface.php b/src/Repositories/RefreshTokenRepositoryInterface.php index a769cf6d3..a25e50133 100644 --- a/src/Repositories/RefreshTokenRepositoryInterface.php +++ b/src/Repositories/RefreshTokenRepositoryInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -17,35 +20,14 @@ */ interface RefreshTokenRepositoryInterface extends RepositoryInterface { - /** - * Creates a new refresh token - * - * @return RefreshTokenEntityInterface|null - */ - public function getNewRefreshToken(); + public function getNewRefreshToken(): ?RefreshTokenEntityInterface; /** - * Create a new refresh token_name. - * - * @param RefreshTokenEntityInterface $refreshTokenEntity - * * @throws UniqueTokenIdentifierConstraintViolationException */ - public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity); + public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity): void; - /** - * Revoke the refresh token. - * - * @param string $tokenId - */ - public function revokeRefreshToken($tokenId); + public function revokeRefreshToken(string $tokenId): void; - /** - * Check if the refresh token has been revoked. - * - * @param string $tokenId - * - * @return bool Return true if this token has been revoked - */ - public function isRefreshTokenRevoked($tokenId); + public function isRefreshTokenRevoked(string $tokenId): bool; } diff --git a/src/Repositories/RepositoryInterface.php b/src/Repositories/RepositoryInterface.php index 9c27b4b0a..00b1dc106 100644 --- a/src/Repositories/RepositoryInterface.php +++ b/src/Repositories/RepositoryInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Repositories; /** diff --git a/src/Repositories/ScopeRepositoryInterface.php b/src/Repositories/ScopeRepositoryInterface.php index 9dbc0a896..95bfdbb9a 100644 --- a/src/Repositories/ScopeRepositoryInterface.php +++ b/src/Repositories/ScopeRepositoryInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\ClientEntityInterface; @@ -21,28 +24,22 @@ interface ScopeRepositoryInterface extends RepositoryInterface * Return information about a scope. * * @param string $identifier The scope identifier - * - * @return ScopeEntityInterface|null */ - public function getScopeEntityByIdentifier($identifier); + public function getScopeEntityByIdentifier(string $identifier): ?ScopeEntityInterface; /** * Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally * append additional scopes or remove requested scopes. * * @param ScopeEntityInterface[] $scopes - * @param string $grantType - * @param ClientEntityInterface $clientEntity - * @param null|string $userIdentifier - * @param null|string $authCodeId * * @return ScopeEntityInterface[] */ public function finalizeScopes( array $scopes, - $grantType, + string $grantType, ClientEntityInterface $clientEntity, - $userIdentifier = null, - $authCodeId = null - ); + string|int|null $userIdentifier = null, + ?string $authCodeId = null + ): array; } diff --git a/src/Repositories/UserRepositoryInterface.php b/src/Repositories/UserRepositoryInterface.php index 8ad49aa7c..b8cf53d09 100644 --- a/src/Repositories/UserRepositoryInterface.php +++ b/src/Repositories/UserRepositoryInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\ClientEntityInterface; @@ -16,18 +19,11 @@ interface UserRepositoryInterface extends RepositoryInterface { /** * Get a user entity. - * - * @param string $username - * @param string $password - * @param string $grantType The grant type used - * @param ClientEntityInterface $clientEntity - * - * @return UserEntityInterface|null */ public function getUserEntityByUserCredentials( - $username, - $password, - $grantType, + string $username, + string $password, + string $grantType, ClientEntityInterface $clientEntity - ); + ): ?UserEntityInterface; } diff --git a/src/RequestAccessTokenEvent.php b/src/RequestAccessTokenEvent.php index c2f478284..9bb0c3c2f 100644 --- a/src/RequestAccessTokenEvent.php +++ b/src/RequestAccessTokenEvent.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; @@ -14,27 +17,16 @@ class RequestAccessTokenEvent extends RequestEvent { - /** - * @var AccessTokenEntityInterface - */ - private $accessToken; - - /** - * @param string $name - * @param ServerRequestInterface $request - */ - public function __construct($name, ServerRequestInterface $request, AccessTokenEntityInterface $accessToken) + public function __construct(string $name, ServerRequestInterface $request, private AccessTokenEntityInterface $accessToken) { parent::__construct($name, $request); - $this->accessToken = $accessToken; } /** - * @return AccessTokenEntityInterface * * @codeCoverageIgnore */ - public function getAccessToken() + public function getAccessToken(): AccessTokenEntityInterface { return $this->accessToken; } diff --git a/src/RequestEvent.php b/src/RequestEvent.php index 4f7dad097..b0ae00767 100644 --- a/src/RequestEvent.php +++ b/src/RequestEvent.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server; use League\Event\Event; @@ -14,36 +17,23 @@ class RequestEvent extends Event { - const CLIENT_AUTHENTICATION_FAILED = 'client.authentication.failed'; - const USER_AUTHENTICATION_FAILED = 'user.authentication.failed'; - const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed'; + public const CLIENT_AUTHENTICATION_FAILED = 'client.authentication.failed'; + public const USER_AUTHENTICATION_FAILED = 'user.authentication.failed'; + public const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed'; - const REFRESH_TOKEN_ISSUED = 'refresh_token.issued'; - const ACCESS_TOKEN_ISSUED = 'access_token.issued'; - - /** - * @var ServerRequestInterface - */ - private $request; + public const REFRESH_TOKEN_ISSUED = 'refresh_token.issued'; + public const ACCESS_TOKEN_ISSUED = 'access_token.issued'; - /** - * RequestEvent constructor. - * - * @param string $name - * @param ServerRequestInterface $request - */ - public function __construct($name, ServerRequestInterface $request) + public function __construct(string $name, private ServerRequestInterface $request) { parent::__construct($name); - $this->request = $request; } /** - * @return ServerRequestInterface * * @codeCoverageIgnore */ - public function getRequest() + public function getRequest(): ServerRequestInterface { return $this->request; } diff --git a/src/RequestRefreshTokenEvent.php b/src/RequestRefreshTokenEvent.php index 326a115ed..a6d17971c 100644 --- a/src/RequestRefreshTokenEvent.php +++ b/src/RequestRefreshTokenEvent.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -14,27 +17,16 @@ class RequestRefreshTokenEvent extends RequestEvent { - /** - * @var RefreshTokenEntityInterface - */ - private $refreshToken; - - /** - * @param string $name - * @param ServerRequestInterface $request - */ - public function __construct($name, ServerRequestInterface $request, RefreshTokenEntityInterface $refreshToken) + public function __construct(string $name, ServerRequestInterface $request, private RefreshTokenEntityInterface $refreshToken) { parent::__construct($name, $request); - $this->refreshToken = $refreshToken; } /** - * @return RefreshTokenEntityInterface * * @codeCoverageIgnore */ - public function getRefreshToken() + public function getRefreshToken(): RefreshTokenEntityInterface { return $this->refreshToken; } diff --git a/src/RequestTypes/AuthorizationRequest.php b/src/RequestTypes/AuthorizationRequest.php index d05e5b394..276396722 100644 --- a/src/RequestTypes/AuthorizationRequest.php +++ b/src/RequestTypes/AuthorizationRequest.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\RequestTypes; use League\OAuth2\Server\Entities\ClientEntityInterface; @@ -17,111 +20,76 @@ class AuthorizationRequest implements AuthorizationRequestInterface { /** * The grant type identifier - * - * @var string */ - protected $grantTypeId; + protected string $grantTypeId; /** * The client identifier - * - * @var ClientEntityInterface */ - protected $client; + protected ClientEntityInterface $client; /** * The user identifier - * - * @var UserEntityInterface */ - protected $user; + protected UserEntityInterface $user; /** * An array of scope identifiers - * * @var ScopeEntityInterface[] */ - protected $scopes = []; + protected array $scopes = []; /** * Has the user authorized the authorization request - * - * @var bool */ - protected $authorizationApproved = false; + protected bool $authorizationApproved = false; /** * The redirect URI used in the request - * - * @var string|null */ - protected $redirectUri; + protected ?string $redirectUri = null; /** * The state parameter on the authorization request - * - * @var string|null */ - protected $state; + protected ?string $state = null; /** * The code challenge (if provided) - * - * @var string */ - protected $codeChallenge; + protected string $codeChallenge; /** * The code challenge method (if provided) - * - * @var string */ - protected $codeChallengeMethod; + protected string $codeChallengeMethod; - /** - * @return string - */ - public function getGrantTypeId() + public function getGrantTypeId(): string { return $this->grantTypeId; } - /** - * @param string $grantTypeId - */ - public function setGrantTypeId($grantTypeId) + public function setGrantTypeId(string $grantTypeId): void { $this->grantTypeId = $grantTypeId; } - /** - * @return ClientEntityInterface - */ - public function getClient() + public function getClient(): ClientEntityInterface { return $this->client; } - /** - * @param ClientEntityInterface $client - */ - public function setClient(ClientEntityInterface $client) + public function setClient(ClientEntityInterface $client): void { $this->client = $client; } - /** - * @return UserEntityInterface|null - */ - public function getUser() + public function getUser(): ?UserEntityInterface { - return $this->user; + return $this->user ?? null; } - /** - * @param UserEntityInterface $user - */ - public function setUser(UserEntityInterface $user) + public function setUser(UserEntityInterface $user): void { $this->user = $user; } @@ -129,7 +97,7 @@ public function setUser(UserEntityInterface $user) /** * @return ScopeEntityInterface[] */ - public function getScopes() + public function getScopes(): array { return $this->scopes; } @@ -137,87 +105,57 @@ public function getScopes() /** * @param ScopeEntityInterface[] $scopes */ - public function setScopes(array $scopes) + public function setScopes(array $scopes): void { $this->scopes = $scopes; } - /** - * @return bool - */ - public function isAuthorizationApproved() + public function isAuthorizationApproved(): bool { return $this->authorizationApproved; } - /** - * @param bool $authorizationApproved - */ - public function setAuthorizationApproved($authorizationApproved) + public function setAuthorizationApproved(bool $authorizationApproved): void { $this->authorizationApproved = $authorizationApproved; } - /** - * @return string|null - */ - public function getRedirectUri() + public function getRedirectUri(): ?string { return $this->redirectUri; } - /** - * @param string|null $redirectUri - */ - public function setRedirectUri($redirectUri) + public function setRedirectUri(?string $redirectUri): void { $this->redirectUri = $redirectUri; } - /** - * @return string|null - */ - public function getState() + public function getState(): ?string { return $this->state; } - /** - * @param string $state - */ - public function setState($state) + public function setState(string $state): void { $this->state = $state; } - /** - * @return string - */ - public function getCodeChallenge() + public function getCodeChallenge(): ?string { - return $this->codeChallenge; + return $this->codeChallenge ?? null; } - /** - * @param string $codeChallenge - */ - public function setCodeChallenge($codeChallenge) + public function setCodeChallenge(string $codeChallenge): void { $this->codeChallenge = $codeChallenge; } - /** - * @return string - */ - public function getCodeChallengeMethod() + public function getCodeChallengeMethod(): ?string { - return $this->codeChallengeMethod; + return $this->codeChallengeMethod ?? null; } - /** - * @param string $codeChallengeMethod - */ - public function setCodeChallengeMethod($codeChallengeMethod) + public function setCodeChallengeMethod(string $codeChallengeMethod): void { $this->codeChallengeMethod = $codeChallengeMethod; } diff --git a/src/RequestTypes/AuthorizationRequestInterface.php b/src/RequestTypes/AuthorizationRequestInterface.php index 869dc1ea2..6ae358439 100644 --- a/src/RequestTypes/AuthorizationRequestInterface.php +++ b/src/RequestTypes/AuthorizationRequestInterface.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server\RequestTypes; use League\OAuth2\Server\Entities\ClientEntityInterface; @@ -15,93 +18,45 @@ interface AuthorizationRequestInterface { - /** - * @return UserEntityInterface|null - */ - public function getUser(); + public function getUser(): UserEntityInterface|null; - /** - * @param string $state - */ - public function setState($state); + public function setState(string $state): void; - /** - * @return ClientEntityInterface - */ - public function getClient(); + public function getClient(): ClientEntityInterface; - /** - * @param bool $authorizationApproved - */ - public function setAuthorizationApproved($authorizationApproved); + public function setAuthorizationApproved(bool $authorizationApproved): void; /** * @param ScopeEntityInterface[] $scopes */ - public function setScopes(array $scopes); + public function setScopes(array $scopes): void; - /** - * @param string|null $redirectUri - */ - public function setRedirectUri($redirectUri); + public function setRedirectUri(?string $redirectUri): void; - /** - * @return string|null - */ - public function getRedirectUri(); + public function getRedirectUri(): ?string; - /** - * @return string - */ - public function getCodeChallengeMethod(); + public function getCodeChallengeMethod(): ?string; - /** - * @param string $grantTypeId - */ - public function setGrantTypeId($grantTypeId); + public function setGrantTypeId(string $grantTypeId): void; - /** - * @param UserEntityInterface $user - */ - public function setUser(UserEntityInterface $user); + public function setUser(UserEntityInterface $user): void; - /** - * @param ClientEntityInterface $client - */ - public function setClient(ClientEntityInterface $client); + public function setClient(ClientEntityInterface $client): void; - /** - * @param string $codeChallenge - */ - public function setCodeChallenge($codeChallenge); + public function setCodeChallenge(string $codeChallenge): void; - /** - * @return bool - */ - public function isAuthorizationApproved(); + public function isAuthorizationApproved(): bool; - /** - * @return string|null - */ - public function getState(); + public function getState(): ?string; - /** - * @return string - */ - public function getCodeChallenge(); + public function getCodeChallenge(): ?string; - /** - * @param string $codeChallengeMethod - */ - public function setCodeChallengeMethod($codeChallengeMethod); + public function setCodeChallengeMethod(string $codeChallengeMethod): void; /** * @return ScopeEntityInterface[] */ - public function getScopes(); + public function getScopes(): array; - /** - * @return string - */ - public function getGrantTypeId(); + public function getGrantTypeId(): string; } diff --git a/src/ResourceServer.php b/src/ResourceServer.php index 92a72763e..0ffbf889a 100644 --- a/src/ResourceServer.php +++ b/src/ResourceServer.php @@ -1,4 +1,5 @@ * @copyright Copyright (c) Alex Bilbie @@ -7,6 +8,8 @@ * @link https://github.com/thephpleague/oauth2-server */ +declare(strict_types=1); + namespace League\OAuth2\Server; use League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface; @@ -17,34 +20,15 @@ class ResourceServer { - /** - * @var AccessTokenRepositoryInterface - */ - private $accessTokenRepository; + private CryptKeyInterface $publicKey; - /** - * @var CryptKeyInterface - */ - private $publicKey; - - /** - * @var null|AuthorizationValidatorInterface - */ - private $authorizationValidator; + private ?AuthorizationValidatorInterface $authorizationValidator = null; - /** - * New server instance. - * - * @param AccessTokenRepositoryInterface $accessTokenRepository - * @param CryptKeyInterface|string $publicKey - * @param null|AuthorizationValidatorInterface $authorizationValidator - */ public function __construct( - AccessTokenRepositoryInterface $accessTokenRepository, - $publicKey, + private AccessTokenRepositoryInterface $accessTokenRepository, + CryptKeyInterface|string $publicKey, AuthorizationValidatorInterface $authorizationValidator = null ) { - $this->accessTokenRepository = $accessTokenRepository; if ($publicKey instanceof CryptKeyInterface === false) { $publicKey = new CryptKey($publicKey); @@ -54,10 +38,7 @@ public function __construct( $this->authorizationValidator = $authorizationValidator; } - /** - * @return AuthorizationValidatorInterface - */ - protected function getAuthorizationValidator() + protected function getAuthorizationValidator(): AuthorizationValidatorInterface { if ($this->authorizationValidator instanceof AuthorizationValidatorInterface === false) { $this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository); @@ -73,13 +54,9 @@ protected function getAuthorizationValidator() /** * Determine the access token validity. * - * @param ServerRequestInterface $request - * * @throws OAuthServerException - * - * @return ServerRequestInterface */ - public function validateAuthenticatedRequest(ServerRequestInterface $request) + public function validateAuthenticatedRequest(ServerRequestInterface $request): ServerRequestInterface { return $this->getAuthorizationValidator()->validateAuthorization($request); } diff --git a/src/ResponseTypes/AbstractResponseType.php b/src/ResponseTypes/AbstractResponseType.php index f5f201908..2af00e224 100644 --- a/src/ResponseTypes/AbstractResponseType.php +++ b/src/ResponseTypes/AbstractResponseType.php @@ -1,4 +1,5 @@ accessToken = $accessToken; } - /** - * {@inheritdoc} - */ - public function setRefreshToken(RefreshTokenEntityInterface $refreshToken) + public function setRefreshToken(RefreshTokenEntityInterface $refreshToken): void { $this->refreshToken = $refreshToken; } - /** - * Set the private key - * - * @param CryptKeyInterface $key - */ - public function setPrivateKey(CryptKeyInterface $key) + public function setPrivateKey(CryptKeyInterface $key): void { $this->privateKey = $key; } diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index 33c1606e8..f738a4e27 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -1,4 +1,5 @@ accessToken->getExpiryDateTime()->getTimestamp(); $responseParams = [ 'token_type' => 'Bearer', - 'expires_in' => $expireDateTime - \time(), + 'expires_in' => $expireDateTime - time(), 'access_token' => (string) $this->accessToken, ]; - if ($this->refreshToken instanceof RefreshTokenEntityInterface) { - $refreshTokenPayload = \json_encode([ + $refreshTokenPayload = json_encode([ '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(), - ]); - - if ($refreshTokenPayload === false) { - throw new LogicException('Error encountered JSON encoding the refresh token payload'); - } + ]); - $responseParams['refresh_token'] = $this->encrypt($refreshTokenPayload); + if ($refreshTokenPayload === false) { + throw new LogicException('Error encountered JSON encoding the refresh token payload'); } - $responseParams = \json_encode(\array_merge($this->getExtraParams($this->accessToken), $responseParams)); + $responseParams['refresh_token'] = $this->encrypt($refreshTokenPayload); + + $responseParams = json_encode(array_merge($this->getExtraParams($this->accessToken), $responseParams)); if ($responseParams === false) { throw new LogicException('Error encountered JSON encoding response parameters'); @@ -70,11 +71,9 @@ public function generateHttpResponse(ResponseInterface $response) * AuthorizationServer::getResponseType() to pull in your version of * this class rather than the default. * - * @param AccessTokenEntityInterface $accessToken - * - * @return array + * @return mixed[] */ - protected function getExtraParams(AccessTokenEntityInterface $accessToken) + protected function getExtraParams(AccessTokenEntityInterface $accessToken): array { return []; } diff --git a/src/ResponseTypes/RedirectResponse.php b/src/ResponseTypes/RedirectResponse.php index e4639148d..cff6af6eb 100644 --- a/src/ResponseTypes/RedirectResponse.php +++ b/src/ResponseTypes/RedirectResponse.php @@ -1,4 +1,5 @@ redirectUri = $redirectUri; } - /** - * @param ResponseInterface $response - * - * @return ResponseInterface - */ - public function generateHttpResponse(ResponseInterface $response) + public function generateHttpResponse(ResponseInterface $response): ResponseInterface { return $response->withStatus(302)->withHeader('Location', $this->redirectUri); } diff --git a/src/ResponseTypes/ResponseTypeInterface.php b/src/ResponseTypes/ResponseTypeInterface.php index 5eddd6079..8e70ae9f8 100644 --- a/src/ResponseTypes/ResponseTypeInterface.php +++ b/src/ResponseTypes/ResponseTypeInterface.php @@ -1,4 +1,5 @@ getMockBuilder(ClientRepositoryInterface::class)->getMock(), $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/Stubs/private.key', - \base64_encode(\random_bytes(36)), + base64_encode(random_bytes(36)), new StubResponseType() ); $server->enableGrantType(new GrantType(), new DateInterval('PT1M')); $authRequest = $server->validateAuthorizationRequest($this->createMock(ServerRequestInterface::class)); - $this->assertSame(GrantType::class, $authRequest->getGrantTypeId()); + self::assertSame(GrantType::class, $authRequest->getGrantTypeId()); } - */ - public function testRespondToRequestInvalidGrantType() + public function testRespondToRequestInvalidGrantType(): void { $server = new AuthorizationServer( $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(), $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/Stubs/private.key', - \base64_encode(\random_bytes(36)), + base64_encode(random_bytes(36)), new StubResponseType() ); $server->enableGrantType(new ClientCredentialsGrant(), new DateInterval('PT1M')); try { - $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response); + $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response()); } catch (OAuthServerException $e) { - $this->assertEquals('unsupported_grant_type', $e->getErrorType()); - $this->assertEquals(400, $e->getHttpStatusCode()); + self::assertEquals('unsupported_grant_type', $e->getErrorType()); + self::assertEquals(400, $e->getHttpStatusCode()); } } - public function testRespondToRequest() + public function testRespondToRequest(): void { $client = new ClientEntity(); @@ -91,6 +97,7 @@ public function testRespondToRequest() $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn($client); + $clientRepository->method('validateClient')->willReturn(true); $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); @@ -105,7 +112,7 @@ public function testRespondToRequest() $accessTokenRepositoryMock, $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', - \base64_encode(\random_bytes(36)), + base64_encode(random_bytes(36)), new StubResponseType() ); @@ -115,11 +122,11 @@ public function testRespondToRequest() $_POST['grant_type'] = 'client_credentials'; $_POST['client_id'] = 'foo'; $_POST['client_secret'] = 'bar'; - $response = $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response); - $this->assertEquals(200, $response->getStatusCode()); + $response = $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response()); + self::assertEquals(200, $response->getStatusCode()); } - public function testGetResponseType() + public function testGetResponseType(): void { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -131,14 +138,14 @@ public function testGetResponseType() 'file://' . __DIR__ . '/Stubs/public.key' ); - $abstractGrantReflection = new \ReflectionClass($server); + $abstractGrantReflection = new ReflectionClass($server); $method = $abstractGrantReflection->getMethod('getResponseType'); $method->setAccessible(true); - $this->assertInstanceOf(BearerTokenResponse::class, $method->invoke($server)); + self::assertInstanceOf(BearerTokenResponse::class, $method->invoke($server)); } - public function testGetResponseTypeExtended() + public function testGetResponseTypeExtended(): void { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $privateKey = 'file://' . __DIR__ . '/Stubs/private.key'; @@ -152,13 +159,13 @@ public function testGetResponseTypeExtended() 'file://' . __DIR__ . '/Stubs/public.key' ); - $abstractGrantReflection = new \ReflectionClass($server); + $abstractGrantReflection = new ReflectionClass($server); $method = $abstractGrantReflection->getMethod('getResponseType'); $method->setAccessible(true); $responseType = $method->invoke($server); - $responseTypeReflection = new \ReflectionClass($responseType); + $responseTypeReflection = new ReflectionClass($responseType); $privateKeyProperty = $responseTypeReflection->getProperty('privateKey'); $privateKeyProperty->setAccessible(true); @@ -167,23 +174,25 @@ public function testGetResponseTypeExtended() $encryptionKeyProperty->setAccessible(true); // generated instances should have keys setup - $this->assertSame($privateKey, $privateKeyProperty->getValue($responseType)->getKeyPath()); - $this->assertSame($encryptionKey, $encryptionKeyProperty->getValue($responseType)); + self::assertSame($privateKey, $privateKeyProperty->getValue($responseType)->getKeyPath()); + self::assertSame($encryptionKey, $encryptionKeyProperty->getValue($responseType)); } - public function testMultipleRequestsGetDifferentResponseTypeInstances() + public function testMultipleRequestsGetDifferentResponseTypeInstances(): void { $privateKey = 'file://' . __DIR__ . '/Stubs/private.key'; $encryptionKey = 'file://' . __DIR__ . '/Stubs/public.key'; $responseTypePrototype = new class extends BearerTokenResponse { - /* @return null|CryptKeyInterface */ - public function getPrivateKey() + protected CryptKeyInterface $privateKey; + protected Key|string|null $encryptionKey = null; + + public function getPrivateKey(): CryptKeyInterface { return $this->privateKey; } - public function getEncryptionKey() + public function getEncryptionKey(): Key|string|null { return $this->encryptionKey; } @@ -200,30 +209,26 @@ public function getEncryptionKey() $responseTypePrototype ); - $abstractGrantReflection = new \ReflectionClass($server); + $abstractGrantReflection = new ReflectionClass($server); $method = $abstractGrantReflection->getMethod('getResponseType'); $method->setAccessible(true); $responseTypeA = $method->invoke($server); $responseTypeB = $method->invoke($server); - // prototype should not get changed - $this->assertNull($responseTypePrototype->getPrivateKey()); - $this->assertNull($responseTypePrototype->getEncryptionKey()); - // generated instances should have keys setup - $this->assertSame($privateKey, $responseTypeA->getPrivateKey()->getKeyPath()); - $this->assertSame($encryptionKey, $responseTypeA->getEncryptionKey()); + self::assertSame($privateKey, $responseTypeA->getPrivateKey()->getKeyPath()); + self::assertSame($encryptionKey, $responseTypeA->getEncryptionKey()); // all instances should be different but based on the same prototype - $this->assertSame(\get_class($responseTypePrototype), \get_class($responseTypeA)); - $this->assertSame(\get_class($responseTypePrototype), \get_class($responseTypeB)); - $this->assertNotSame($responseTypePrototype, $responseTypeA); - $this->assertNotSame($responseTypePrototype, $responseTypeB); - $this->assertNotSame($responseTypeA, $responseTypeB); + self::assertSame(get_class($responseTypePrototype), get_class($responseTypeA)); + self::assertSame(get_class($responseTypePrototype), get_class($responseTypeB)); + self::assertNotSame($responseTypePrototype, $responseTypeA); + self::assertNotSame($responseTypePrototype, $responseTypeB); + self::assertNotSame($responseTypeA, $responseTypeB); } - public function testCompleteAuthorizationRequest() + public function testCompleteAuthorizationRequest(): void { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -247,7 +252,9 @@ public function testCompleteAuthorizationRequest() $server->enableGrantType($grant); $client = new ClientEntity(); - $client->setRedirectUri(self::REDIRECT_URI); + + $client->setRedirectUri('http://foo/bar'); + $client->setIdentifier('clientId'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -255,13 +262,15 @@ public function testCompleteAuthorizationRequest() $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); - $this->assertInstanceOf( - ResponseInterface::class, - $server->completeAuthorizationRequest($authRequest, new Response) - ); + $response = $server->completeAuthorizationRequest($authRequest, new Response()); + + $locationHeader = $response->getHeader('Location')[0]; + + self::assertStringStartsWith('http://foo/bar', $locationHeader); + self::assertStringContainsString('code=', $locationHeader); } - public function testValidateAuthorizationRequest() + public function testValidateAuthorizationRequest(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -305,56 +314,10 @@ public function testValidateAuthorizationRequest() ] ); - $this->assertInstanceOf(AuthorizationRequest::class, $server->validateAuthorizationRequest($request)); - } - - public function testValidateAuthorizationRequestWithMissingRedirectUri() - { - $client = new ClientEntity(); - $client->setConfidential(); - - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - $grant = new AuthCodeGrant( - $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), - $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new DateInterval('PT10M') - ); - $grant->setClientRepository($clientRepositoryMock); - - $server = new AuthorizationServer( - $clientRepositoryMock, - $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), - $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), - 'file://' . __DIR__ . '/Stubs/private.key', - 'file://' . __DIR__ . '/Stubs/public.key' - ); - $server->enableGrantType($grant); - - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - $headers = [], - $cookies = [], - $queryParams = [ - 'response_type' => 'code', - 'client_id' => 'foo', - ] - ); - - try { - $server->validateAuthorizationRequest($request); - } catch (OAuthServerException $e) { - $this->assertEquals('invalid_client', $e->getErrorType()); - $this->assertEquals(401, $e->getHttpStatusCode()); - } + self::assertInstanceOf(AuthorizationRequest::class, $server->validateAuthorizationRequest($request)); } - public function testValidateAuthorizationRequestUnregistered() + public function testValidateAuthorizationRequestUnregistered(): void { $server = new AuthorizationServer( $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(), @@ -369,7 +332,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/AuthorizationValidators/BearerTokenValidatorTest.php b/tests/AuthorizationValidators/BearerTokenValidatorTest.php index 94899ddd4..148473eea 100644 --- a/tests/AuthorizationValidators/BearerTokenValidatorTest.php +++ b/tests/AuthorizationValidators/BearerTokenValidatorTest.php @@ -1,5 +1,7 @@ getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -36,14 +41,14 @@ public function testBearerTokenValidatorAcceptsValidToken() ->withClaim('scopes', 'scope1 scope2 scope3 scope4') ->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key')); - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $validJwt->toString())); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $validJwt->toString())); $validRequest = $bearerTokenValidator->validateAuthorization($request); - $this->assertArrayHasKey('authorization', $validRequest->getHeaders()); + self::assertArrayHasKey('authorization', $validRequest->getHeaders()); } - public function testBearerTokenValidatorRejectsExpiredToken() + public function testBearerTokenValidatorRejectsExpiredToken(): void { $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -64,22 +69,22 @@ public function testBearerTokenValidatorRejectsExpiredToken() ->withClaim('scopes', 'scope1 scope2 scope3 scope4') ->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key')); - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $expiredJwt->toString())); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $expiredJwt->toString())); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(9); $bearerTokenValidator->validateAuthorization($request); } - public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway() + public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway(): void { $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); // We fake generating this token 10 seconds into the future, an extreme example of possible time drift between servers $future = (new DateTimeImmutable())->add(new DateInterval('PT10S')); - $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new \DateInterval('PT10S')); + $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new DateInterval('PT10S')); $bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $bearerTokenValidatorReflection = new ReflectionClass(BearerTokenValidator::class); @@ -96,21 +101,21 @@ public function testBearerTokenValidatorAcceptsExpiredTokenWithinLeeway() ->withClaim('scopes', 'scope1 scope2 scope3 scope4') ->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key')); - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $jwtTokenFromFutureWithinLeeway->toString())); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $jwtTokenFromFutureWithinLeeway->toString())); $validRequest = $bearerTokenValidator->validateAuthorization($request); - $this->assertArrayHasKey('authorization', $validRequest->getHeaders()); + self::assertArrayHasKey('authorization', $validRequest->getHeaders()); } - public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway() + public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway(): void { $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); // We fake generating this token 20 seconds into the future, an extreme example of possible time drift between servers $future = (new DateTimeImmutable())->add(new DateInterval('PT20S')); - $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new \DateInterval('PT10S')); + $bearerTokenValidator = new BearerTokenValidator($accessTokenRepositoryMock, new DateInterval('PT10S')); $bearerTokenValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $bearerTokenValidatorReflection = new ReflectionClass(BearerTokenValidator::class); @@ -127,9 +132,9 @@ public function testBearerTokenValidatorRejectsExpiredTokenBeyondLeeway() ->withClaim('scopes', 'scope1 scope2 scope3 scope4') ->getToken(new Sha256(), InMemory::file(__DIR__ . '/../Stubs/private.key')); - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $jwtTokenFromFutureBeyondLeeway->toString())); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $jwtTokenFromFutureBeyondLeeway->toString())); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(9); $bearerTokenValidator->validateAuthorization($request); diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php index a31ef6f34..6445d351e 100644 --- a/tests/Bootstrap.php +++ b/tests/Bootstrap.php @@ -1,6 +1,6 @@ assertEquals('plain', $verifier->getMethod()); + self::assertEquals('plain', $verifier->getMethod()); } - public function testVerifyCodeChallenge() + public function testVerifyCodeChallenge(): void { $verifier = new PlainVerifier(); - $this->assertTrue($verifier->verifyCodeChallenge('foo', 'foo')); - $this->assertFalse($verifier->verifyCodeChallenge('foo', 'bar')); + self::assertTrue($verifier->verifyCodeChallenge('foo', 'foo')); + self::assertFalse($verifier->verifyCodeChallenge('foo', 'bar')); } } diff --git a/tests/CodeChallengeVerifiers/S256VerifierTest.php b/tests/CodeChallengeVerifiers/S256VerifierTest.php index 58185a452..4bcbbe0e3 100644 --- a/tests/CodeChallengeVerifiers/S256VerifierTest.php +++ b/tests/CodeChallengeVerifiers/S256VerifierTest.php @@ -1,37 +1,44 @@ assertEquals('S256', $verifier->getMethod()); + self::assertEquals('S256', $verifier->getMethod()); } - public function testVerifyCodeChallengeSucceeds() + public function testVerifyCodeChallengeSucceeds(): void { $codeChallenge = $this->createCodeChallenge('foo'); $verifier = new S256Verifier(); - $this->assertTrue($verifier->verifyCodeChallenge('foo', $codeChallenge)); + self::assertTrue($verifier->verifyCodeChallenge('foo', $codeChallenge)); } - public function testVerifyCodeChallengeFails() + public function testVerifyCodeChallengeFails(): void { $codeChallenge = $this->createCodeChallenge('bar'); $verifier = new S256Verifier(); - $this->assertFalse($verifier->verifyCodeChallenge('foo', $codeChallenge)); + self::assertFalse($verifier->verifyCodeChallenge('foo', $codeChallenge)); } - private function createCodeChallenge($codeVerifier) + private function createCodeChallenge(string $codeVerifier): string { - return \strtr(\rtrim(\base64_encode(\hash('sha256', $codeVerifier, true)), '='), '+/', '-_'); + return strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'); } } diff --git a/tests/Exception/OAuthServerExceptionTest.php b/tests/Exception/OAuthServerExceptionTest.php index 38b86d433..27922b427 100644 --- a/tests/Exception/OAuthServerExceptionTest.php +++ b/tests/Exception/OAuthServerExceptionTest.php @@ -1,5 +1,7 @@ withParsedBody([ @@ -25,11 +29,11 @@ public function testInvalidClientExceptionSetsAuthenticateHeader() } catch (OAuthServerException $e) { $response = $e->generateHttpResponse(new Response()); - $this->assertTrue($response->hasHeader('WWW-Authenticate')); + self::assertTrue($response->hasHeader('WWW-Authenticate')); } } - public function testInvalidClientExceptionSetsBearerAuthenticateHeader() + public function testInvalidClientExceptionSetsBearerAuthenticateHeader(): void { $serverRequest = (new ServerRequest()) ->withParsedBody([ @@ -42,11 +46,11 @@ public function testInvalidClientExceptionSetsBearerAuthenticateHeader() } catch (OAuthServerException $e) { $response = $e->generateHttpResponse(new Response()); - $this->assertEquals(['Bearer realm="OAuth"'], $response->getHeader('WWW-Authenticate')); + self::assertEquals(['Bearer realm="OAuth"'], $response->getHeader('WWW-Authenticate')); } } - public function testInvalidClientExceptionOmitsAuthenticateHeader() + public function testInvalidClientExceptionOmitsAuthenticateHeader(): void { $serverRequest = (new ServerRequest()) ->withParsedBody([ @@ -58,11 +62,11 @@ public function testInvalidClientExceptionOmitsAuthenticateHeader() } catch (OAuthServerException $e) { $response = $e->generateHttpResponse(new Response()); - $this->assertFalse($response->hasHeader('WWW-Authenticate')); + self::assertFalse($response->hasHeader('WWW-Authenticate')); } } - public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAuthorizationHeader() + public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAuthorizationHeader(): void { $serverRequest = (new ServerRequest()) ->withParsedBody([ @@ -75,7 +79,7 @@ public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAutho } catch (OAuthServerException $e) { $response = $e->generateHttpResponse(new Response()); - $this->assertFalse($response->hasHeader('WWW-Authenticate')); + self::assertFalse($response->hasHeader('WWW-Authenticate')); } } @@ -84,7 +88,7 @@ public function testInvalidClientExceptionOmitsAuthenticateHeaderGivenEmptyAutho * * @throws OAuthServerException */ - private function issueInvalidClientException($serverRequest) + private function issueInvalidClientException(ServerRequestInterface $serverRequest): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('validateClient')->willReturn(false); @@ -92,7 +96,7 @@ private function issueInvalidClientException($serverRequest) $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); @@ -100,48 +104,48 @@ private function issueInvalidClientException($serverRequest) $validateClientMethod->invoke($grantMock, $serverRequest); } - public function testHasRedirect() + public function testHasRedirect(): void { $exceptionWithRedirect = OAuthServerException::accessDenied('some hint', 'https://example.com/error'); - $this->assertTrue($exceptionWithRedirect->hasRedirect()); + self::assertTrue($exceptionWithRedirect->hasRedirect()); } - public function testDoesNotHaveRedirect() + public function testDoesNotHaveRedirect(): void { $exceptionWithoutRedirect = OAuthServerException::accessDenied('Some hint'); - $this->assertFalse($exceptionWithoutRedirect->hasRedirect()); + self::assertFalse($exceptionWithoutRedirect->hasRedirect()); } - public function testHasPrevious() + public function testHasPrevious(): void { $previous = new Exception('This is the previous'); $exceptionWithPrevious = OAuthServerException::accessDenied(null, null, $previous); $previousMessage = $exceptionWithPrevious->getPrevious() !== null ? $exceptionWithPrevious->getPrevious()->getMessage() : null; - $this->assertSame('This is the previous', $previousMessage); + self::assertSame('This is the previous', $previousMessage); } - public function testDoesNotHavePrevious() + public function testDoesNotHavePrevious(): void { $exceptionWithoutPrevious = OAuthServerException::accessDenied(); - $this->assertNull($exceptionWithoutPrevious->getPrevious()); + self::assertNull($exceptionWithoutPrevious->getPrevious()); } - public function testCanGetRedirectionUri() + public function testCanGetRedirectionUri(): void { $exceptionWithRedirect = OAuthServerException::accessDenied('some hint', 'https://example.com/error'); - $this->assertSame('https://example.com/error', $exceptionWithRedirect->getRedirectUri()); + self::assertSame('https://example.com/error', $exceptionWithRedirect->getRedirectUri()); } - public function testInvalidCredentialsIsInvalidGrant() + public function testInvalidCredentialsIsInvalidGrant(): void { $exception = OAuthServerException::invalidCredentials(); - $this->assertSame('invalid_grant', $exception->getErrorType()); + self::assertSame('invalid_grant', $exception->getErrorType()); } } diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 618545efe..37ff51c4d 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -1,5 +1,7 @@ getMockForAbstractClass(AbstractGrant::class); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); - $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . \base64_encode('Open:Sesame')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('Open:Sesame')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); - $this->assertSame(['Open', 'Sesame'], $basicAuthMethod->invoke($grantMock, $serverRequest)); + self::assertSame(['Open', 'Sesame'], $basicAuthMethod->invoke($grantMock, $serverRequest)); } - public function testHttpBasicNoPassword() + public function testHttpBasicNoPassword(): void { /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); - $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . \base64_encode('Open:')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('Open:')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); - $this->assertSame(['Open', ''], $basicAuthMethod->invoke($grantMock, $serverRequest)); + self::assertSame(['Open', ''], $basicAuthMethod->invoke($grantMock, $serverRequest)); } - public function testHttpBasicNotBasic() + public function testHttpBasicNotBasic(): void { /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); - $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Foo ' . \base64_encode('Open:Sesame')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Foo ' . base64_encode('Open:Sesame')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); - $this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest)); + self::assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest)); } - public function testHttpBasicNotBase64() + public function testHttpBasicNotBase64(): void { /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ||'); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); - $this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest)); + self::assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest)); } - public function testHttpBasicNoColon() + public function testHttpBasicNoColon(): void { /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); - $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . \base64_encode('OpenSesame')); + $serverRequest = (new ServerRequest())->withHeader('Authorization', 'Basic ' . base64_encode('OpenSesame')); $basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials'); $basicAuthMethod->setAccessible(true); - $this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest)); + self::assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest)); } - public function testGetClientCredentialsClientSecretNotAString() + public function testGetClientCredentialsClientSecretNotAString(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -97,7 +104,7 @@ public function testGetClientCredentialsClientSecretNotAString() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = new ServerRequest( [], @@ -116,24 +123,26 @@ public function testGetClientCredentialsClientSecretNotAString() $getClientCredentialsMethod = $abstractGrantReflection->getMethod('getClientCredentials'); $getClientCredentialsMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $getClientCredentialsMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientPublic() + public function testValidateClientPublic(): void { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -143,22 +152,24 @@ public function testValidateClientPublic() $validateClientMethod->setAccessible(true); $result = $validateClientMethod->invoke($grantMock, $serverRequest); - $this->assertEquals($client, $result); + self::assertEquals($client, $result); } - public function testValidateClientConfidential() + public function testValidateClientConfidential(): void { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -170,10 +181,9 @@ public function testValidateClientConfidential() $validateClientMethod->setAccessible(true); $result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true); - $this->assertEquals($client, $result); + self::assertEquals($client, $result); } - - public function testValidateClientMissingClientId() + public function testValidateClientMissingClientId(): void { $client = new ClientEntity(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -183,18 +193,18 @@ public function testValidateClientMissingClientId() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = new ServerRequest(); $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientMissingClientSecret() + public function testValidateClientMissingClientSecret(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('validateClient')->willReturn(false); @@ -203,7 +213,7 @@ public function testValidateClientMissingClientSecret() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -212,12 +222,12 @@ public function testValidateClientMissingClientSecret() $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientInvalidClientSecret() + public function testValidateClientInvalidClientSecret(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('validateClient')->willReturn(false); @@ -226,7 +236,7 @@ public function testValidateClientInvalidClientSecret() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -236,12 +246,12 @@ public function testValidateClientInvalidClientSecret() $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientInvalidRedirectUri() + public function testValidateClientInvalidRedirectUri(): void { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); @@ -252,7 +262,7 @@ public function testValidateClientInvalidRedirectUri() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -262,12 +272,12 @@ public function testValidateClientInvalidRedirectUri() $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientInvalidRedirectUriArray() + public function testValidateClientInvalidRedirectUriArray(): void { $client = new ClientEntity(); $client->setRedirectUri(['http://foo/bar']); @@ -278,7 +288,7 @@ public function testValidateClientInvalidRedirectUriArray() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -288,12 +298,12 @@ public function testValidateClientInvalidRedirectUriArray() $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientMalformedRedirectUri() + public function testValidateClientMalformedRedirectUri(): void { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); @@ -304,7 +314,7 @@ public function testValidateClientMalformedRedirectUri() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -314,12 +324,12 @@ public function testValidateClientMalformedRedirectUri() $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $validateClientMethod->invoke($grantMock, $serverRequest, true, true); } - public function testValidateClientBadClient() + public function testValidateClientBadClient(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('validateClient')->willReturn(false); @@ -328,7 +338,7 @@ public function testValidateClientBadClient() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setClientRepository($clientRepositoryMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -338,12 +348,12 @@ public function testValidateClientBadClient() $validateClientMethod = $abstractGrantReflection->getMethod('validateClient'); $validateClientMethod->setAccessible(true); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $validateClientMethod->invoke($grantMock, $serverRequest, true); } - public function testCanRespondToRequest() + public function testCanRespondToRequest(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->method('getIdentifier')->willReturn('foobar'); @@ -352,14 +362,14 @@ public function testCanRespondToRequest() 'grant_type' => 'foobar', ]); - $this->assertTrue($grantMock->canRespondToAccessTokenRequest($serverRequest)); + self::assertTrue($grantMock->canRespondToAccessTokenRequest($serverRequest)); } - public function testIssueRefreshToken() + public function testIssueRefreshToken(): void { $refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepoMock - ->expects($this->once()) + ->expects(self::once()) ->method('getNewRefreshToken') ->willReturn(new RefreshTokenEntity()); @@ -368,39 +378,40 @@ public function testIssueRefreshToken() $grantMock->setRefreshTokenTTL(new DateInterval('PT1M')); $grantMock->setRefreshTokenRepository($refreshTokenRepoMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken'); $issueRefreshTokenMethod->setAccessible(true); $accessToken = new AccessTokenEntity(); + /** @var RefreshTokenEntityInterface $refreshToken */ $refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $refreshToken); - $this->assertEquals($accessToken, $refreshToken->getAccessToken()); + + self::assertEquals($accessToken, $refreshToken->getAccessToken()); } - public function testIssueNullRefreshToken() + public function testIssueNullRefreshToken(): void { $refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepoMock - ->expects($this->once()) + ->expects(self::once()) ->method('getNewRefreshToken') ->willReturn(null); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setRefreshTokenTTL(new \DateInterval('PT1M')); + $grantMock->setRefreshTokenTTL(new DateInterval('PT1M')); $grantMock->setRefreshTokenRepository($refreshTokenRepoMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken'); $issueRefreshTokenMethod->setAccessible(true); $accessToken = new AccessTokenEntity(); - $this->assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken)); + self::assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken)); } - public function testIssueAccessToken() + public function testIssueAccessToken(): void { $accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepoMock->method('getNewToken')->willReturn(new AccessTokenEntity()); @@ -410,7 +421,7 @@ public function testIssueAccessToken() $grantMock->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grantMock->setAccessTokenRepository($accessTokenRepoMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $issueAccessTokenMethod = $abstractGrantReflection->getMethod('issueAccessToken'); $issueAccessTokenMethod->setAccessible(true); @@ -422,23 +433,27 @@ public function testIssueAccessToken() 123, [new ScopeEntity()] ); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $accessToken); + + self::assertNotEmpty($accessToken->getIdentifier()); } - public function testIssueAuthCode() + public function testIssueAuthCode(): void { $authCodeRepoMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); - $authCodeRepoMock->expects($this->once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); + $authCodeRepoMock->expects(self::once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setAuthCodeRepository($authCodeRepoMock); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $issueAuthCodeMethod = $abstractGrantReflection->getMethod('issueAuthCode'); $issueAuthCodeMethod->setAccessible(true); - $this->assertInstanceOf( + $scope = new ScopeEntity(); + $scope->setIdentifier('scopeId'); + + self::assertInstanceOf( AuthCodeEntityInterface::class, $issueAuthCodeMethod->invoke( $grantMock, @@ -446,17 +461,17 @@ public function testIssueAuthCode() new ClientEntity(), 123, 'http://foo/bar', - [new ScopeEntity()] + [$scope] ) ); } - public function testGetCookieParameter() + public function testGetCookieParameter(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->method('getIdentifier')->willReturn('foobar'); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $method = $abstractGrantReflection->getMethod('getCookieParameter'); $method->setAccessible(true); @@ -464,16 +479,16 @@ public function testGetCookieParameter() 'foo' => 'bar', ]); - $this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest)); - $this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo')); + self::assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest)); + self::assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo')); } - public function testGetQueryStringParameter() + public function testGetQueryStringParameter(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->method('getIdentifier')->willReturn('foobar'); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $method = $abstractGrantReflection->getMethod('getQueryStringParameter'); $method->setAccessible(true); @@ -481,24 +496,24 @@ public function testGetQueryStringParameter() 'foo' => 'bar', ]); - $this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest)); - $this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo')); + self::assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest)); + self::assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo')); } - public function testValidateScopes() + public function testValidateScopes(): void { $scope = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->expects($this->exactly(3))->method('getScopeEntityByIdentifier')->willReturn($scope); + $scopeRepositoryMock->expects(self::exactly(3))->method('getScopeEntityByIdentifier')->willReturn($scope); /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setScopeRepository($scopeRepositoryMock); - $this->assertEquals([$scope, $scope, $scope], $grantMock->validateScopes('basic test 0 ')); + self::assertEquals([$scope, $scope, $scope], $grantMock->validateScopes('basic test 0 ')); } - public function testValidateScopesBadScope() + public function testValidateScopesBadScope(): void { $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null); @@ -507,42 +522,42 @@ public function testValidateScopesBadScope() $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); $grantMock->setScopeRepository($scopeRepositoryMock); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grantMock->validateScopes('basic '); } - public function testGenerateUniqueIdentifier() + public function testGenerateUniqueIdentifier(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $abstractGrantReflection = new \ReflectionClass($grantMock); + $abstractGrantReflection = new ReflectionClass($grantMock); $method = $abstractGrantReflection->getMethod('generateUniqueIdentifier'); $method->setAccessible(true); - $this->assertIsString($method->invoke($grantMock)); + self::assertIsString($method->invoke($grantMock)); } - public function testCanRespondToAuthorizationRequest() + public function testCanRespondToAuthorizationRequest(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $this->assertFalse($grantMock->canRespondToAuthorizationRequest(new ServerRequest())); + self::assertFalse($grantMock->canRespondToAuthorizationRequest(new ServerRequest())); } - public function testValidateAuthorizationRequest() + public function testValidateAuthorizationRequest(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $grantMock->validateAuthorizationRequest(new ServerRequest()); } - public function testCompleteAuthorizationRequest() + public function testCompleteAuthorizationRequest(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $grantMock->completeAuthorizationRequest(new AuthorizationRequest()); } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 7281d5357..1a58983ae 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -1,12 +1,13 @@ cryptStub = new CryptTraitStub(); } - public function testGetIdentifier() + public function testGetIdentifier(): void { $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -55,10 +59,10 @@ public function testGetIdentifier() new DateInterval('PT10M') ); - $this->assertEquals('authorization_code', $grant->getIdentifier()); + self::assertEquals('authorization_code', $grant->getIdentifier()); } - public function testCanRespondToAuthorizationRequest() + public function testCanRespondToAuthorizationRequest(): void { $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -80,10 +84,10 @@ public function testCanRespondToAuthorizationRequest() ] ); - $this->assertTrue($grant->canRespondToAuthorizationRequest($request)); + self::assertTrue($grant->canRespondToAuthorizationRequest($request)); } - public function testValidateAuthorizationRequest() + public function testValidateAuthorizationRequest(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -120,10 +124,10 @@ public function testValidateAuthorizationRequest() ] ); - $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); + self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } - public function testValidateAuthorizationRequestRedirectUriArray() + public function testValidateAuthorizationRequestRedirectUriArray(): void { $client = new ClientEntity(); $client->setRedirectUri([self::REDIRECT_URI]); @@ -159,10 +163,10 @@ public function testValidateAuthorizationRequestRedirectUriArray() ] ); - $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); + self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } - public function testValidateAuthorizationRequestWithoutRedirectUri() + public function testValidateAuthorizationRequestWithoutRedirectUri(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -199,12 +203,12 @@ public function testValidateAuthorizationRequestWithoutRedirectUri() ); $authorizationRequest = $grant->validateAuthorizationRequest($request); - $this->assertInstanceOf(AuthorizationRequest::class, $authorizationRequest); + self::assertInstanceOf(AuthorizationRequest::class, $authorizationRequest); - $this->assertEmpty($authorizationRequest->getRedirectUri()); + self::assertEmpty($authorizationRequest->getRedirectUri()); } - public function testValidateAuthorizationRequestCodeChallenge() + public function testValidateAuthorizationRequestCodeChallenge(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -241,15 +245,16 @@ public function testValidateAuthorizationRequestCodeChallenge() ] ); - $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); + self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } - public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort() + public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -257,26 +262,29 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooSho new DateInterval('PT10M') ); + $grant->setDefaultScope(self::DEFAULT_SCOPE); $grant->setClientRepository($clientRepositoryMock); + $grant->setScopeRepository($scopeRepositoryMock); $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => \str_repeat('A', 42), + 'code_challenge' => str_repeat('A', 42), ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->validateAuthorizationRequest($request); } - public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong() + public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -285,25 +293,28 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLon ); $grant->setClientRepository($clientRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setScopeRepository($scopeRepositoryMock); $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => \str_repeat('A', 129), + 'code_challenge' => str_repeat('A', 129), ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->validateAuthorizationRequest($request); } - public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() + public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -312,20 +323,22 @@ public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() ); $grant->setClientRepository($clientRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setScopeRepository($scopeRepositoryMock); $request = (new ServerRequest())->withQueryParams([ 'response_type' => 'code', 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, - 'code_challenge' => \str_repeat('A', 42) . '!', + 'code_challenge' => str_repeat('A', 42) . '!', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->validateAuthorizationRequest($request); } - public function testValidateAuthorizationRequestMissingClientId() + public function testValidateAuthorizationRequestMissingClientId(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -340,13 +353,13 @@ public function testValidateAuthorizationRequestMissingClientId() 'response_type' => 'code', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->validateAuthorizationRequest($request); } - public function testValidateAuthorizationRequestInvalidClientId() + public function testValidateAuthorizationRequestInvalidClientId(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn(null); @@ -363,13 +376,13 @@ public function testValidateAuthorizationRequestInvalidClientId() 'client_id' => 'foo', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - public function testValidateAuthorizationRequestBadRedirectUriString() + public function testValidateAuthorizationRequestBadRedirectUriString(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -389,13 +402,13 @@ 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); } - public function testValidateAuthorizationRequestBadRedirectUriArray() + public function testValidateAuthorizationRequestBadRedirectUriArray(): void { $client = new ClientEntity(); $client->setRedirectUri([self::REDIRECT_URI]); @@ -415,13 +428,13 @@ public function testValidateAuthorizationRequestBadRedirectUriArray() 'redirect_uri' => 'http://bar', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - public function testValidateAuthorizationRequestInvalidCodeChallengeMethod() + public function testValidateAuthorizationRequestInvalidCodeChallengeMethod(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -450,16 +463,17 @@ 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); } - public function testCompleteAuthorizationRequest() + public function testCompleteAuthorizationRequest(): void { $client = new ClientEntity(); - $client->setRedirectUri(self::REDIRECT_URI); + $client->setIdentifier('clientId'); + $client->setRedirectUri('http://foo/bar'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -477,13 +491,15 @@ public function testCompleteAuthorizationRequest() ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); + self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - public function testCompleteAuthorizationRequestWithMultipleRedirectUrisOnClient() + public function testCompleteAuthorizationRequestWithMultipleRedirectUrisOnClient(): void { $client = new ClientEntity(); + $client->setIdentifier('clientId'); $client->setRedirectUri(['uriOne', 'uriTwo']); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); $authRequest->setClient($client); @@ -500,13 +516,13 @@ public function testCompleteAuthorizationRequestWithMultipleRedirectUrisOnClient ); $grant->setEncryptionKey($this->cryptStub->getKey()); - $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); + self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - public function testCompleteAuthorizationRequestDenied() + public function testCompleteAuthorizationRequestDenied(): void { $client = new ClientEntity(); - $client->setRedirectUri(self::REDIRECT_URI); + $client->setRedirectUri('http://foo/bar'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(false); @@ -524,20 +540,24 @@ 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); } - public function testRespondToAccessTokenRequest() + public function testRespondToAccessTokenRequest(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -578,16 +598,14 @@ public function testRespondToAccessTokenRequest() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + ], JSON_THROW_ON_ERROR) ), ] ); @@ -595,11 +613,10 @@ public function testRespondToAccessTokenRequest() /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testRespondToAccessTokenRequestUsingHttpBasicAuth() + public function testRespondToAccessTokenRequestUsingHttpBasicAuth(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -620,7 +637,7 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth() $authCodeGrant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $refreshTokenRepositoryMock, - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $authCodeGrant->setClientRepository($clientRepositoryMock); @@ -644,28 +661,25 @@ public function testRespondToAccessTokenRequestUsingHttpBasicAuth() 'grant_type' => 'authorization_code', 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( - \json_encode( - [ - 'auth_code_id' => \uniqid(), - 'client_id' => 'foo', - 'expire_time' => \time() + 3600, - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'client_id' => 'foo', + 'expire_time' => time() + 3600, + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + ], JSON_THROW_ON_ERROR) ), ] ); /** @var StubResponseType $response */ - $response = $authCodeGrant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $authCodeGrant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testRespondToAccessTokenRequestForPublicClient() + public function testRespondToAccessTokenRequestForPublicClient(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -712,16 +726,14 @@ public function testRespondToAccessTokenRequestForPublicClient() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + ], JSON_THROW_ON_ERROR) ), ] ); @@ -729,11 +741,10 @@ public function testRespondToAccessTokenRequestForPublicClient() /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testRespondToAccessTokenRequestNullRefreshToken() + public function testRespondToAccessTokenRequestNullRefreshToken(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -757,7 +768,7 @@ public function testRespondToAccessTokenRequestNullRefreshToken() $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), $refreshTokenRepositoryMock, - new \DateInterval('PT10M') + new DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); @@ -780,35 +791,36 @@ public function testRespondToAccessTokenRequestNullRefreshToken() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + ], JSON_THROW_ON_ERROR) ), ] ); /** @var StubResponseType $response */ - $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M')); + $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertNull($response->getRefreshToken()); + self::assertNull($response->getRefreshToken()); } - public function testRespondToAccessTokenRequestCodeChallengePlain() + public function testRespondToAccessTokenRequestCodeChallengePlain(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -851,18 +863,16 @@ public function testRespondToAccessTokenRequestCodeChallengePlain() 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, '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, - 'code_challenge_method' => 'plain', - ] - ) + 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, + 'code_challenge_method' => 'plain', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -870,18 +880,21 @@ public function testRespondToAccessTokenRequestCodeChallengePlain() /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testRespondToAccessTokenRequestCodeChallengeS256() + public function testRespondToAccessTokenRequestCodeChallengeS256(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -924,18 +937,16 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, '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, - 'code_challenge_method' => 'S256', - ] - ) + 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, + 'code_challenge_method' => 'S256', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -943,11 +954,10 @@ public function testRespondToAccessTokenRequestCodeChallengeS256() /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testPKCEDowngradeBlocked() + public function testPKCEDowngradeBlocked(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -955,6 +965,7 @@ public function testPKCEDowngradeBlocked() $client->setConfidential(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -997,35 +1008,40 @@ public function testPKCEDowngradeBlocked() 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, 'code' => $this->cryptStub->doEncrypt( - \json_encode( + json_encode( [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, 'client_id' => 'foo', 'user_id' => 123, 'scopes' => ['foo'], 'redirect_uri' => self::REDIRECT_URI, - ] + ], + JSON_THROW_ON_ERROR ) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } - public function testRespondToAccessTokenRequestMissingRedirectUri() + public function testRespondToAccessTokenRequestMissingRedirectUri(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setConfidential(); $client->setRedirectUri(self::REDIRECT_URI); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -1048,32 +1064,34 @@ public function testRespondToAccessTokenRequestMissingRedirectUri() 'client_id' => 'foo', 'grant_type' => 'authorization_code', 'code' => $this->cryptStub->doEncrypt( - \json_encode( - [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ], JSON_THROW_ON_ERROR) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } - public function testRespondToAccessTokenRequestRedirectUriMismatch() + public function testRespondToAccessTokenRequestRedirectUriMismatch(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setConfidential(); $client->setRedirectUri('http://bar/foo'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -1097,31 +1115,33 @@ public function testRespondToAccessTokenRequestRedirectUriMismatch() 'grant_type' => 'authorization_code', 'redirect_uri' => 'http://bar/foo', 'code' => $this->cryptStub->doEncrypt( - \json_encode( - [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'foo', - 'redirect_uri' => self::REDIRECT_URI, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + ], JSON_THROW_ON_ERROR) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } - public function testRespondToAccessTokenRequestMissingCode() + public function testRespondToAccessTokenRequestMissingCode(): void { $client = new ClientEntity(); + $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -1153,14 +1173,14 @@ public function testRespondToAccessTokenRequestMissingCode() ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } - public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode() + public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -1191,16 +1211,14 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + json_encode([ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1209,11 +1227,11 @@ public function testRespondToAccessTokenRequestWithRefreshTokenInsteadOfAuthCode /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals('Authorization code malformed', $e->getHint()); + self::assertEquals('Authorization code malformed', $e->getHint()); } } - public function testRespondToAccessTokenRequestWithAuthCodeNotAString() + public function testRespondToAccessTokenRequestWithAuthCodeNotAString(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -1247,11 +1265,11 @@ public function testRespondToAccessTokenRequestWithAuthCodeNotAString() ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } - public function testRespondToAccessTokenRequestExpiredCode() + public function testRespondToAccessTokenRequestExpiredCode(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -1282,16 +1300,14 @@ public function testRespondToAccessTokenRequestExpiredCode() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() - 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1300,18 +1316,22 @@ public function testRespondToAccessTokenRequestExpiredCode() /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Authorization code has expired'); + self::assertEquals($e->getHint(), 'Authorization code has expired'); } } - public function testRespondToAccessTokenRequestRevokedCode() + public function testRespondToAccessTokenRequestRevokedCode(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1346,16 +1366,14 @@ public function testRespondToAccessTokenRequestRevokedCode() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1364,19 +1382,23 @@ public function testRespondToAccessTokenRequestRevokedCode() /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Authorization code has been revoked'); - $this->assertEquals($e->getErrorType(), 'invalid_grant'); + self::assertEquals($e->getHint(), 'Authorization code has been revoked'); + self::assertEquals($e->getErrorType(), 'invalid_grant'); } } - public function testRespondToAccessTokenRequestClientMismatch() + public function testRespondToAccessTokenRequestClientMismatch(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1408,16 +1430,14 @@ public function testRespondToAccessTokenRequestClientMismatch() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, 'code' => $this->cryptStub->doEncrypt( - \json_encode( - [ - 'auth_code_id' => \uniqid(), - 'expire_time' => \time() + 3600, - 'client_id' => 'bar', - 'user_id' => 123, - 'scopes' => ['foo'], - 'redirect_uri' => self::REDIRECT_URI, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'bar', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => 'http://foo/bar', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1426,18 +1446,22 @@ public function testRespondToAccessTokenRequestClientMismatch() /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Authorization code was not issued to this client'); + self::assertEquals($e->getHint(), 'Authorization code was not issued to this client'); } } - public function testRespondToAccessTokenRequestBadCodeEncryption() + public function testRespondToAccessTokenRequestBadCodeEncryption(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -1476,18 +1500,22 @@ public function testRespondToAccessTokenRequestBadCodeEncryption() /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Cannot decrypt the authorization code'); + self::assertEquals($e->getHint(), 'Cannot decrypt the authorization code'); } } - public function testRespondToAccessTokenRequestBadCodeVerifierPlain() + public function testRespondToAccessTokenRequestBadCodeVerifierPlain(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1529,18 +1557,16 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain() 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => self::CODE_VERIFIER, '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', - 'code_challenge_method' => 'plain', - ] - ) + 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', + 'code_challenge_method' => 'plain', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1549,18 +1575,22 @@ public function testRespondToAccessTokenRequestBadCodeVerifierPlain() /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.'); + self::assertEquals($e->getHint(), 'Failed to verify `code_verifier`.'); } } - public function testRespondToAccessTokenRequestBadCodeVerifierS256() + public function testRespondToAccessTokenRequestBadCodeVerifierS256(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1602,18 +1632,16 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256() 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'nope', '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', - 'code_challenge_method' => 'S256', - ] - ) + 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', + 'code_challenge_method' => 'S256', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1622,18 +1650,22 @@ public function testRespondToAccessTokenRequestBadCodeVerifierS256() /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); + self::assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); } } - public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidChars() + public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidChars(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1675,18 +1707,16 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHYtytmhGTigKdZCXfxq-+xbsk9_GxUcaE', // Malformed code. Contains `+`. '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, - 'code_challenge_method' => 'S256', - ] - ) + 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, + 'code_challenge_method' => 'S256', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1695,18 +1725,22 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); + self::assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); } } - public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidLength() + public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInvalidLength(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1748,18 +1782,16 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva 'redirect_uri' => self::REDIRECT_URI, 'code_verifier' => 'dqX7C-RbqjHY', // Malformed code. Invalid length. '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', - 'code_challenge_method' => 'S256', - ] - ) + 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', + 'code_challenge_method' => 'S256', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1768,18 +1800,22 @@ public function testRespondToAccessTokenRequestMalformedCodeVerifierS256WithInva /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); + self::assertEquals($e->getHint(), 'Code Verifier must follow the specifications of RFC-7636.'); } } - public function testRespondToAccessTokenRequestMissingCodeVerifier() + public function testRespondToAccessTokenRequestMissingCodeVerifier(): void { $client = new ClientEntity(); + $client->setIdentifier('foo'); $client->setRedirectUri(self::REDIRECT_URI); $client->setConfidential(); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeEntity = new ScopeEntity(); @@ -1820,18 +1856,16 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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', - 'code_challenge_method' => 'plain', - ] - ) + 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', + 'code_challenge_method' => 'plain', + ], JSON_THROW_ON_ERROR) ), ] ); @@ -1840,13 +1874,14 @@ public function testRespondToAccessTokenRequestMissingCodeVerifier() /* @var StubResponseType $response */ $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); } catch (OAuthServerException $e) { - $this->assertEquals($e->getHint(), 'Check the `code_verifier` parameter'); + self::assertEquals($e->getHint(), 'Check the `code_verifier` parameter'); } } - public function testAuthCodeRepositoryUniqueConstraintCheck() + public function testAuthCodeRepositoryUniqueConstraintCheck(): void { $client = new ClientEntity(); + $client->setIdentifier('clientId'); $client->setRedirectUri(self::REDIRECT_URI); $authRequest = new AuthorizationRequest(); @@ -1858,12 +1893,12 @@ public function testAuthCodeRepositoryUniqueConstraintCheck() $authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(); $authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity()); - $matcher = $this->exactly(2); + $matcher = self::exactly(2); $authCodeRepository ->expects($matcher) ->method('persistNewAuthCode') - ->willReturnCallback(function () use ($matcher) { + ->willReturnCallback(function () use ($matcher): void { if ($matcher->getInvocationCount() === 1) { throw UniqueTokenIdentifierConstraintViolationException::create(); } @@ -1877,14 +1912,18 @@ public function testAuthCodeRepositoryUniqueConstraintCheck() $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); + self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - public function testAuthCodeRepositoryFailToPersist() + public function testAuthCodeRepositoryFailToPersist(): void { + $client = new ClientEntity(); + + $client->setRedirectUri('http://foo/bar'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); @@ -1899,17 +1938,21 @@ 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)); + self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop() + public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop(): void { + $client = new ClientEntity(); + + $client->setRedirectUri('http://foo/bar'); + $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); @@ -1923,13 +1966,13 @@ 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)); + self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - public function testRefreshTokenRepositoryUniqueConstraintCheck() + public function testRefreshTokenRepositoryUniqueConstraintCheck(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -1949,12 +1992,12 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); - $matcher = $this->exactly(2); + $matcher = self::exactly(2); $refreshTokenRepositoryMock ->expects($matcher) ->method('persistNewRefreshToken') - ->willReturnCallback(function () use ($matcher) { + ->willReturnCallback(function () use ($matcher): void { if ($matcher->getInvocationCount() === 1) { throw UniqueTokenIdentifierConstraintViolationException::create(); } @@ -1986,16 +2029,14 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + ], JSON_THROW_ON_ERROR) ), ] ); @@ -2003,11 +2044,10 @@ public function testRefreshTokenRepositoryUniqueConstraintCheck() /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testRefreshTokenRepositoryFailToPersist() + public function testRefreshTokenRepositoryFailToPersist(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -2054,31 +2094,28 @@ public function testRefreshTokenRepositoryFailToPersist() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + ], JSON_THROW_ON_ERROR) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(7); /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() + public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -2125,31 +2162,28 @@ public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop() 'client_id' => 'foo', 'redirect_uri' => self::REDIRECT_URI, '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, - ] - ) + json_encode([ + 'auth_code_id' => uniqid(), + 'expire_time' => time() + 3600, + 'client_id' => 'foo', + 'user_id' => 123, + 'scopes' => ['foo'], + 'redirect_uri' => self::REDIRECT_URI, + ], JSON_THROW_ON_ERROR) ), ] ); - $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); /** @var StubResponseType $response */ $response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new DateInterval('PT10M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken()); } - public function testCompleteAuthorizationRequestNoUser() + public function testCompleteAuthorizationRequestNoUser(): void { $grant = new AuthCodeGrant( $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), @@ -2157,12 +2191,12 @@ public function testCompleteAuthorizationRequestNoUser() new DateInterval('PT10M') ); - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $grant->completeAuthorizationRequest(new AuthorizationRequest()); } - public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequiredButNotGiven() + public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequiredButNotGiven(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -2197,7 +2231,7 @@ public function testPublicClientAuthCodeRequestRejectedWhenCodeChallengeRequired $grant->validateAuthorizationRequest($request); } - public function testUseValidRedirectUriIfScopeCheckFails() + public function testUseValidRedirectUriIfScopeCheckFails(): void { $client = new ClientEntity(); $client->setRedirectUri([self::REDIRECT_URI, 'http://bar/foo']); @@ -2239,48 +2273,7 @@ public function testUseValidRedirectUriIfScopeCheckFails() } catch (OAuthServerException $e) { $response = $e->generateHttpResponse(new Response()); - $this->assertStringStartsWith('http://bar/foo', $response->getHeader('Location')[0]); + self::assertStringStartsWith('http://bar/foo', $response->getHeader('Location')[0]); } } - - public function testThrowExceptionWhenNoClientRedirectUriRegistered() - { - $client = (new ClientEntity())->setConfidential(); - - $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); - $clientRepositoryMock->method('getClientEntity')->willReturn($client); - - $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); - $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null); - - $grant = new AuthCodeGrant( - $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), - $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), - new DateInterval('PT10M') - ); - - $grant->setClientRepository($clientRepositoryMock); - $grant->setScopeRepository($scopeRepositoryMock); - $grant->setDefaultScope(self::DEFAULT_SCOPE); - - $request = new ServerRequest( - [], - [], - null, - null, - 'php://input', - [], - [], - [ - 'response_type' => 'code', - 'client_id' => 'foo', - 'redirect_uri' => 'http://bar/foo', - ] - ); - - $this->expectException(OAuthServerException::class); - $this->expectExceptionCode(4); - - $grant->validateAuthorizationRequest($request); - } } diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 13ea78bae..264e026e2 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -1,11 +1,12 @@ assertEquals('client_credentials', $grant->getIdentifier()); + self::assertEquals('client_credentials', $grant->getIdentifier()); } - public function testRespondToRequest() + public function testRespondToRequest(): void { $client = new ClientEntity(); $client->setConfidential(); @@ -34,6 +35,7 @@ public function testRespondToRequest() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); @@ -57,8 +59,8 @@ public function testRespondToRequest() ]); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); + $response = $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); + self::assertNotEmpty($response->getAccessToken()->getIdentifier()); } } diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 5f69242c7..36a5294c8 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -1,5 +1,7 @@ cryptStub = new CryptTraitStub(); } - public function testGetIdentifier() + public function testGetIdentifier(): void { $grant = new ImplicitGrant(new DateInterval('PT10M')); - $this->assertEquals('implicit', $grant->getIdentifier()); + self::assertEquals('implicit', $grant->getIdentifier()); } - public function testCanRespondToAccessTokenRequest() + public function testCanRespondToAccessTokenRequest(): void { $grant = new ImplicitGrant(new DateInterval('PT10M')); - $this->assertFalse( + self::assertFalse( $grant->canRespondToAccessTokenRequest(new ServerRequest()) ); } - public function testRespondToAccessTokenRequest() + public function testRespondToAccessTokenRequest(): void { $grant = new ImplicitGrant(new DateInterval('PT10M')); - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $grant->respondToAccessTokenRequest( new ServerRequest(), @@ -65,7 +65,7 @@ public function testRespondToAccessTokenRequest() ); } - public function testCanRespondToAuthorizationRequest() + public function testCanRespondToAuthorizationRequest(): void { $grant = new ImplicitGrant(new DateInterval('PT10M')); @@ -74,10 +74,10 @@ public function testCanRespondToAuthorizationRequest() 'client_id' => 'foo', ]); - $this->assertTrue($grant->canRespondToAuthorizationRequest($request)); + self::assertTrue($grant->canRespondToAuthorizationRequest($request)); } - public function testValidateAuthorizationRequest() + public function testValidateAuthorizationRequest(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -99,10 +99,10 @@ public function testValidateAuthorizationRequest() 'redirect_uri' => self::REDIRECT_URI, ]); - $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); + self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } - public function testValidateAuthorizationRequestRedirectUriArray() + public function testValidateAuthorizationRequestRedirectUriArray(): void { $client = new ClientEntity(); $client->setRedirectUri([self::REDIRECT_URI]); @@ -124,10 +124,10 @@ public function testValidateAuthorizationRequestRedirectUriArray() 'redirect_uri' => self::REDIRECT_URI, ]); - $this->assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); + self::assertInstanceOf(AuthorizationRequest::class, $grant->validateAuthorizationRequest($request)); } - public function testValidateAuthorizationRequestMissingClientId() + public function testValidateAuthorizationRequestMissingClientId(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -136,13 +136,13 @@ 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); } - public function testValidateAuthorizationRequestInvalidClientId() + public function testValidateAuthorizationRequestInvalidClientId(): void { $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn(null); @@ -155,13 +155,13 @@ public function testValidateAuthorizationRequestInvalidClientId() 'client_id' => 'foo', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - public function testValidateAuthorizationRequestBadRedirectUriString() + public function testValidateAuthorizationRequestBadRedirectUriString(): void { $client = new ClientEntity(); $client->setRedirectUri(self::REDIRECT_URI); @@ -177,13 +177,13 @@ 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); } - public function testValidateAuthorizationRequestBadRedirectUriArray() + public function testValidateAuthorizationRequestBadRedirectUriArray(): void { $client = new ClientEntity(); $client->setRedirectUri([self::REDIRECT_URI]); @@ -199,17 +199,17 @@ public function testValidateAuthorizationRequestBadRedirectUriArray() 'redirect_uri' => 'http://bar', ]); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(4); $grant->validateAuthorizationRequest($request); } - public function testCompleteAuthorizationRequest() + public function testCompleteAuthorizationRequest(): void { $client = new ClientEntity(); $client->setIdentifier('identifier'); - $client->setRedirectUri(self::REDIRECT_URI); + $client->setRedirectUri('https://foo/bar'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -227,18 +227,19 @@ public function testCompleteAuthorizationRequest() $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); + self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - public function testCompleteAuthorizationRequestDenied() + public function testCompleteAuthorizationRequestDenied(): void { $client = new ClientEntity(); - $client->setRedirectUri(self::REDIRECT_URI); + $client->setIdentifier('clientId'); + $client->setRedirectUri('https://foo/bar'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(false); @@ -253,22 +254,22 @@ public function testCompleteAuthorizationRequestDenied() $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(9); $grant->completeAuthorizationRequest($authRequest); } - public function testAccessTokenRepositoryUniqueConstraintCheck() + public function testAccessTokenRepositoryUniqueConstraintCheck(): void { $client = new ClientEntity(); - $client->setIdentifier('identifier'); - $client->setRedirectUri(self::REDIRECT_URI); + $client->setIdentifier('clientId'); + $client->setRedirectUri('https://foo/bar'); $authRequest = new AuthorizationRequest(); $authRequest->setAuthorizationApproved(true); @@ -283,12 +284,12 @@ public function testAccessTokenRepositoryUniqueConstraintCheck() $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn($accessToken); - $matcher = $this->exactly(2); + $matcher = self::exactly(2); $accessTokenRepositoryMock ->expects($matcher) ->method('persistNewAccessToken') - ->willReturnCallback(function () use ($matcher) { + ->willReturnCallback(function () use ($matcher): void { if ($matcher->getInvocationCount() === 1) { throw UniqueTokenIdentifierConstraintViolationException::create(); } @@ -297,19 +298,24 @@ public function testAccessTokenRepositoryUniqueConstraintCheck() $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); + self::assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } - public function testAccessTokenRepositoryFailToPersist() + public function testAccessTokenRepositoryFailToPersist(): void { + $client = new ClientEntity(); + + $client->setRedirectUri('https://foo/bar'); + $authRequest = new AuthorizationRequest(); + $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); @@ -321,22 +327,27 @@ public function testAccessTokenRepositoryFailToPersist() $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(7); $grant->completeAuthorizationRequest($authRequest); } - public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop() + public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop(): void { + $client = new ClientEntity(); + + $client->setRedirectUri('https://foo/bar'); + $authRequest = new AuthorizationRequest(); + $authRequest->setAuthorizationApproved(true); - $authRequest->setClient(new ClientEntity()); + $authRequest->setClient($client); $authRequest->setGrantTypeId('authorization_code'); $authRequest->setUser(new UserEntity()); @@ -348,42 +359,42 @@ public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop() $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); - $grant = new ImplicitGrant(new \DateInterval('PT10M')); + $grant = new ImplicitGrant(new DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $this->expectException(\League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException::class); + $this->expectException(UniqueTokenIdentifierConstraintViolationException::class); $this->expectExceptionCode(100); $grant->completeAuthorizationRequest($authRequest); } - public function testSetRefreshTokenTTL() + public function testSetRefreshTokenTTL(): void { $grant = new ImplicitGrant(new DateInterval('PT10M')); - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $grant->setRefreshTokenTTL(new DateInterval('PT10M')); } - public function testSetRefreshTokenRepository() + public function testSetRefreshTokenRepository(): void { $grant = new ImplicitGrant(new DateInterval('PT10M')); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); } - public function testCompleteAuthorizationRequestNoUser() + public function testCompleteAuthorizationRequestNoUser(): void { $grant = new ImplicitGrant(new DateInterval('PT10M')); - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $grant->completeAuthorizationRequest(new AuthorizationRequest()); } diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index b53ab2357..8c60a8c78 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -1,12 +1,14 @@ getMockBuilder(UserRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); - $this->assertEquals('password', $grant->getIdentifier()); + self::assertEquals('password', $grant->getIdentifier()); } - public function testRespondToRequest() + public function testRespondToRequest(): void { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); @@ -76,17 +79,17 @@ public function testRespondToRequest() $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } - public function testRespondToRequestNullRefreshToken() + public function testRespondToRequestNullRefreshToken(): void { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); @@ -119,13 +122,12 @@ public function testRespondToRequestNullRefreshToken() ]); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); - $this->assertNull($responseType->getRefreshToken()); + self::assertNull($responseType->getRefreshToken()); } - public function testRespondToRequestMissingUsername() + public function testRespondToRequestMissingUsername(): void { $client = new ClientEntity(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -148,12 +150,12 @@ public function testRespondToRequestMissingUsername() $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestMissingPassword() + public function testRespondToRequestMissingPassword(): void { $client = new ClientEntity(); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -177,18 +179,19 @@ public function testRespondToRequestMissingPassword() $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestBadCredentials() + public function testRespondToRequestBadCredentials(): void { $client = new ClientEntity(); $client->setRedirectUri('http://foo/bar'); $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); @@ -197,9 +200,14 @@ public function testRespondToRequestBadCredentials() $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); + $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(new ScopeEntity()); + $grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); + $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setScopeRepository($scopeRepositoryMock); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', @@ -210,7 +218,7 @@ public function testRespondToRequestBadCredentials() $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(6); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index ef14cabe7..747ba5ead 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -1,12 +1,14 @@ cryptStub = new CryptTraitStub(); } - public function testGetIdentifier() + public function testGetIdentifier(): void { $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); - $this->assertEquals('refresh_token', $grant->getIdentifier()); + self::assertEquals('refresh_token', $grant->getIdentifier()); } - public function testRespondToRequest() + public function testRespondToRequest(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -49,6 +50,7 @@ public function testRespondToRequest() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeEntity = new ScopeEntity(); $scopeEntity->setIdentifier('foo'); @@ -58,11 +60,11 @@ public function testRespondToRequest() $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); + $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity()); - $refreshTokenRepositoryMock->expects($this->once())->method('persistNewRefreshToken')->willReturnSelf(); + $refreshTokenRepositoryMock->expects(self::once())->method('persistNewRefreshToken')->willReturnSelf(); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); @@ -72,34 +74,39 @@ public function testRespondToRequest() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->revokeRefreshTokens(true); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, 'scopes' => ['foo'], ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } - public function testRespondToRequestNullRefreshToken() + public function testRespondToRequestNullRefreshToken(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -107,6 +114,7 @@ public function testRespondToRequestNullRefreshToken() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeEntity = new ScopeEntity(); $scopeEntity->setIdentifier('foo'); @@ -117,11 +125,11 @@ public function testRespondToRequestNullRefreshToken() $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); + $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null); - $refreshTokenRepositoryMock->expects($this->never())->method('persistNewRefreshToken'); + $refreshTokenRepositoryMock->expects(self::never())->method('persistNewRefreshToken'); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); @@ -130,34 +138,39 @@ public function testRespondToRequestNullRefreshToken() $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, 'scopes' => ['foo'], ]); $responseType = new StubResponseType(); - $grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M')); + $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); - $this->assertNull($responseType->getRefreshToken()); + self::assertNull($responseType->getRefreshToken()); } - public function testRespondToReducedScopes() + public function testRespondToReducedScopes(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -165,6 +178,7 @@ public function testRespondToReducedScopes() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); @@ -188,34 +202,39 @@ public function testRespondToReducedScopes() $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grant->revokeRefreshTokens(true); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo', 'bar'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, 'scope' => 'foo', ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); - $this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken()); - $this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); + self::assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken()); } - public function testRespondToUnexpectedScope() + public function testRespondToUnexpectedScope(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -223,6 +242,7 @@ public function testRespondToUnexpectedScope() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -242,35 +262,41 @@ public function testRespondToUnexpectedScope() $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo', 'bar'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, 'scope' => 'foobar', ]); $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(5); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestMissingOldToken() + public function testRespondToRequestMissingOldToken(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -278,6 +304,7 @@ public function testRespondToRequestMissingOldToken() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -295,13 +322,13 @@ public function testRespondToRequestMissingOldToken() $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(3); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestInvalidOldToken() + public function testRespondToRequestInvalidOldToken(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -309,6 +336,7 @@ public function testRespondToRequestInvalidOldToken() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -329,13 +357,13 @@ public function testRespondToRequestInvalidOldToken() $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(8); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestClientMismatch() + public function testRespondToRequestClientMismatch(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -343,6 +371,7 @@ public function testRespondToRequestClientMismatch() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); @@ -356,34 +385,40 @@ public function testRespondToRequestClientMismatch() $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'bar', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'bar', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, ]); $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(8); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestExpiredToken() + public function testRespondToRequestExpiredToken(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -391,6 +426,7 @@ public function testRespondToRequestExpiredToken() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -401,34 +437,40 @@ public function testRespondToRequestExpiredToken() $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() - 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() - 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, ]); $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(8); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestRevokedToken() + public function testRespondToRequestRevokedToken(): void { $client = new ClientEntity(); $client->setIdentifier('foo'); @@ -436,6 +478,7 @@ public function testRespondToRequestRevokedToken() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); @@ -447,34 +490,40 @@ public function testRespondToRequestRevokedToken() $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, ]); $responseType = new StubResponseType(); - $this->expectException(\League\OAuth2\Server\Exception\OAuthServerException::class); + $this->expectException(OAuthServerException::class); $this->expectExceptionCode(8); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRespondToRequestFinalizeScopes() + public function testRespondToRequestFinalizeScopes(): void { $client = new ClientEntity(); @@ -483,6 +532,7 @@ public function testRespondToRequestFinalizeScopes() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $fooScopeEntity = new ScopeEntity(); $fooScopeEntity->setIdentifier('foo'); @@ -512,7 +562,7 @@ public function testRespondToRequestFinalizeScopes() $finalizedScopes = [$fooScopeEntity]; $scopeRepositoryMock - ->expects($this->once()) + ->expects(self::once()) ->method('finalizeScopes') ->with($scopes, $grant->getIdentifier(), $client) ->willReturn($finalizedScopes); @@ -522,32 +572,38 @@ public function testRespondToRequestFinalizeScopes() ->with($client, $finalizedScopes) ->willReturn(new AccessTokenEntity()); - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => 'zyxwvu', - 'access_token_id' => 'abcdef', - 'scopes' => ['foo', 'bar'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => 'zyxwvu', + 'access_token_id' => 'abcdef', + 'scopes' => ['foo', 'bar'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, 'scope' => ['foo', 'bar'], - ]); + ]); $responseType = new StubResponseType(); $grant->respondToAccessTokenRequest($serverRequest, $responseType, new DateInterval('PT5M')); } - public function testRevokedRefreshToken() + public function testRevokedRefreshToken(): void { $refreshTokenId = 'foo'; @@ -557,39 +613,47 @@ public function testRevokedRefreshToken() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeEntity = new ScopeEntity(); $scopeEntity->setIdentifier('foo'); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); + $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); + $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('isRefreshTokenRevoked') - ->will($this->onConsecutiveCalls(false, true)); - $refreshTokenRepositoryMock->expects($this->once())->method('revokeRefreshToken')->with($this->equalTo($refreshTokenId)); - - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => $refreshTokenId, - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + ->will(self::onConsecutiveCalls(false, true)); + $refreshTokenRepositoryMock->expects(self::once())->method('revokeRefreshToken')->with(self::equalTo($refreshTokenId)); + + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => $refreshTokenId, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, 'scope' => ['foo'], ]); @@ -602,10 +666,10 @@ public function testRevokedRefreshToken() $grant->revokeRefreshTokens(true); $grant->respondToAccessTokenRequest($serverRequest, new StubResponseType(), new DateInterval('PT5M')); - Assert::assertTrue($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId)); + self::assertTrue($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId)); } - public function testUnrevokedRefreshToken() + public function testUnrevokedRefreshToken(): void { $refreshTokenId = 'foo'; @@ -615,38 +679,46 @@ public function testUnrevokedRefreshToken() $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepositoryMock->method('getClientEntity')->willReturn($client); + $clientRepositoryMock->method('validateClient')->willReturn(true); $scopeEntity = new ScopeEntity(); $scopeEntity->setIdentifier('foo'); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); + $scopeRepositoryMock->method('finalizeScopes')->willReturn([$scopeEntity]); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity()); - $accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf(); + $accessTokenRepositoryMock->expects(self::once())->method('persistNewAccessToken')->willReturnSelf(); $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('isRefreshTokenRevoked')->willReturn(false); - $refreshTokenRepositoryMock->expects($this->never())->method('revokeRefreshToken'); - - $oldRefreshToken = $this->cryptStub->doEncrypt( - \json_encode( - [ - 'client_id' => 'foo', - 'refresh_token_id' => $refreshTokenId, - 'access_token_id' => 'abcdef', - 'scopes' => ['foo'], - 'user_id' => 123, - 'expire_time' => \time() + 3600, - ] - ) + $refreshTokenRepositoryMock->expects(self::never())->method('revokeRefreshToken'); + + $oldRefreshToken = json_encode( + [ + 'client_id' => 'foo', + 'refresh_token_id' => $refreshTokenId, + 'access_token_id' => 'abcdef', + 'scopes' => ['foo'], + 'user_id' => 123, + 'expire_time' => time() + 3600, + ] + ); + + if ($oldRefreshToken === false) { + self::fail('json_encode failed'); + } + + $encryptedOldRefreshToken = $this->cryptStub->doEncrypt( + $oldRefreshToken ); $serverRequest = (new ServerRequest())->withParsedBody([ 'client_id' => 'foo', 'client_secret' => 'bar', - 'refresh_token' => $oldRefreshToken, + 'refresh_token' => $encryptedOldRefreshToken, 'scope' => ['foo'], ]); @@ -656,8 +728,10 @@ public function testUnrevokedRefreshToken() $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->revokeRefreshTokens(false); + $grant->respondToAccessTokenRequest($serverRequest, new StubResponseType(), new DateInterval('PT5M')); - Assert::assertFalse($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId)); + self::assertFalse($refreshTokenRepositoryMock->isRefreshTokenRevoked($refreshTokenId)); } } diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index 69c8d3791..814e96a6c 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -1,5 +1,7 @@ setConfidential(); @@ -30,8 +36,9 @@ public function testValidResponse() $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('getClientEntity')->willReturn($client); + $clientRepository->method('validateClient')->willReturn(true); - $scopeEntity = new ScopeEntity; + $scopeEntity = new ScopeEntity(); $scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(); $scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity); $scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0); @@ -44,7 +51,7 @@ public function testValidResponse() $accessRepositoryMock, $scopeRepositoryMock, 'file://' . __DIR__ . '/../Stubs/private.key', - \base64_encode(\random_bytes(36)), + base64_encode(random_bytes(36)), new StubResponseType() ); @@ -62,13 +69,13 @@ public function testValidResponse() $request, new Response(), function () { - return \func_get_args()[1]; + return func_get_args()[1]; } ); - $this->assertEquals(200, $response->getStatusCode()); + self::assertEquals(200, $response->getStatusCode()); } - public function testOAuthErrorResponse() + public function testOAuthErrorResponse(): void { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository->method('validateClient')->willReturn(false); @@ -78,7 +85,7 @@ public function testOAuthErrorResponse() $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/../Stubs/private.key', - \base64_encode(\random_bytes(36)), + base64_encode(random_bytes(36)), new StubResponseType() ); @@ -96,33 +103,33 @@ public function testOAuthErrorResponse() $request, new Response(), function () { - return \func_get_args()[1]; + return func_get_args()[1]; } ); - $this->assertEquals(401, $response->getStatusCode()); + self::assertEquals(401, $response->getStatusCode()); } - public function testOAuthErrorResponseRedirectUri() + public function testOAuthErrorResponseRedirectUri(): void { $exception = OAuthServerException::invalidScope('test', 'http://foo/bar'); $response = $exception->generateHttpResponse(new Response()); - $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals( - 'http://foo/bar?error=invalid_scope&error_description=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed', + self::assertEquals(302, $response->getStatusCode()); + self::assertEquals( + 'http://foo/bar?error=invalid_scope&error_description=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0] ); } - public function testOAuthErrorResponseRedirectUriFragment() + public function testOAuthErrorResponseRedirectUriFragment(): void { $exception = OAuthServerException::invalidScope('test', 'http://foo/bar'); $response = $exception->generateHttpResponse(new Response(), true); - $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals( - 'http://foo/bar#error=invalid_scope&error_description=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed', + self::assertEquals(302, $response->getStatusCode()); + self::assertEquals( + 'http://foo/bar#error=invalid_scope&error_description=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0] ); } diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index 106111885..119d43ae0 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -1,5 +1,7 @@ getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), @@ -35,23 +40,23 @@ public function testValidResponse() $token = (string) $accessToken; - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $token)); $middleware = new ResourceServerMiddleware($server); $response = $middleware->__invoke( $request, new Response(), function () { - $this->assertEquals('test', \func_get_args()[0]->getAttribute('oauth_access_token_id')); + self::assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id')); - return \func_get_args()[1]; + return func_get_args()[1]; } ); - $this->assertEquals(200, $response->getStatusCode()); + self::assertEquals(200, $response->getStatusCode()); } - public function testValidResponseExpiredToken() + public function testValidResponseExpiredToken(): void { $server = new ResourceServer( $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), @@ -70,23 +75,23 @@ public function testValidResponseExpiredToken() $token = (string) $accessToken; - $request = (new ServerRequest())->withHeader('authorization', \sprintf('Bearer %s', $token)); + $request = (new ServerRequest())->withHeader('authorization', sprintf('Bearer %s', $token)); $middleware = new ResourceServerMiddleware($server); $response = $middleware->__invoke( $request, new Response(), function () { - $this->assertEquals('test', \func_get_args()[0]->getAttribute('oauth_access_token_id')); + self::assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id')); - return \func_get_args()[1]; + return func_get_args()[1]; } ); - $this->assertEquals(401, $response->getStatusCode()); + self::assertEquals(401, $response->getStatusCode()); } - public function testErrorResponse() + public function testErrorResponse(): void { $server = new ResourceServer( $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), @@ -100,10 +105,10 @@ public function testErrorResponse() $request, new Response(), function () { - return \func_get_args()[1]; + return func_get_args()[1]; } ); - $this->assertEquals(401, $response->getStatusCode()); + self::assertEquals(401, $response->getStatusCode()); } } diff --git a/tests/PHPStan/AbstractGrantExtension.php b/tests/PHPStan/AbstractGrantExtension.php index da65ab961..f23062d5b 100644 --- a/tests/PHPStan/AbstractGrantExtension.php +++ b/tests/PHPStan/AbstractGrantExtension.php @@ -1,5 +1,6 @@ getName(), [ + return in_array($methodReflection->getName(), [ 'getRequestParameter', 'getQueryStringParameter', 'getCookieParameter', @@ -33,7 +36,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method { return TypeCombinator::union(...[ new StringType(), - isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new NullType(), + isset($methodCall->getArgs[2]) ? $scope->getType($methodCall->getArgs[2]->value) : new NullType(), ]); } } diff --git a/tests/RedirectUriValidators/RedirectUriValidatorTest.php b/tests/RedirectUriValidators/RedirectUriValidatorTest.php index 2f297c264..c648d7ab1 100644 --- a/tests/RedirectUriValidators/RedirectUriValidatorTest.php +++ b/tests/RedirectUriValidators/RedirectUriValidatorTest.php @@ -1,5 +1,7 @@ assertFalse( + self::assertFalse( $validator->validateRedirectUri($invalidRedirectUri), 'Non loopback URI must match in every part' ); } - public function testValidNonLoopbackUri() + public function testValidNonLoopbackUri(): void { $validator = new RedirectUriValidator([ 'https://example.com:8443/endpoint', @@ -31,73 +33,73 @@ public function testValidNonLoopbackUri() $validRedirectUri = 'https://example.com:8443/endpoint'; - $this->assertTrue( + self::assertTrue( $validator->validateRedirectUri($validRedirectUri), 'Redirect URI must be valid when matching in every part' ); } - public function testInvalidLoopbackUri() + public function testInvalidLoopbackUri(): void { $validator = new RedirectUriValidator('http://127.0.0.1:8443/endpoint'); $invalidRedirectUri = 'http://127.0.0.1:8443/different/endpoint'; - $this->assertFalse( + self::assertFalse( $validator->validateRedirectUri($invalidRedirectUri), 'Valid loopback redirect URI can change only the port number' ); } - public function testValidLoopbackUri() + public function testValidLoopbackUri(): void { $validator = new RedirectUriValidator('http://127.0.0.1:8443/endpoint'); $validRedirectUri = 'http://127.0.0.1:8080/endpoint'; - $this->assertTrue( + self::assertTrue( $validator->validateRedirectUri($validRedirectUri), 'Loopback redirect URI can change the port number' ); } - public function testValidIpv6LoopbackUri() + public function testValidIpv6LoopbackUri(): void { $validator = new RedirectUriValidator('http://[::1]:8443/endpoint'); $validRedirectUri = 'http://[::1]:8080/endpoint'; - $this->assertTrue( + self::assertTrue( $validator->validateRedirectUri($validRedirectUri), 'Loopback redirect URI can change the port number' ); } - public function testCanValidateUrn() + public function testCanValidateUrn(): void { $validator = new RedirectUriValidator('urn:ietf:wg:oauth:2.0:oob'); - $this->assertTrue( + self::assertTrue( $validator->validateRedirectUri('urn:ietf:wg:oauth:2.0:oob'), 'An invalid pre-registered urn was provided' ); } - public function canValidateCustomSchemeHost() + public function canValidateCustomSchemeHost(): void { $validator = new RedirectUriValidator('msal://redirect'); - $this->assertTrue( + self::assertTrue( $validator->validateRedirectUri('msal://redirect'), 'An invalid, pre-registered, custom scheme uri was provided' ); } - public function canValidateCustomSchemePath() + public function canValidateCustomSchemePath(): void { $validator = new RedirectUriValidator('com.example.app:/oauth2redirect/example-provider'); - $this->assertTrue( + self::assertTrue( $validator->validateRedirectUri('com.example.app:/oauth2redirect/example-provider'), 'An invalid, pre-registered, custom scheme uri was provided' ); diff --git a/tests/ResourceServerTest.php b/tests/ResourceServerTest.php index 7281070e2..41ac2e854 100644 --- a/tests/ResourceServerTest.php +++ b/tests/ResourceServerTest.php @@ -1,5 +1,6 @@ getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), @@ -21,7 +22,7 @@ public function testValidateAuthenticatedRequest() try { $server->validateAuthenticatedRequest(ServerRequestFactory::fromGlobals()); } catch (OAuthServerException $e) { - $this->assertEquals('Missing "Authorization" header', $e->getHint()); + self::assertEquals('Missing "Authorization" header', $e->getHint()); } } } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index da7242651..64c040f57 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -1,5 +1,7 @@ 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'); @@ -49,25 +55,24 @@ 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() + 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'); @@ -92,28 +97,27 @@ public function testGenerateHttpResponseWithExtraParams() $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); - - $this->assertObjectHasAttribute('foo', $json); - $this->assertEquals('bar', $json->foo); + $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() + 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'); @@ -134,7 +138,7 @@ public function testDetermineAccessTokenInHeaderValidToken() $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); @@ -142,23 +146,23 @@ public function testDetermineAccessTokenInHeaderValidToken() $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); - $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')); + 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() + 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))); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -179,28 +183,28 @@ public function testDetermineAccessTokenInHeaderInvalidJWT() $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); } catch (OAuthServerException $e) { - $this->assertEquals( + self::assertEquals( 'Access token could not be verified', $e->getHint() ); } } - public function testDetermineAccessTokenInHeaderRevokedToken() + 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'); @@ -221,7 +225,7 @@ public function testDetermineAccessTokenInHeaderRevokedToken() $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); @@ -229,23 +233,23 @@ public function testDetermineAccessTokenInHeaderRevokedToken() $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); } catch (OAuthServerException $e) { - $this->assertEquals( + self::assertEquals( 'Access token has been revoked', $e->getHint() ); } } - public function testDetermineAccessTokenInHeaderInvalidToken() + 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(); @@ -257,18 +261,18 @@ public function testDetermineAccessTokenInHeaderInvalidToken() try { $authorizationValidator->validateAuthorization($request); } catch (OAuthServerException $e) { - $this->assertEquals( + self::assertEquals( 'The JWT string must have two dots', $e->getHint() ); } } - public function testDetermineMissingBearerInHeader() + 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(); @@ -280,7 +284,7 @@ public function testDetermineMissingBearerInHeader() try { $authorizationValidator->validateAuthorization($request); } catch (OAuthServerException $e) { - $this->assertEquals( + self::assertEquals( 'Error while decoding from JSON', $e->getHint() ); diff --git a/tests/ResponseTypes/BearerTokenResponseWithParams.php b/tests/ResponseTypes/BearerTokenResponseWithParams.php index 4ba09f4b9..2be1a3272 100644 --- a/tests/ResponseTypes/BearerTokenResponseWithParams.php +++ b/tests/ResponseTypes/BearerTokenResponseWithParams.php @@ -1,5 +1,7 @@ + */ + protected function getExtraParams(AccessTokenEntityInterface $accessToken): array { return ['foo' => 'bar', 'token_type' => 'Should not overwrite']; } diff --git a/tests/Stubs/AccessTokenEntity.php b/tests/Stubs/AccessTokenEntity.php index 77a4d2231..339b29c8b 100644 --- a/tests/Stubs/AccessTokenEntity.php +++ b/tests/Stubs/AccessTokenEntity.php @@ -1,5 +1,7 @@ redirectUri = $uri; } - public function setConfidential() + public function setConfidential(): void { $this->isConfidential = true; } diff --git a/tests/Stubs/CryptTraitStub.php b/tests/Stubs/CryptTraitStub.php index d4d8f1c68..19de09008 100644 --- a/tests/Stubs/CryptTraitStub.php +++ b/tests/Stubs/CryptTraitStub.php @@ -1,29 +1,35 @@ setEncryptionKey(\base64_encode(\random_bytes(36))); + $this->setEncryptionKey(base64_encode(random_bytes(36))); } - public function getKey() + public function getKey(): string|Key|null { return $this->encryptionKey; } - public function doEncrypt($unencryptedData) + public function doEncrypt(string $unencryptedData): string { return $this->encrypt($unencryptedData); } - public function doDecrypt($encryptedData) + public function doDecrypt(string $encryptedData): string { return $this->decrypt($encryptedData); } diff --git a/tests/Stubs/GrantType.php b/tests/Stubs/GrantType.php index 5270a47a0..bd2f95f77 100644 --- a/tests/Stubs/GrantType.php +++ b/tests/Stubs/GrantType.php @@ -5,37 +5,40 @@ namespace LeagueTests\Stubs; use DateInterval; +use Defuse\Crypto\Key; use League\Event\EmitterInterface; -use League\OAuth2\Server\CryptKey; +use League\OAuth2\Server\CryptKeyInterface; use League\OAuth2\Server\Grant\GrantTypeInterface; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; use League\OAuth2\Server\Repositories\ClientRepositoryInterface; use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface; +use League\OAuth2\Server\ResponseTypes\BearerTokenResponse; use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; use Psr\Http\Message\ServerRequestInterface; final class GrantType implements GrantTypeInterface { - private $emitter; + private ?EmitterInterface $emitter; - public function setEmitter(EmitterInterface $emitter = null) + public function setEmitter(EmitterInterface $emitter = null): self { $this->emitter = $emitter; return $this; } - public function getEmitter() + public function getEmitter(): ?EmitterInterface { return $this->emitter; } - public function setRefreshTokenTTL(DateInterval $refreshTokenTTL) + public function setRefreshTokenTTL(DateInterval $refreshTokenTTL): void { } - public function getIdentifier() + public function getIdentifier(): string { return 'grant_type_identifier'; } @@ -44,16 +47,16 @@ public function respondToAccessTokenRequest( ServerRequestInterface $request, ResponseTypeInterface $responseType, DateInterval $accessTokenTTL - ) { + ): ResponseTypeInterface { return $responseType; } - public function canRespondToAuthorizationRequest(ServerRequestInterface $request) + public function canRespondToAuthorizationRequest(ServerRequestInterface $request): bool { return true; } - public function validateAuthorizationRequest(ServerRequestInterface $request) + public function validateAuthorizationRequest(ServerRequestInterface $request): AuthorizationRequest { $authRequest = new AuthorizationRequest(); $authRequest->setGrantTypeId(self::class); @@ -61,36 +64,41 @@ public function validateAuthorizationRequest(ServerRequestInterface $request) return $authRequest; } - public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest) + public function completeAuthorizationRequest(AuthorizationRequestInterface $authorizationRequest): BearerTokenResponse { + return new BearerTokenResponse(); } - public function canRespondToAccessTokenRequest(ServerRequestInterface $request) + public function canRespondToAccessTokenRequest(ServerRequestInterface $request): bool { return true; } - public function setClientRepository(ClientRepositoryInterface $clientRepository) + public function setClientRepository(ClientRepositoryInterface $clientRepository): void { } - public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository) + public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository): void { } - public function setScopeRepository(ScopeRepositoryInterface $scopeRepository) + public function setScopeRepository(ScopeRepositoryInterface $scopeRepository): void { } - public function setDefaultScope($scope) + public function setDefaultScope(string $scope): void { } - public function setPrivateKey(CryptKey $privateKey) + public function setPrivateKey(CryptKeyInterface $privateKey): void { } - public function setEncryptionKey($key = null) + public function setEncryptionKey(Key|string|null $key = null): void + { + } + + public function revokeRefreshTokens(bool $willRevoke): void { } } diff --git a/tests/Stubs/RefreshTokenEntity.php b/tests/Stubs/RefreshTokenEntity.php index f145b7065..9d6d79f27 100644 --- a/tests/Stubs/RefreshTokenEntity.php +++ b/tests/Stubs/RefreshTokenEntity.php @@ -1,5 +1,7 @@ getIdentifier(); } diff --git a/tests/Stubs/StubResponseType.php b/tests/Stubs/StubResponseType.php index 7aa71f67e..0f8f371a1 100644 --- a/tests/Stubs/StubResponseType.php +++ b/tests/Stubs/StubResponseType.php @@ -1,5 +1,7 @@ accessToken; } - public function getRefreshToken() + public function getRefreshToken(): RefreshTokenEntityInterface|null { - return $this->refreshToken; + return $this->refreshToken ?? null; } - /** - * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken - */ - public function setAccessToken(AccessTokenEntityInterface $accessToken) + public function setAccessToken(AccessTokenEntityInterface $accessToken): void { $this->accessToken = $accessToken; } - /** - * @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken - */ - public function setRefreshToken(RefreshTokenEntityInterface $refreshToken) + public function setRefreshToken(RefreshTokenEntityInterface $refreshToken): void { $this->refreshToken = $refreshToken; } /** - * @param ServerRequestInterface $request - * * @throws \League\OAuth2\Server\Exception\OAuthServerException - * - * @return \Psr\Http\Message\ServerRequestInterface */ - public function validateAccessToken(ServerRequestInterface $request) + public function validateAccessToken(ServerRequestInterface $request): ServerRequestInterface { if ($request->getHeader('authorization')[0] === 'Basic test') { return $request->withAttribute('oauth_access_token_id', 'test'); @@ -58,12 +46,7 @@ public function validateAccessToken(ServerRequestInterface $request) throw OAuthServerException::accessDenied(); } - /** - * @param ResponseInterface $response - * - * @return ResponseInterface - */ - public function generateHttpResponse(ResponseInterface $response) + public function generateHttpResponse(ResponseInterface $response): ResponseInterface { return new Response(); } diff --git a/tests/Stubs/UserEntity.php b/tests/Stubs/UserEntity.php index 6bbdc8c41..4e0fa0123 100644 --- a/tests/Stubs/UserEntity.php +++ b/tests/Stubs/UserEntity.php @@ -1,5 +1,7 @@ setIdentifier(123); + $this->setIdentifier('123'); } } diff --git a/tests/Utils/CryptKeyTest.php b/tests/Utils/CryptKeyTest.php index 8edadb8b0..9ec87cb1c 100644 --- a/tests/Utils/CryptKeyTest.php +++ b/tests/Utils/CryptKeyTest.php @@ -1,138 +1,152 @@ expectException(\LogicException::class); + $this->expectException(LogicException::class); new CryptKey('undefined file'); } - public function testKeyCreation() + public function testKeyCreation(): void { $keyFile = __DIR__ . '/../Stubs/public.key'; $key = new CryptKey($keyFile, 'secret'); - $this->assertEquals('file://' . $keyFile, $key->getKeyPath()); - $this->assertEquals('secret', $key->getPassPhrase()); + self::assertEquals('file://' . $keyFile, $key->getKeyPath()); + self::assertEquals('secret', $key->getPassPhrase()); } - public function testKeyString() + public function testKeyString(): void { - $keyContent = \file_get_contents(__DIR__ . '/../Stubs/public.key'); + $keyContent = file_get_contents(__DIR__ . '/../Stubs/public.key'); - if (!\is_string($keyContent)) { - $this->fail('The public key stub is not a string'); + if (!is_string($keyContent)) { + self::fail('The public key stub is not a string'); } $key = new CryptKey($keyContent); - $this->assertEquals( + self::assertEquals( $keyContent, $key->getKeyContents() ); - $keyContent = \file_get_contents(__DIR__ . '/../Stubs/private.key.crlf'); + $keyContent = file_get_contents(__DIR__ . '/../Stubs/private.key.crlf'); - if (!\is_string($keyContent)) { - $this->fail('The private key (crlf) stub is not a string'); + if (!is_string($keyContent)) { + self::fail('The private key (crlf) stub is not a string'); } $key = new CryptKey($keyContent); - $this->assertEquals( + self::assertEquals( $keyContent, $key->getKeyContents() ); } - public function testUnsupportedKeyType() + public function testUnsupportedKeyType(): void { - $this->expectException(\LogicException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage('Invalid key supplied'); try { // Create the keypair - $res = \openssl_pkey_new([ + $res = openssl_pkey_new([ 'digest_alg' => 'sha512', 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_DSA, ]); + + if ($res === false) { + self::fail('The keypair was not created'); + } + // Get private key - \openssl_pkey_export($res, $keyContent, 'mystrongpassword'); + openssl_pkey_export($res, $keyContent, 'mystrongpassword'); $path = self::generateKeyPath($keyContent); new CryptKey($keyContent, 'mystrongpassword'); } finally { if (isset($path)) { - @\unlink($path); + @unlink($path); } } } - public function testECKeyType() + public function testECKeyType(): void { try { // Create the keypair - $res = \openssl_pkey_new([ + $res = openssl_pkey_new([ 'digest_alg' => 'sha512', 'curve_name' => 'prime256v1', 'private_key_type' => OPENSSL_KEYTYPE_EC, ]); + + if ($res === false) { + self::fail('The keypair was not created'); + } + // Get private key - \openssl_pkey_export($res, $keyContent, 'mystrongpassword'); + openssl_pkey_export($res, $keyContent, 'mystrongpassword'); $key = new CryptKey($keyContent, 'mystrongpassword'); - $this->assertEquals('', $key->getKeyPath()); - $this->assertEquals('mystrongpassword', $key->getPassPhrase()); - } catch (\Throwable $e) { - $this->fail('The EC key was not created'); - } finally { - if (isset($path)) { - @\unlink($path); - } + self::assertEquals('', $key->getKeyPath()); + self::assertEquals('mystrongpassword', $key->getPassPhrase()); + } catch (Throwable $e) { + self::fail('The EC key was not created'); } } - public function testRSAKeyType() + public function testRSAKeyType(): void { try { // Create the keypair - $res = \openssl_pkey_new([ + $res = openssl_pkey_new([ 'digest_alg' => 'sha512', 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA, ]); + + if ($res === false) { + self::fail('The keypair was not created'); + } + // Get private key - \openssl_pkey_export($res, $keyContent, 'mystrongpassword'); + openssl_pkey_export($res, $keyContent, 'mystrongpassword'); $key = new CryptKey($keyContent, 'mystrongpassword'); - $this->assertEquals('', $key->getKeyPath()); - $this->assertEquals('mystrongpassword', $key->getPassPhrase()); - } catch (\Throwable $e) { - $this->fail('The RSA key was not created'); - } finally { - if (isset($path)) { - @\unlink($path); - } + self::assertEquals('', $key->getKeyPath()); + self::assertEquals('mystrongpassword', $key->getPassPhrase()); + } catch (Throwable $e) { + self::fail('The RSA key was not created'); } } - /** - * @param string $keyContent - * - * @return string - */ - private static function generateKeyPath($keyContent) + private static function generateKeyPath(string $keyContent): string { - return 'file://' . \sys_get_temp_dir() . '/' . \sha1($keyContent) . '.key'; + return 'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key'; } } diff --git a/tests/Utils/CryptTraitTest.php b/tests/Utils/CryptTraitTest.php index a07d6d50c..b49b0e9e2 100644 --- a/tests/Utils/CryptTraitTest.php +++ b/tests/Utils/CryptTraitTest.php @@ -1,41 +1,46 @@ cryptStub = new CryptTraitStub(); } - public function testEncryptDecryptWithPassword() + public function testEncryptDecryptWithPassword(): void { - $this->cryptStub->setEncryptionKey(\base64_encode(\random_bytes(36))); + $this->cryptStub->setEncryptionKey(base64_encode(random_bytes(36))); $this->encryptDecrypt(); } - public function testEncryptDecryptWithKey() + public function testEncryptDecryptWithKey(): void { $this->cryptStub->setEncryptionKey(Key::createNewRandomKey()); $this->encryptDecrypt(); } - private function encryptDecrypt() + private function encryptDecrypt(): void { $payload = 'alex loves whisky'; $encrypted = $this->cryptStub->doEncrypt($payload); $plainText = $this->cryptStub->doDecrypt($encrypted); - $this->assertNotEquals($payload, $encrypted); - $this->assertEquals($payload, $plainText); + self::assertNotEquals($payload, $encrypted); + self::assertEquals($payload, $plainText); } }