diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..fe13e48b8 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,74 @@ +name: Test Suite +on: + push: + branches: + - main + pull_request: +jobs: + test: + runs-on: ubuntu-latest + services: + redis: + image: redis + ports: + - 6379:6379 + options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 + mongodb: + image: mongo + ports: + - 27017:27017 + myriadb: + image: mariadb + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3808:3808 + - 3306:3306 + postgres: + image: postgres + env: + POSTGRES_DB: oauth2_server_php + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: --health-cmd="pg_isready -h localhost" --health-interval=10s --health-timeout=5s --health-retries=5 + strategy: + matrix: + php: [ 7.2, 7.3, 7.4, "8.0", 8.1, 8.2 ] + name: "PHP ${{ matrix.php }} Unit Test" + steps: + - uses: actions/checkout@v2 + - uses: codecov/codecov-action@v1 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mongodb, mbstring, intl, redis, pdo_mysql + - name: Install composer dependencies + uses: nick-invision/retry@v1 + with: + timeout_minutes: 10 + max_attempts: 3 + command: composer install + - name: Run PHPUnit + run: vendor/bin/phpunit -v + phpstan: + name: "PHPStan" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + - name: Install composer dependencies + uses: nick-invision/retry@v1 + with: + timeout_minutes: 10 + max_attempts: 3 + command: composer install + - name: Run PHPStan + run: | + composer require phpstan/phpstan + vendor/bin/phpstan analyse --level=0 src/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5f4b9f5e2..000000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: php -sudo: false -dist: precise -cache: - directories: - - $HOME/.composer/cache - - vendor -php: -- 5.3 -- 5.4 -- 5.5 -- 5.6 -- 7.0 -- 7.1 -- 7.2 -env: - global: - - secure: Bc5ZqvZ1YYpoPZNNuU2eCB8DS6vBYrAdfBtTenBs5NSxzb+Vjven4kWakbzaMvZjb/Ib7Uph7DGuOtJXpmxnvBXPLd707LZ89oFWN/yqQlZKCcm8iErvJCB5XL+/ONHj2iPdR242HJweMcat6bMCwbVWoNDidjtWMH0U2mYFy3M= - - secure: R3bXlymyFiY2k2jf7+fv/J8i34wtXTkmD4mCr5Ps/U+vn9axm2VtvR2Nj+r7LbRjn61gzFE/xIVjYft/wOyBOYwysrfriydrnRVS0owh6y+7EyOyQWbRX11vVQMf8o31QCQE5BY58V5AJZW3MjoOL0FVlTgySJiJvdw6Pv18v+E= -services: -- mongodb -- redis-server -- cassandra -before_install: -- phpenv config-rm xdebug.ini || return 0 -- phpenv version-name | grep ^5.[3-6] && composer remove mongodb/mongodb --dev && echo "extension=mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true -- phpenv version-name | grep ^7 && pecl install -f mongodb-1.3.4 ; true -install: -- composer install -before_script: -- psql -c 'create database oauth2_server_php;' -U postgres -after_script: -- php test/cleanup.php -script: -- vendor/bin/phpunit -v diff --git a/README.md b/README.md index 117743d4f..4ecadbe7a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ oauth2-server-php ================= -[![Build Status](https://travis-ci.org/bshaffer/oauth2-server-php.svg?branch=master)](https://travis-ci.org/bshaffer/oauth2-server-php) - -[![Total Downloads](https://poser.pugx.org/bshaffer/oauth2-server-php/downloads.png)](https://packagist.org/packages/bshaffer/oauth2-server-php) +[![Total Downloads](https://poser.pugx.org/bshaffer/oauth2-server-php/downloads)](https://packagist.org/packages/bshaffer/oauth2-server-php) View the [complete documentation](https://bshaffer.github.io/oauth2-server-php-docs/) diff --git a/composer.json b/composer.json index 272d20027..50a950774 100644 --- a/composer.json +++ b/composer.json @@ -16,21 +16,21 @@ "psr-0": { "OAuth2": "src/" } }, "require":{ - "php":">=5.3.9" + "php":">=7.2" }, "require-dev": { - "phpunit/phpunit": "^4.0", - "aws/aws-sdk-php": "~2.8", - "firebase/php-jwt": "~2.2", - "predis/predis": "dev-master", + "phpunit/phpunit": "^7.5||^8.0", + "aws/aws-sdk-php": "^2.8", + "firebase/php-jwt": "^6.4", + "predis/predis": "^1.1", "thobbs/phpcassa": "dev-master", - "mongodb/mongodb": "^1.1" + "yoast/phpunit-polyfills": "^1.0" }, "suggest": { "predis/predis": "Required to use Redis storage", "thobbs/phpcassa": "Required to use Cassandra storage", "aws/aws-sdk-php": "~2.8 is required to use DynamoDB storage", - "firebase/php-jwt": "~2.2 is required to use JWT features", + "firebase/php-jwt": "~v6.4 is required to use JWT features", "mongodb/mongodb": "^1.1 is required to use MongoDB storage" } } diff --git a/phpunit.xml b/phpunit.xml index e36403f0a..6e663e39d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" bootstrap="test/bootstrap.php" > diff --git a/src/OAuth2/Autoloader.php b/src/OAuth2/Autoloader.php index 4ec08cbdd..b025db34c 100644 --- a/src/OAuth2/Autoloader.php +++ b/src/OAuth2/Autoloader.php @@ -39,7 +39,7 @@ public static function register($dir = null) * Handles autoloading of classes. * * @param string $class - A class name. - * @return boolean - Returns true if the class has been loaded + * @return void */ public function autoload($class) { diff --git a/src/OAuth2/ClientAssertionType/HttpBasic.php b/src/OAuth2/ClientAssertionType/HttpBasic.php index ef6120300..0d8007178 100644 --- a/src/OAuth2/ClientAssertionType/HttpBasic.php +++ b/src/OAuth2/ClientAssertionType/HttpBasic.php @@ -112,7 +112,7 @@ public function getClientId() * * @ingroup oauth2_section_2 */ - public function getClientCredentials(RequestInterface $request, ResponseInterface $response = null) + public function getClientCredentials(RequestInterface $request, ?ResponseInterface $response = null) { if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) { return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW')); diff --git a/src/OAuth2/Controller/AuthorizeController.php b/src/OAuth2/Controller/AuthorizeController.php index 4bafb1d24..181f884a6 100644 --- a/src/OAuth2/Controller/AuthorizeController.php +++ b/src/OAuth2/Controller/AuthorizeController.php @@ -78,7 +78,7 @@ class AuthorizeController implements AuthorizeControllerInterface * ); * @endcode */ - public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ScopeInterface $scopeUtil = null) + public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ?ScopeInterface $scopeUtil = null) { $this->clientStorage = $clientStorage; $this->responseTypes = $responseTypes; @@ -87,6 +87,7 @@ public function __construct(ClientInterface $clientStorage, array $responseTypes 'enforce_state' => true, 'require_exact_redirect_uri' => true, 'redirect_status_code' => 302, + 'enforce_pkce' => false, ), $config); if (is_null($scopeUtil)) { diff --git a/src/OAuth2/Controller/ResourceController.php b/src/OAuth2/Controller/ResourceController.php index 926f90fda..2a5d4b4e3 100644 --- a/src/OAuth2/Controller/ResourceController.php +++ b/src/OAuth2/Controller/ResourceController.php @@ -47,7 +47,7 @@ class ResourceController implements ResourceControllerInterface * @param array $config * @param ScopeInterface $scopeUtil */ - public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, $config = array(), ScopeInterface $scopeUtil = null) + public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, $config = array(), ?ScopeInterface $scopeUtil = null) { $this->tokenType = $tokenType; $this->tokenStorage = $tokenStorage; diff --git a/src/OAuth2/Controller/TokenController.php b/src/OAuth2/Controller/TokenController.php index 7fdaf85a6..1e21b5f67 100644 --- a/src/OAuth2/Controller/TokenController.php +++ b/src/OAuth2/Controller/TokenController.php @@ -54,7 +54,7 @@ class TokenController implements TokenControllerInterface * @param ScopeInterface $scopeUtil * @throws InvalidArgumentException */ - public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ClientAssertionTypeInterface $clientAssertionType = null, ScopeInterface $scopeUtil = null) + public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ?ClientAssertionTypeInterface $clientAssertionType = null, ?ScopeInterface $scopeUtil = null) { if (is_null($clientAssertionType)) { foreach ($grantTypes as $grantType) { diff --git a/src/OAuth2/Encryption/FirebaseJwt.php b/src/OAuth2/Encryption/FirebaseJwt.php index 1b527e0a0..8524bb32c 100644 --- a/src/OAuth2/Encryption/FirebaseJwt.php +++ b/src/OAuth2/Encryption/FirebaseJwt.php @@ -2,6 +2,9 @@ namespace OAuth2\Encryption; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; + /** * Bridge file to use the firebase/php-jwt package for JWT encoding and decoding. * @author Francis Chuang @@ -10,26 +13,36 @@ class FirebaseJwt implements EncryptionInterface { public function __construct() { - if (!class_exists('\JWT')) { + if (!class_exists(JWT::class)) { throw new \ErrorException('firebase/php-jwt must be installed to use this feature. You can do this by running "composer require firebase/php-jwt"'); } } public function encode($payload, $key, $alg = 'HS256', $keyId = null) { - return \JWT::encode($payload, $key, $alg, $keyId); + return JWT::encode($payload, $key, $alg, $keyId); } public function decode($jwt, $key = null, $allowedAlgorithms = null) { try { - //Maintain BC: Do not verify if no algorithms are passed in. if (!$allowedAlgorithms) { - $key = null; + $tks = \explode('.', $jwt); + if (\count($tks) === 3) { + [$headb64] = $tks; + $headerRaw = JWT::urlsafeB64Decode($headb64); + if (($header = JWT::jsonDecode($headerRaw))) { + $key = new Key($key, $header->alg); + } + } + } elseif(is_array($allowedAlgorithms)) { + $key = new Key($key, $allowedAlgorithms[0]); + } else { + $key = new Key($key, $allowedAlgorithms); } - return (array)\JWT::decode($jwt, $key, $allowedAlgorithms); + return (array) JWT::decode($jwt, $key); } catch (\Exception $e) { return false; } @@ -37,11 +50,11 @@ public function decode($jwt, $key = null, $allowedAlgorithms = null) public function urlSafeB64Encode($data) { - return \JWT::urlsafeB64Encode($data); + return JWT::urlsafeB64Encode($data); } public function urlSafeB64Decode($b64) { - return \JWT::urlsafeB64Decode($b64); + return JWT::urlsafeB64Decode($b64); } } diff --git a/src/OAuth2/GrantType/AuthorizationCode.php b/src/OAuth2/GrantType/AuthorizationCode.php index 784f6b3a3..5bcb4f253 100644 --- a/src/OAuth2/GrantType/AuthorizationCode.php +++ b/src/OAuth2/GrantType/AuthorizationCode.php @@ -84,6 +84,41 @@ public function validateRequest(RequestInterface $request, ResponseInterface $re return false; } + if (isset($authCode['code_challenge']) && $authCode['code_challenge']) { + if (!($code_verifier = $request->request('code_verifier'))) { + $response->setError(400, 'code_verifier_missing', "The PKCE code verifier parameter is required."); + + return false; + } + // 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}$/', $code_verifier) !== 1) { + $response->setError(400, 'code_verifier_invalid', "The PKCE code verifier parameter is invalid."); + + return false; + } + $code_verifier = $request->request('code_verifier'); + switch ($authCode['code_challenge_method']) { + case 'S256': + $code_verifier_hashed = strtr(rtrim(base64_encode(hash('sha256', $code_verifier, true)), '='), '+/', '-_'); + break; + + case 'plain': + $code_verifier_hashed = $code_verifier; + break; + + default: + $response->setError(400, 'code_challenge_method_invalid', "Unknown PKCE code challenge method."); + + return FALSE; + } + if ($code_verifier_hashed !== $authCode['code_challenge']) { + $response->setError(400, 'code_verifier_mismatch', "The PKCE code verifier parameter does not match the code challenge."); + + return FALSE; + } + } + if (!isset($authCode['code'])) { $authCode['code'] = $code; // used to expire the code after the access token is granted } diff --git a/src/OAuth2/GrantType/JwtBearer.php b/src/OAuth2/GrantType/JwtBearer.php index 62c1efabd..6d5a9f963 100644 --- a/src/OAuth2/GrantType/JwtBearer.php +++ b/src/OAuth2/GrantType/JwtBearer.php @@ -37,7 +37,7 @@ class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface * @param EncryptionInterface|JWT $jwtUtil - OPTONAL The class used to decode, encode and verify JWTs. * @param array $config */ - public function __construct(JwtBearerInterface $storage, $audience, EncryptionInterface $jwtUtil = null, array $config = array()) + public function __construct(JwtBearerInterface $storage, $audience, ?EncryptionInterface $jwtUtil = null, array $config = array()) { $this->storage = $storage; $this->audience = $audience; @@ -46,13 +46,13 @@ public function __construct(JwtBearerInterface $storage, $audience, EncryptionIn $jwtUtil = new Jwt(); } - $this->config = array_merge(array( + $config = array_merge(array( 'allowed_algorithms' => array('RS256', 'RS384', 'RS512') ), $config); $this->jwtUtil = $jwtUtil; - $this->allowedAlgorithms = $this->config['allowed_algorithms']; + $this->allowedAlgorithms = $config['allowed_algorithms']; } /** @@ -127,7 +127,7 @@ public function validateRequest(RequestInterface $request, ResponseInterface $re } // Check expiration - if (ctype_digit($jwt['exp'])) { + if (ctype_digit((string)$jwt['exp'])) { if ($jwt['exp'] <= time()) { $response->setError(400, 'invalid_grant', "JWT has expired"); @@ -141,7 +141,7 @@ public function validateRequest(RequestInterface $request, ResponseInterface $re // Check the not before time if ($notBefore = $jwt['nbf']) { - if (ctype_digit($notBefore)) { + if (ctype_digit((string)$notBefore)) { if ($notBefore > time()) { $response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time"); diff --git a/src/OAuth2/OpenID/Controller/AuthorizeController.php b/src/OAuth2/OpenID/Controller/AuthorizeController.php index 54c5f9a63..52e183bb3 100644 --- a/src/OAuth2/OpenID/Controller/AuthorizeController.php +++ b/src/OAuth2/OpenID/Controller/AuthorizeController.php @@ -16,6 +16,16 @@ class AuthorizeController extends BaseAuthorizeController implements AuthorizeCo */ private $nonce; + /** + * @var mixed + */ + protected $code_challenge; + + /** + * @var mixed + */ + protected $code_challenge_method; + /** * Set not authorized response * @@ -65,6 +75,10 @@ protected function buildAuthorizeParameters($request, $response, $user_id) // add the nonce to return with the redirect URI $params['nonce'] = $this->nonce; + // Add PKCE code challenge. + $params['code_challenge'] = $this->code_challenge; + $params['code_challenge_method'] = $this->code_challenge_method; + return $params; } @@ -90,6 +104,32 @@ public function validateAuthorizeRequest(RequestInterface $request, ResponseInte $this->nonce = $nonce; + $code_challenge = $request->query('code_challenge'); + $code_challenge_method = $request->query('code_challenge_method'); + + if ($this->config['enforce_pkce']) { + if (!$code_challenge) { + $response->setError(400, 'missing_code_challenge', 'This application requires you provide a PKCE code challenge'); + + return false; + } + + if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $code_challenge) !== 1) { + $response->setError(400, 'invalid_code_challenge', 'The PKCE code challenge supplied is invalid'); + + return false; + } + + if (!in_array($code_challenge_method, array('plain', 'S256'), true)) { + $response->setError(400, 'missing_code_challenge_method', 'This application requires you specify a PKCE code challenge method'); + + return false; + } + } + + $this->code_challenge = $code_challenge; + $this->code_challenge_method = $code_challenge_method; + return true; } diff --git a/src/OAuth2/OpenID/Controller/UserInfoController.php b/src/OAuth2/OpenID/Controller/UserInfoController.php index c489b7af3..e6f8fd77b 100644 --- a/src/OAuth2/OpenID/Controller/UserInfoController.php +++ b/src/OAuth2/OpenID/Controller/UserInfoController.php @@ -30,7 +30,7 @@ class UserInfoController extends ResourceController implements UserInfoControlle * @param array $config * @param ScopeInterface $scopeUtil */ - public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, UserClaimsInterface $userClaimsStorage, $config = array(), ScopeInterface $scopeUtil = null) + public function __construct(TokenTypeInterface $tokenType, AccessTokenInterface $tokenStorage, UserClaimsInterface $userClaimsStorage, $config = array(), ?ScopeInterface $scopeUtil = null) { parent::__construct($tokenType, $tokenStorage, $config, $scopeUtil); diff --git a/src/OAuth2/OpenID/ResponseType/AuthorizationCode.php b/src/OAuth2/OpenID/ResponseType/AuthorizationCode.php index b8ad41ffb..19e04104d 100644 --- a/src/OAuth2/OpenID/ResponseType/AuthorizationCode.php +++ b/src/OAuth2/OpenID/ResponseType/AuthorizationCode.php @@ -31,9 +31,9 @@ public function getAuthorizeResponse($params, $user_id = null) // build the URL to redirect to $result = array('query' => array()); - $params += array('scope' => null, 'state' => null, 'id_token' => null); + $params += array('scope' => null, 'state' => null, 'id_token' => null, 'code_challenge' => null, 'code_challenge_method' => null); - $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['id_token']); + $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['id_token'], $params['code_challenge'], $params['code_challenge_method']); if (isset($params['state'])) { $result['query']['state'] = $params['state']; @@ -56,10 +56,10 @@ public function getAuthorizeResponse($params, $user_id = null) * @see http://tools.ietf.org/html/rfc6749#section-4 * @ingroup oauth2_section_4 */ - public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null) + public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { $code = $this->generateAuthorizationCode(); - $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, $id_token); + $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, $id_token, $code_challenge, $code_challenge_method); return $code; } diff --git a/src/OAuth2/OpenID/ResponseType/IdToken.php b/src/OAuth2/OpenID/ResponseType/IdToken.php index 55e446074..98973f9e9 100644 --- a/src/OAuth2/OpenID/ResponseType/IdToken.php +++ b/src/OAuth2/OpenID/ResponseType/IdToken.php @@ -38,7 +38,7 @@ class IdToken implements IdTokenInterface * @param EncryptionInterface $encryptionUtil * @throws LogicException */ - public function __construct(UserClaimsInterface $userClaimsStorage, PublicKeyInterface $publicKeyStorage, array $config = array(), EncryptionInterface $encryptionUtil = null) + public function __construct(UserClaimsInterface $userClaimsStorage, PublicKeyInterface $publicKeyStorage, array $config = array(), ?EncryptionInterface $encryptionUtil = null) { $this->userClaimsStorage = $userClaimsStorage; $this->publicKeyStorage = $publicKeyStorage; diff --git a/src/OAuth2/OpenID/Storage/AuthorizationCodeInterface.php b/src/OAuth2/OpenID/Storage/AuthorizationCodeInterface.php index 446cec928..8e0988ff4 100644 --- a/src/OAuth2/OpenID/Storage/AuthorizationCodeInterface.php +++ b/src/OAuth2/OpenID/Storage/AuthorizationCodeInterface.php @@ -33,5 +33,5 @@ interface AuthorizationCodeInterface extends BaseAuthorizationCodeInterface * * @ingroup oauth2_section_4 */ - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null); + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null); } diff --git a/src/OAuth2/Request.php b/src/OAuth2/Request.php index f547bf6e8..56a681a62 100644 --- a/src/OAuth2/Request.php +++ b/src/OAuth2/Request.php @@ -34,7 +34,7 @@ class Request implements RequestInterface * * @api */ - public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null) + public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, ?array $headers = null) { $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content, $headers); } @@ -55,7 +55,7 @@ public function __construct(array $query = array(), array $request = array(), ar * * @api */ - public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null) + public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, ?array $headers = null) { $this->request = $request; $this->query = $query; diff --git a/src/OAuth2/Response.php b/src/OAuth2/Response.php index 88c1ad5f7..be1eaa2ac 100644 --- a/src/OAuth2/Response.php +++ b/src/OAuth2/Response.php @@ -140,11 +140,10 @@ public function getStatusCode() } /** - * @param int $statusCode * @param string $text * @throws InvalidArgumentException */ - public function setStatusCode($statusCode, $text = null) + public function setStatusCode(int $statusCode, $text = null) { $this->statusCode = (int) $statusCode; if ($this->isInvalid()) { diff --git a/src/OAuth2/ResponseInterface.php b/src/OAuth2/ResponseInterface.php index fe920864a..df4fa48f5 100644 --- a/src/OAuth2/ResponseInterface.php +++ b/src/OAuth2/ResponseInterface.php @@ -20,10 +20,7 @@ public function addParameters(array $parameters); */ public function addHttpHeaders(array $httpHeaders); - /** - * @param int $statusCode - */ - public function setStatusCode($statusCode); + public function setStatusCode(int $statusCode); /** * @param int $statusCode diff --git a/src/OAuth2/ResponseType/AccessToken.php b/src/OAuth2/ResponseType/AccessToken.php index e836a3447..8b5aec14a 100644 --- a/src/OAuth2/ResponseType/AccessToken.php +++ b/src/OAuth2/ResponseType/AccessToken.php @@ -38,7 +38,7 @@ class AccessToken implements AccessTokenInterface * ); * @endcode */ - public function __construct(AccessTokenStorageInterface $tokenStorage, RefreshTokenInterface $refreshStorage = null, array $config = array()) + public function __construct(AccessTokenStorageInterface $tokenStorage, ?RefreshTokenInterface $refreshStorage = null, array $config = array()) { $this->tokenStorage = $tokenStorage; $this->refreshStorage = $refreshStorage; diff --git a/src/OAuth2/ResponseType/AuthorizationCode.php b/src/OAuth2/ResponseType/AuthorizationCode.php index b92c73cda..12a9f8c38 100644 --- a/src/OAuth2/ResponseType/AuthorizationCode.php +++ b/src/OAuth2/ResponseType/AuthorizationCode.php @@ -26,9 +26,9 @@ public function getAuthorizeResponse($params, $user_id = null) // build the URL to redirect to $result = array('query' => array()); - $params += array('scope' => null, 'state' => null); + $params += array('scope' => null, 'state' => null, 'code_challenge' => null, 'code_challenge_method' => null); - $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope']); + $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['code_challenge'], $params['code_challenge_method']); if (isset($params['state'])) { $result['query']['state'] = $params['state']; @@ -53,10 +53,10 @@ public function getAuthorizeResponse($params, $user_id = null) * @see http://tools.ietf.org/html/rfc6749#section-4 * @ingroup oauth2_section_4 */ - public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null) + public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $code_challenge = null, $code_challenge_method = null) { $code = $this->generateAuthorizationCode(); - $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope); + $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, null, $code_challenge, $code_challenge_method); return $code; } diff --git a/src/OAuth2/ResponseType/JwtAccessToken.php b/src/OAuth2/ResponseType/JwtAccessToken.php index 0ee3708aa..13a24a56d 100644 --- a/src/OAuth2/ResponseType/JwtAccessToken.php +++ b/src/OAuth2/ResponseType/JwtAccessToken.php @@ -26,7 +26,7 @@ class JwtAccessToken extends AccessToken * or just the token ID is stored * @param EncryptionInterface $encryptionUtil - */ - public function __construct(PublicKeyInterface $publicKeyStorage = null, AccessTokenStorageInterface $tokenStorage = null, RefreshTokenInterface $refreshStorage = null, array $config = array(), EncryptionInterface $encryptionUtil = null) + public function __construct(?PublicKeyInterface $publicKeyStorage = null, ?AccessTokenStorageInterface $tokenStorage = null, ?RefreshTokenInterface $refreshStorage = null, array $config = array(), ?EncryptionInterface $encryptionUtil = null) { $this->publicKeyStorage = $publicKeyStorage; $config = array_merge(array( diff --git a/src/OAuth2/Server.php b/src/OAuth2/Server.php index cf040c2bc..e5716358d 100644 --- a/src/OAuth2/Server.php +++ b/src/OAuth2/Server.php @@ -150,7 +150,7 @@ class Server implements ResourceControllerInterface, * * @ingroup oauth2_section_7 */ - public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), TokenTypeInterface $tokenType = null, ScopeInterface $scopeUtil = null, ClientAssertionTypeInterface $clientAssertionType = null) + public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), ?TokenTypeInterface $tokenType = null, ?ScopeInterface $scopeUtil = null, ?ClientAssertionTypeInterface $clientAssertionType = null) { $storage = is_array($storage) ? $storage : array($storage); $this->storages = array(); @@ -172,6 +172,7 @@ public function __construct($storage = array(), array $config = array(), array $ 'enforce_state' => true, 'require_exact_redirect_uri' => true, 'allow_implicit' => false, + 'enforce_pkce' => false, 'allow_credentials_in_request_body' => true, 'allow_public_clients' => true, 'always_issue_new_refresh_token' => false, @@ -288,7 +289,7 @@ public function setUserInfoController(UserInfoControllerInterface $userInfoContr * * @see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo */ - public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response = null) + public function handleUserInfoRequest(RequestInterface $request, ?ResponseInterface $response = null) { $this->response = is_null($response) ? new Response() : $response; $this->getUserInfoController()->handleUserInfoRequest($request, $this->response); @@ -314,7 +315,7 @@ public function handleUserInfoRequest(RequestInterface $request, ResponseInterfa * * @ingroup oauth2_section_4 */ - public function handleTokenRequest(RequestInterface $request, ResponseInterface $response = null) + public function handleTokenRequest(RequestInterface $request, ?ResponseInterface $response = null) { $this->response = is_null($response) ? new Response() : $response; $this->getTokenController()->handleTokenRequest($request, $this->response); @@ -327,7 +328,7 @@ public function handleTokenRequest(RequestInterface $request, ResponseInterface * @param ResponseInterface $response - Response object * @return mixed */ - public function grantAccessToken(RequestInterface $request, ResponseInterface $response = null) + public function grantAccessToken(RequestInterface $request, ?ResponseInterface $response = null) { $this->response = is_null($response) ? new Response() : $response; $value = $this->getTokenController()->grantAccessToken($request, $this->response); @@ -345,7 +346,7 @@ public function grantAccessToken(RequestInterface $request, ResponseInterface $r * @param ResponseInterface $response * @return Response|ResponseInterface */ - public function handleRevokeRequest(RequestInterface $request, ResponseInterface $response = null) + public function handleRevokeRequest(RequestInterface $request, ?ResponseInterface $response = null) { $this->response = is_null($response) ? new Response() : $response; $this->getTokenController()->handleRevokeRequest($request, $this->response); @@ -407,7 +408,7 @@ public function handleAuthorizeRequest(RequestInterface $request, ResponseInterf * * @ingroup oauth2_section_3 */ - public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response = null) + public function validateAuthorizeRequest(RequestInterface $request, ?ResponseInterface $response = null) { $this->response = is_null($response) ? new Response() : $response; $value = $this->getAuthorizeController()->validateAuthorizeRequest($request, $this->response); @@ -421,7 +422,7 @@ public function validateAuthorizeRequest(RequestInterface $request, ResponseInte * @param string $scope - Scope * @return mixed */ - public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response = null, $scope = null) + public function verifyResourceRequest(RequestInterface $request, ?ResponseInterface $response = null, $scope = null) { $this->response = is_null($response) ? new Response() : $response; $value = $this->getResourceController()->verifyResourceRequest($request, $this->response, $scope); @@ -434,7 +435,7 @@ public function verifyResourceRequest(RequestInterface $request, ResponseInterfa * @param ResponseInterface $response - Response object * @return mixed */ - public function getAccessTokenData(RequestInterface $request, ResponseInterface $response = null) + public function getAccessTokenData(RequestInterface $request, ?ResponseInterface $response = null) { $this->response = is_null($response) ? new Response() : $response; $value = $this->getResourceController()->getAccessTokenData($request, $this->response); @@ -577,7 +578,7 @@ protected function createDefaultAuthorizeController() } } - $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state require_exact_redirect_uri'))); + $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state require_exact_redirect_uri enforce_pkce'))); if ($this->config['use_openid_connect']) { return new OpenIDAuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil()); diff --git a/src/OAuth2/Storage/Cassandra.php b/src/OAuth2/Storage/Cassandra.php index e60e9d3ad..3a138bb52 100644 --- a/src/OAuth2/Storage/Cassandra.php +++ b/src/OAuth2/Storage/Cassandra.php @@ -191,11 +191,11 @@ public function getAuthorizationCode($code) * @param string $id_token * @return bool */ - public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { return $this->setValue( $this->config['code_key'] . $authorization_code, - compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), + compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method'), $expires ); } diff --git a/src/OAuth2/Storage/CouchbaseDB.php b/src/OAuth2/Storage/CouchbaseDB.php index 9e8148b6b..31b0cd301 100755 --- a/src/OAuth2/Storage/CouchbaseDB.php +++ b/src/OAuth2/Storage/CouchbaseDB.php @@ -2,6 +2,7 @@ namespace OAuth2\Storage; +use Couchbase; use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface; /** @@ -27,14 +28,18 @@ class CouchbaseDB implements AuthorizationCodeInterface, public function __construct($connection, $config = array()) { - if ($connection instanceof \Couchbase) { + if (!class_exists(Couchbase::class)) { + throw new \RuntimeException('Missing Couchbase'); + } + + if ($connection instanceof Couchbase) { $this->db = $connection; } else { if (!is_array($connection) || !is_array($connection['servers'])) { throw new \InvalidArgumentException('First argument to OAuth2\Storage\CouchbaseDB must be an instance of Couchbase or a configuration array containing a server array'); } - $this->db = new \Couchbase($connection['servers'], (!isset($connection['username'])) ? '' : $connection['username'], (!isset($connection['password'])) ? '' : $connection['password'], $connection['bucket'], false); + $this->db = new Couchbase($connection['servers'], (!isset($connection['username'])) ? '' : $connection['username'], (!isset($connection['password'])) ? '' : $connection['password'], $connection['bucket'], false); } $this->config = array_merge(array( @@ -173,7 +178,7 @@ public function getAuthorizationCode($code) return is_null($code) ? false : $code; } - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { // if it exists, update it. if ($this->getAuthorizationCode($code)) { @@ -185,6 +190,8 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, 'expires' => $expires, 'scope' => $scope, 'id_token' => $id_token, + 'code_challenge' => $code_challenge, + 'code_challenge_method' => $code_challenge_method, )); } else { $this->setObjectByType('code_table',$code,array( @@ -195,6 +202,8 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, 'expires' => $expires, 'scope' => $scope, 'id_token' => $id_token, + 'code_challenge' => $code_challenge, + 'code_challenge_method' => $code_challenge_method, )); } @@ -328,4 +337,4 @@ public function setJti($client_id, $subject, $audience, $expiration, $jti) //TODO: Needs couchbase implementation. throw new \Exception('setJti() for the Couchbase driver is currently unimplemented.'); } -} \ No newline at end of file +} diff --git a/src/OAuth2/Storage/DynamoDB.php b/src/OAuth2/Storage/DynamoDB.php index a54cb3712..713189d23 100644 --- a/src/OAuth2/Storage/DynamoDB.php +++ b/src/OAuth2/Storage/DynamoDB.php @@ -213,12 +213,12 @@ public function getAuthorizationCode($code) } - public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { // convert expires to datestring $expires = date('Y-m-d H:i:s', $expires); - $clientData = compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'id_token', 'scope'); + $clientData = compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method'); $clientData = array_filter($clientData, 'self::isNotEmpty'); $result = $this->client->putItem(array( diff --git a/src/OAuth2/Storage/JwtAccessToken.php b/src/OAuth2/Storage/JwtAccessToken.php index 6ccacd6d9..73c5a3e62 100644 --- a/src/OAuth2/Storage/JwtAccessToken.php +++ b/src/OAuth2/Storage/JwtAccessToken.php @@ -21,7 +21,7 @@ class JwtAccessToken implements JwtAccessTokenInterface * is not necessary when using this grant type. * @param OAuth2\Encryption\EncryptionInterface $encryptionUtil OPTIONAL class to use for "encode" and "decode" functions. */ - public function __construct(PublicKeyInterface $publicKeyStorage, AccessTokenInterface $tokenStorage = null, EncryptionInterface $encryptionUtil = null) + public function __construct(PublicKeyInterface $publicKeyStorage, ?AccessTokenInterface $tokenStorage = null, ?EncryptionInterface $encryptionUtil = null) { $this->publicKeyStorage = $publicKeyStorage; $this->tokenStorage = $tokenStorage; @@ -84,4 +84,4 @@ protected function convertJwtToOAuth2($tokenData) return $tokenData; } -} \ No newline at end of file +} diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 2c60b71ce..c33bd0ebb 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -74,9 +74,9 @@ public function getAuthorizationCode($code) ), $this->authorizationCodes[$code]); } - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { - $this->authorizationCodes[$code] = compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'); + $this->authorizationCodes[$code] = compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method'); return true; } diff --git a/src/OAuth2/Storage/Mongo.php b/src/OAuth2/Storage/Mongo.php index eea06e315..92f93d5b2 100644 --- a/src/OAuth2/Storage/Mongo.php +++ b/src/OAuth2/Storage/Mongo.php @@ -179,7 +179,7 @@ public function getAuthorizationCode($code) return is_null($code) ? false : $code; } - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { // if it exists, update it. if ($this->getAuthorizationCode($code)) { @@ -192,6 +192,8 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, 'expires' => $expires, 'scope' => $scope, 'id_token' => $id_token, + 'code_challenge' => $code_challenge, + 'code_challenge_method' => $code_challenge_method, )) ); } else { @@ -203,6 +205,8 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, 'expires' => $expires, 'scope' => $scope, 'id_token' => $id_token, + 'code_challenge' => $code_challenge, + 'code_challenge_method' => $code_challenge_method, ); $this->collection('code_table')->insert($token); } diff --git a/src/OAuth2/Storage/MongoDB.php b/src/OAuth2/Storage/MongoDB.php index 64f740fc1..0b28a7797 100644 --- a/src/OAuth2/Storage/MongoDB.php +++ b/src/OAuth2/Storage/MongoDB.php @@ -32,6 +32,9 @@ class MongoDB implements AuthorizationCodeInterface, public function __construct($connection, $config = array()) { + if (!class_exists(Database::class) || !class_exists(Client::class)) { + throw new \LogicException('Missing MongoDB php extension. Please install mongodb.so'); + } if ($connection instanceof Database) { $this->db = $connection; } else { @@ -167,7 +170,7 @@ public function getAuthorizationCode($code) return is_null($code) ? false : $code; } - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { // if it exists, update it. if ($this->getAuthorizationCode($code)) { @@ -180,6 +183,8 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, 'expires' => $expires, 'scope' => $scope, 'id_token' => $id_token, + 'code_challenge' => $code_challenge, + 'code_challenge_method' => $code_challenge_method, )) ); return $result->getMatchedCount() > 0; @@ -192,6 +197,8 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, 'expires' => $expires, 'scope' => $scope, 'id_token' => $id_token, + 'code_challenge' => $code_challenge, + 'code_challenge_method' => $code_challenge_method, ); $result = $this->collection('code_table')->insertOne($token); return $result->getInsertedCount() > 0; diff --git a/src/OAuth2/Storage/Pdo.php b/src/OAuth2/Storage/Pdo.php index 074cee447..46c873359 100644 --- a/src/OAuth2/Storage/Pdo.php +++ b/src/OAuth2/Storage/Pdo.php @@ -247,7 +247,7 @@ public function getAuthorizationCode($code) * @param string $id_token * @return bool|mixed */ - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { if (func_num_args() > 6) { // we are calling with an id token @@ -259,12 +259,12 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, // if it exists, update it. if ($this->getAuthorizationCode($code)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope where authorization_code=:code', $this->config['code_table'])); + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, code_challenge=:code_challenge, code_challenge_method=:code_challenge_method where authorization_code=:code', $this->config['code_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope)', $this->config['code_table'])); + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, code_challenge, code_challenge_method) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :code_challenge, :code_challenge_method)', $this->config['code_table'])); } - return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); + return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'code_challenge', 'code_challenge_method')); } /** @@ -277,19 +277,19 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, * @param string $id_token * @return bool */ - private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { // convert expires to datestring $expires = date('Y-m-d H:i:s', $expires); // if it exists, update it. if ($this->getAuthorizationCode($code)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, id_token =:id_token where authorization_code=:code', $this->config['code_table'])); + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, id_token =:id_token, code_challenge=:code_challenge, code_challenge_method=:code_challenge_method where authorization_code=:code', $this->config['code_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :id_token)', $this->config['code_table'])); + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token, code_challenge, code_challenge_method) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :id_token, :code_challenge, :code_challenge_method)', $this->config['code_table'])); } - return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); + return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method')); } /** @@ -676,6 +676,8 @@ public function getBuildSql($dbName = 'oauth2_server_php') expires TIMESTAMP NOT NULL, scope VARCHAR(4000), id_token VARCHAR(1000), + code_challenge VARCHAR(1000), + code_challenge_method VARCHAR(20), PRIMARY KEY (authorization_code) ); diff --git a/src/OAuth2/Storage/Redis.php b/src/OAuth2/Storage/Redis.php index e6294e22d..5a41dfc22 100644 --- a/src/OAuth2/Storage/Redis.php +++ b/src/OAuth2/Storage/Redis.php @@ -95,11 +95,11 @@ public function getAuthorizationCode($code) return $this->getValue($this->config['code_key'] . $code); } - public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null, $code_challenge = null, $code_challenge_method = null) { return $this->setValue( $this->config['code_key'] . $authorization_code, - compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), + compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code_challenge', 'code_challenge_method'), $expires ); } diff --git a/src/OAuth2/TokenType/Bearer.php b/src/OAuth2/TokenType/Bearer.php index 8ac8596ac..aa1094b1e 100644 --- a/src/OAuth2/TokenType/Bearer.php +++ b/src/OAuth2/TokenType/Bearer.php @@ -109,7 +109,7 @@ public function getAccessTokenParameter(RequestInterface $request, ResponseInter } $contentType = $request->server('CONTENT_TYPE'); - if (false !== $pos = strpos($contentType, ';')) { + if (false !== $pos = strpos((string) $contentType, ';')) { $contentType = substr($contentType, 0, $pos); } diff --git a/test/OAuth2/Controller/AuthorizeControllerTest.php b/test/OAuth2/Controller/AuthorizeControllerTest.php index fe3553b23..88f0d0da0 100644 --- a/test/OAuth2/Controller/AuthorizeControllerTest.php +++ b/test/OAuth2/Controller/AuthorizeControllerTest.php @@ -133,7 +133,7 @@ public function testDoNotEnforceState() $server->handleAuthorizeRequest($request, $response = new Response(), true); $this->assertEquals($response->getStatusCode(), 302); - $this->assertNotContains('error', $response->getHttpHeader('Location')); + $this->assertStringNotContainsString('error', $response->getHttpHeader('Location')); } public function testEnforceScope() @@ -161,7 +161,7 @@ public function testEnforceScope() $server->handleAuthorizeRequest($request, $response = new Response(), true); $this->assertEquals($response->getStatusCode(), 302); - $this->assertNotContains('error', $response->getHttpHeader('Location')); + $this->assertStringNotContainsString('error', $response->getHttpHeader('Location')); } public function testInvalidRedirectUri() @@ -227,7 +227,7 @@ public function testRedirectUriWithValidRedirectUri() $server->handleAuthorizeRequest($request, $response = new Response(), true); $this->assertEquals($response->getStatusCode(), 302); - $this->assertContains('code', $response->getHttpHeader('Location')); + $this->assertStringContainsString('code', $response->getHttpHeader('Location')); } public function testRedirectUriWithDifferentQueryAndExactMatchRequired() @@ -263,7 +263,7 @@ public function testRedirectUriWithDifferentQueryAndExactMatchNotRequired() $server->handleAuthorizeRequest($request, $response = new Response(), true); $this->assertEquals($response->getStatusCode(), 302); - $this->assertContains('code', $response->getHttpHeader('Location')); + $this->assertStringContainsString('code', $response->getHttpHeader('Location')); } public function testMultipleRedirectUris() @@ -278,14 +278,14 @@ public function testMultipleRedirectUris() $server->handleAuthorizeRequest($request, $response = new Response(), true); $this->assertEquals($response->getStatusCode(), 302); - $this->assertContains('code', $response->getHttpHeader('Location')); + $this->assertStringContainsString('code', $response->getHttpHeader('Location')); // call again with different (but still valid) redirect URI $request->query['redirect_uri'] = 'http://morehazards.com/'; $server->handleAuthorizeRequest($request, $response = new Response(), true); $this->assertEquals($response->getStatusCode(), 302); - $this->assertContains('code', $response->getHttpHeader('Location')); + $this->assertStringContainsString('code', $response->getHttpHeader('Location')); } /** @@ -303,7 +303,7 @@ public function testNoRedirectUriSuppliedDoesNotRequireTokenRedirectUri() $server->handleAuthorizeRequest($request, $response = new Response(), true); $this->assertEquals($response->getStatusCode(), 302); - $this->assertContains('state', $response->getHttpHeader('Location')); + $this->assertStringContainsString('state', $response->getHttpHeader('Location')); $this->assertStringStartsWith('http://brentertainment.com/?code=', $response->getHttpHeader('Location')); $parts = parse_url(http://github.com/$response-%3EgetHttpHeader('Location')); @@ -421,7 +421,7 @@ public function testSuccessfulRequestStripsExtraParameters() $this->assertEquals($response->getStatusCode(), 302); $location = $response->getHttpHeader('Location'); - $this->assertNotContains('error', $location); + $this->assertStringNotContainsString('error', $location); $parts = parse_url(http://github.com/$location); $this->assertFalse(isset($parts['fake'])); diff --git a/test/OAuth2/Encryption/FirebaseJwtTest.php b/test/OAuth2/Encryption/FirebaseJwtTest.php index c7e92c053..63a5d4036 100644 --- a/test/OAuth2/Encryption/FirebaseJwtTest.php +++ b/test/OAuth2/Encryption/FirebaseJwtTest.php @@ -9,7 +9,7 @@ class FirebaseJwtTest extends TestCase { private $privateKey; - public function setUp() + public function setUp(): void { $this->privateKey = <<privateKey = <<privateKey = <<assertEquals($response->getStatusCode(), 302); $location = $response->getHttpHeader('Location'); - $this->assertNotContains('error', $location); + $this->assertStringNotContainsString('error', $location); $parts = parse_url(http://github.com/$location); $this->assertArrayHasKey('query', $parts); @@ -87,7 +87,7 @@ public function testUserClaimsWithUserId() $this->assertEquals($response->getStatusCode(), 302); $location = $response->getHttpHeader('Location'); - $this->assertNotContains('error', $location); + $this->assertStringNotContainsString('error', $location); $parts = parse_url(http://github.com/$location); $this->assertArrayHasKey('query', $parts); @@ -133,7 +133,7 @@ public function testUserClaimsWithoutUserId() $this->assertEquals($response->getStatusCode(), 302); $location = $response->getHttpHeader('Location'); - $this->assertNotContains('error', $location); + $this->assertStringNotContainsString('error', $location); $parts = parse_url(http://github.com/$location); $this->assertArrayHasKey('query', $parts); diff --git a/test/OAuth2/OpenID/ResponseType/IdTokenTest.php b/test/OAuth2/OpenID/ResponseType/IdTokenTest.php index a0df3a936..85f8abc86 100644 --- a/test/OAuth2/OpenID/ResponseType/IdTokenTest.php +++ b/test/OAuth2/OpenID/ResponseType/IdTokenTest.php @@ -52,7 +52,7 @@ public function testHandleAuthorizeRequest() $this->assertEquals($response->getStatusCode(), 302); $location = $response->getHttpHeader('Location'); - $this->assertNotContains('error', $location); + $this->assertStringNotContainsString('error', $location); $parts = parse_url(http://github.com/$location); $this->assertArrayHasKey('fragment', $parts); @@ -110,7 +110,7 @@ private function extractTokenDataFromResponse(Response $response) { $this->assertEquals($response->getStatusCode(), 302); $location = $response->getHttpHeader('Location'); - $this->assertNotContains('error', $location); + $this->assertStringNotContainsString('error', $location); $parts = parse_url(http://github.com/$location); $this->assertArrayHasKey('fragment', $parts); diff --git a/test/OAuth2/OpenID/ResponseType/IdTokenTokenTest.php b/test/OAuth2/OpenID/ResponseType/IdTokenTokenTest.php index 0573a9866..6d4cb14c3 100644 --- a/test/OAuth2/OpenID/ResponseType/IdTokenTokenTest.php +++ b/test/OAuth2/OpenID/ResponseType/IdTokenTokenTest.php @@ -31,7 +31,7 @@ public function testHandleAuthorizeRequest() $this->assertEquals($response->getStatusCode(), 302); $location = $response->getHttpHeader('Location'); - $this->assertNotContains('error', $location); + $this->assertStringNotContainsString('error', $location); $parts = parse_url(http://github.com/$location); $this->assertArrayHasKey('fragment', $parts); diff --git a/test/OAuth2/ResponseTest.php b/test/OAuth2/ResponseTest.php index 172bc88fd..338f5cc93 100644 --- a/test/OAuth2/ResponseTest.php +++ b/test/OAuth2/ResponseTest.php @@ -14,7 +14,7 @@ public function testRenderAsXml() )); $string = $response->getResponseBody('xml'); - $this->assertContains('baroates', $string); + $this->assertStringContainsString('baroates', $string); } public function testSetRedirect() diff --git a/test/OAuth2/ResponseType/JwtAccessTokenTest.php b/test/OAuth2/ResponseType/JwtAccessTokenTest.php index 6195d557a..ad90e535b 100644 --- a/test/OAuth2/ResponseType/JwtAccessTokenTest.php +++ b/test/OAuth2/ResponseType/JwtAccessTokenTest.php @@ -40,20 +40,20 @@ public function testCreateAccessToken() $this->assertEquals(3600, $delta); $this->assertEquals($decodedAccessToken['id'], $decodedAccessToken['jti']); } - + public function testExtraPayloadCallback() { $jwtconfig = array('jwt_extra_payload_callable' => function() { return array('custom_param' => 'custom_value'); }); - + $server = $this->getTestServer($jwtconfig); $jwtResponseType = $server->getResponseType('token'); - + $accessToken = $jwtResponseType->createAccessToken('Test Client ID', 123, 'test', false); $jwt = new Jwt; $decodedAccessToken = $jwt->decode($accessToken['access_token'], null, false); - + $this->assertArrayHasKey('custom_param', $decodedAccessToken); $this->assertEquals('custom_value', $decodedAccessToken['custom_param']); } @@ -162,7 +162,7 @@ private function getTestServer($jwtconfig = array()) $memoryStorage = Bootstrap::getInstance()->getMemoryStorage(); $storage = array( - 'access_token' => new JwtAccessTokenStorage($memoryStorage), + 'access_token' => new JwtAccessTokenStorage($memoryStorage, $memoryStorage), 'client' => $memoryStorage, 'client_credentials' => $memoryStorage, ); diff --git a/test/OAuth2/ServerTest.php b/test/OAuth2/ServerTest.php index 3106961e2..fab526a6f 100644 --- a/test/OAuth2/ServerTest.php +++ b/test/OAuth2/ServerTest.php @@ -6,27 +6,26 @@ use OAuth2\ResponseType\AuthorizationCode; use OAuth2\Storage\Bootstrap; use PHPUnit\Framework\TestCase; +use Yoast\PHPUnitPolyfills\Polyfills\ExpectPHPException; class ServerTest extends TestCase { - /** - * @expectedException LogicException OAuth2\Storage\ClientInterface - **/ + use ExpectPHPException; + public function testGetAuthorizeControllerWithNoClientStorageThrowsException() { + $this->expectExceptionMessage('OAuth2\Storage\ClientInterface'); // must set Client Storage $server = new Server(); $server->getAuthorizeController(); } - /** - * @expectedException LogicException OAuth2\Storage\AccessTokenInterface - **/ public function testGetAuthorizeControllerWithNoAccessTokenStorageThrowsException() { + $this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface'); // must set AccessToken or AuthorizationCode $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface')); $server->getAuthorizeController(); } @@ -34,8 +33,8 @@ public function testGetAuthorizeControllerWithClientStorageAndAccessTokenRespons { // must set AccessToken or AuthorizationCode $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); - $server->addResponseType($this->getMock('OAuth2\ResponseType\AccessTokenInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface')); + $server->addResponseType($this->createMock('OAuth2\ResponseType\AccessTokenInterface')); $this->assertNotNull($server->getAuthorizeController()); } @@ -44,21 +43,19 @@ public function testGetAuthorizeControllerWithClientStorageAndAuthorizationCodeR { // must set AccessToken or AuthorizationCode $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); - $server->addResponseType($this->getMock('OAuth2\ResponseType\AuthorizationCodeInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface')); + $server->addResponseType($this->createMock('OAuth2\ResponseType\AuthorizationCodeInterface')); $this->assertNotNull($server->getAuthorizeController()); } - /** - * @expectedException LogicException allow_implicit - **/ public function testGetAuthorizeControllerWithClientStorageAndAccessTokenStorageThrowsException() { // must set AuthorizationCode or AccessToken / implicit + $this->expectExceptionMessage('allow_implicit'); $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); - $server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface')); $this->assertNotNull($server->getAuthorizeController()); } @@ -67,8 +64,8 @@ public function testGetAuthorizeControllerWithClientStorageAndAccessTokenStorage { // must set AuthorizationCode or AccessToken / implicit $server = new Server(array(), array('allow_implicit' => true)); - $server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); - $server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface')); $this->assertNotNull($server->getAuthorizeController()); } @@ -77,63 +74,55 @@ public function testGetAuthorizeControllerWithClientStorageAndAuthorizationCodeS { // must set AccessToken or AuthorizationCode $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\ClientInterface')); - $server->addStorage($this->getMock('OAuth2\Storage\AuthorizationCodeInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\AuthorizationCodeInterface')); $this->assertNotNull($server->getAuthorizeController()); } - /** - * @expectedException LogicException grant_types - **/ public function testGetTokenControllerWithGrantTypeStorageThrowsException() { + $this->expectExceptionMessage('grant_types'); $server = new Server(); $server->getTokenController(); } - /** - * @expectedException LogicException OAuth2\Storage\ClientCredentialsInterface - **/ public function testGetTokenControllerWithNoClientCredentialsStorageThrowsException() { + $this->expectExceptionMessage('OAuth2\Storage\ClientCredentialsInterface'); $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\UserCredentialsInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\UserCredentialsInterface')); $server->getTokenController(); } - /** - * @expectedException LogicException OAuth2\Storage\AccessTokenInterface - **/ public function testGetTokenControllerWithNoAccessTokenStorageThrowsException() { + $this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface'); $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface')); $server->getTokenController(); } public function testGetTokenControllerWithAccessTokenAndClientCredentialsStorage() { $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); - $server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface')); $server->getTokenController(); } public function testGetTokenControllerAccessTokenStorageAndClientCredentialsStorageAndGrantTypes() { $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); - $server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); - $server->addGrantType($this->getMockBuilder('OAuth2\GrantType\AuthorizationCode')->disableOriginalConstructor()->getMock()); + $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface')); + $server->addGrantType($this->createMock('OAuth2\GrantType\AuthorizationCode')); $server->getTokenController(); } - /** - * @expectedException LogicException OAuth2\Storage\AccessTokenInterface - **/ public function testGetResourceControllerWithNoAccessTokenStorageThrowsException() { + $this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface'); $server = new Server(); $server->getResourceController(); } @@ -141,41 +130,35 @@ public function testGetResourceControllerWithNoAccessTokenStorageThrowsException public function testGetResourceControllerWithAccessTokenStorage() { $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface')); $server->getResourceController(); } - /** - * @expectedException InvalidArgumentException OAuth2\Storage\AccessTokenInterface - **/ public function testAddingStorageWithInvalidClass() { + $this->expectExceptionMessage('OAuth2\Storage\AccessTokenInterface'); $server = new Server(); $server->addStorage(new \StdClass()); } - /** - * @expectedException InvalidArgumentException access_token - **/ public function testAddingStorageWithInvalidKey() { + $this->expectExceptionMessage('access_token'); $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface'), 'nonexistant_storage'); + $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'), 'nonexistant_storage'); } - /** - * @expectedException InvalidArgumentException OAuth2\Storage\AuthorizationCodeInterface - **/ public function testAddingStorageWithInvalidKeyStorageCombination() { + $this->expectExceptionMessage('OAuth2\Storage\AuthorizationCodeInterface'); $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\AccessTokenInterface'), 'authorization_code'); + $server->addStorage($this->createMock('OAuth2\Storage\AccessTokenInterface'), 'authorization_code'); } public function testAddingStorageWithValidKeyOnlySetsThatKey() { $server = new Server(); - $server->addStorage($this->getMock('OAuth2\Storage\Memory'), 'access_token'); + $server->addStorage($this->createMock('OAuth2\Storage\Memory'), 'access_token'); $reflection = new \ReflectionClass($server); $prop = $reflection->getProperty('storages'); @@ -191,7 +174,7 @@ public function testAddingStorageWithValidKeyOnlySetsThatKey() public function testAddingClientStorageSetsClientCredentialsStorageByDefault() { $server = new Server(); - $memory = $this->getMock('OAuth2\Storage\Memory'); + $memory = $this->createMock('OAuth2\Storage\Memory'); $server->addStorage($memory, 'client'); $client_credentials = $server->getStorage('client_credentials'); @@ -202,7 +185,7 @@ public function testAddingClientStorageSetsClientCredentialsStorageByDefault() public function testAddStorageWithNullValue() { - $memory = $this->getMock('OAuth2\Storage\Memory'); + $memory = $this->createMock('OAuth2\Storage\Memory'); $server = new Server($memory); $server->addStorage(null, 'refresh_token'); @@ -218,7 +201,7 @@ public function testAddStorageWithNullValue() public function testNewServerWithNullStorageValue() { - $memory = $this->getMock('OAuth2\Storage\Memory'); + $memory = $this->createMock('OAuth2\Storage\Memory'); $server = new Server(array( 'client_credentials' => $memory, 'refresh_token' => null, @@ -237,7 +220,7 @@ public function testNewServerWithNullStorageValue() public function testAddingClientCredentialsStorageSetsClientStorageByDefault() { $server = new Server(); - $memory = $this->getMock('OAuth2\Storage\Memory'); + $memory = $this->createMock('OAuth2\Storage\Memory'); $server->addStorage($memory, 'client_credentials'); $client = $server->getStorage('client'); @@ -249,10 +232,9 @@ public function testAddingClientCredentialsStorageSetsClientStorageByDefault() public function testSettingClientStorageByDefaultDoesNotOverrideSetStorage() { $server = new Server(); - $pdo = $this->getMockBuilder('OAuth2\Storage\Pdo') - ->disableOriginalConstructor()->getMock(); + $pdo = $this->createMock('OAuth2\Storage\Pdo'); - $memory = $this->getMock('OAuth2\Storage\Memory'); + $memory = $this->createMock('OAuth2\Storage\Memory'); $server->addStorage($pdo, 'client'); $server->addStorage($memory, 'client_credentials'); @@ -266,7 +248,7 @@ public function testSettingClientStorageByDefaultDoesNotOverrideSetStorage() public function testAddingResponseType() { - $storage = $this->getMock('OAuth2\Storage\Memory'); + $storage = $this->createMock('OAuth2\Storage\Memory'); $storage ->expects($this->any()) ->method('getClientDetails') @@ -323,7 +305,7 @@ public function testCustomClientAssertionType() 'code' => 'testcode', )); // verify the mock clientAssertionType was called as expected - $clientAssertionType = $this->getMock('OAuth2\ClientAssertionType\ClientAssertionTypeInterface', array('validateRequest', 'getClientId')); + $clientAssertionType = $this->createMock('OAuth2\ClientAssertionType\ClientAssertionTypeInterface'); $clientAssertionType ->expects($this->once()) ->method('validateRequest') @@ -420,98 +402,84 @@ public function testValidRefreshTokenWithNewRefreshTokenInResponse() $this->assertFalse($used_token, 'the refresh token used is no longer valid'); } - /** - * @expectedException InvalidArgumentException OAuth2\ResponseType\AuthorizationCodeInterface - **/ public function testAddingUnknownResponseTypeThrowsException() { + $this->expectExceptionMessage('OAuth2\ResponseType\AuthorizationCodeInterface'); $server = new Server(); - $server->addResponseType($this->getMock('OAuth2\ResponseType\ResponseTypeInterface')); + $server->addResponseType($this->createMock('OAuth2\ResponseType\ResponseTypeInterface')); } - /** - * @expectedException LogicException OAuth2\Storage\PublicKeyInterface - **/ public function testUsingJwtAccessTokensWithoutPublicKeyStorageThrowsException() { + $this->expectExceptionMessage('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array(), array('use_jwt_access_tokens' => true)); - $server->addGrantType($this->getMock('OAuth2\GrantType\GrantTypeInterface')); - $server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); - $server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface')); + $server->addGrantType($this->createMock('OAuth2\GrantType\GrantTypeInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface')); + $server->addStorage($this->createMock('OAuth2\Storage\ClientCredentialsInterface')); $server->getTokenController(); } public function testUsingJustJwtAccessTokenStorageWithResourceControllerIsOkay() { - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true)); $this->assertNotNull($server->getResourceController()); $this->assertInstanceOf('OAuth2\Storage\PublicKeyInterface', $server->getStorage('public_key')); } - /** - * @expectedException LogicException OAuth2\Storage\ClientInterface - **/ public function testUsingJustJwtAccessTokenStorageWithAuthorizeControllerThrowsException() { - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); + $this->expectExceptionMessage('OAuth2\Storage\ClientInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true)); $this->assertNotNull($server->getAuthorizeController()); } - /** - * @expectedException LogicException grant_types - **/ public function testUsingJustJwtAccessTokenStorageWithTokenControllerThrowsException() { - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); + $this->expectExceptionMessage('grant_types'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true)); $server->getTokenController(); } public function testUsingJwtAccessTokenAndClientStorageWithAuthorizeControllerIsOkay() { - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); - $client = $this->getMock('OAuth2\Storage\ClientInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); $server = new Server(array($pubkey, $client), array('use_jwt_access_tokens' => true, 'allow_implicit' => true)); $this->assertNotNull($server->getAuthorizeController()); $this->assertInstanceOf('OAuth2\ResponseType\JwtAccessToken', $server->getResponseType('token')); } - /** - * @expectedException LogicException UserClaims - **/ public function testUsingOpenIDConnectWithoutUserClaimsThrowsException() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); + $this->expectExceptionMessage('UserClaims'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); $server = new Server($client, array('use_openid_connect' => true)); $server->getAuthorizeController(); } - /** - * @expectedException LogicException PublicKeyInterface - **/ public function testUsingOpenIDConnectWithoutPublicKeyThrowsException() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); - $userclaims = $this->getMock('OAuth2\OPenID\Storage\UserClaimsInterface'); + $this->expectExceptionMessage('PublicKeyInterface'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); + $userclaims = $this->createMock('OAuth2\OPenID\Storage\UserClaimsInterface'); $server = new Server(array($client, $userclaims), array('use_openid_connect' => true)); $server->getAuthorizeController(); } - /** - * @expectedException LogicException issuer - **/ public function testUsingOpenIDConnectWithoutIssuerThrowsException() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); + $this->expectExceptionMessage('issuer'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array($client, $userclaims, $pubkey), array('use_openid_connect' => true)); $server->getAuthorizeController(); @@ -519,9 +487,9 @@ public function testUsingOpenIDConnectWithoutIssuerThrowsException() public function testUsingOpenIDConnectWithIssuerPublicKeyAndUserClaimsIsOkay() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array($client, $userclaims, $pubkey), array( 'use_openid_connect' => true, 'issuer' => 'someguy', @@ -533,14 +501,12 @@ public function testUsingOpenIDConnectWithIssuerPublicKeyAndUserClaimsIsOkay() $this->assertNull($server->getResponseType('id_token token')); } - /** - * @expectedException LogicException OAuth2\ResponseType\AccessTokenInterface - **/ public function testUsingOpenIDConnectWithAllowImplicitWithoutTokenStorageThrowsException() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); + $this->expectErrorMessage('OAuth2\ResponseType\AccessTokenInterface'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array($client, $userclaims, $pubkey), array( 'use_openid_connect' => true, 'issuer' => 'someguy', @@ -552,9 +518,9 @@ public function testUsingOpenIDConnectWithAllowImplicitWithoutTokenStorageThrows public function testUsingOpenIDConnectWithAllowImplicitAndUseJwtAccessTokensIsOkay() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); $server = new Server(array($client, $userclaims, $pubkey), array( 'use_openid_connect' => true, 'issuer' => 'someguy', @@ -570,10 +536,10 @@ public function testUsingOpenIDConnectWithAllowImplicitAndUseJwtAccessTokensIsOk public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenStorageIsOkay() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); - $token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); + $token = $this->createMock('OAuth2\Storage\AccessTokenInterface'); $server = new Server(array($client, $userclaims, $pubkey, $token), array( 'use_openid_connect' => true, 'issuer' => 'someguy', @@ -588,17 +554,17 @@ public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenStorageIsOk public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenResponseTypeIsOkay() { - $client = $this->getMock('OAuth2\Storage\ClientInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); - // $token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); + $client = $this->createMock('OAuth2\Storage\ClientInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); + // $token = $this->createMock('OAuth2\Storage\AccessTokenInterface'); $server = new Server(array($client, $userclaims, $pubkey), array( 'use_openid_connect' => true, 'issuer' => 'someguy', 'allow_implicit' => true, )); - $token = $this->getMock('OAuth2\ResponseType\AccessTokenInterface'); + $token = $this->createMock('OAuth2\ResponseType\AccessTokenInterface'); $server->addResponseType($token, 'token'); $server->getAuthorizeController(); @@ -607,17 +573,15 @@ public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenResponseTyp $this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenTokenInterface', $server->getResponseType('id_token token')); } - /** - * @expectedException LogicException OAuth2\OpenID\Storage\AuthorizationCodeInterface - **/ public function testUsingOpenIDConnectWithAuthorizationCodeStorageThrowsException() { - $client = $this->getMock('OAuth2\Storage\ClientCredentialsInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); - $token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); - $authcode = $this->getMock('OAuth2\Storage\AuthorizationCodeInterface'); + $client = $this->createMock('OAuth2\Storage\ClientCredentialsInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); + $token = $this->createMock('OAuth2\Storage\AccessTokenInterface'); + $authcode = $this->createMock('OAuth2\Storage\AuthorizationCodeInterface'); + $this->expectErrorMessage('OAuth2\OpenID\Storage\AuthorizationCodeInterface'); $server = new Server(array($client, $userclaims, $pubkey, $token, $authcode), array( 'use_openid_connect' => true, 'issuer' => 'someguy' @@ -630,11 +594,11 @@ public function testUsingOpenIDConnectWithAuthorizationCodeStorageThrowsExceptio public function testUsingOpenIDConnectWithOpenIDAuthorizationCodeStorageCreatesOpenIDAuthorizationCodeGrantType() { - $client = $this->getMock('OAuth2\Storage\ClientCredentialsInterface'); - $userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface'); - $pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface'); - $token = $this->getMock('OAuth2\Storage\AccessTokenInterface'); - $authcode = $this->getMock('OAuth2\OpenID\Storage\AuthorizationCodeInterface'); + $client = $this->createMock('OAuth2\Storage\ClientCredentialsInterface'); + $userclaims = $this->createMock('OAuth2\OpenID\Storage\UserClaimsInterface'); + $pubkey = $this->createMock('OAuth2\Storage\PublicKeyInterface'); + $token = $this->createMock('OAuth2\Storage\AccessTokenInterface'); + $authcode = $this->createMock('OAuth2\OpenID\Storage\AuthorizationCodeInterface'); $server = new Server(array($client, $userclaims, $pubkey, $token, $authcode), array( 'use_openid_connect' => true, @@ -648,7 +612,7 @@ public function testUsingOpenIDConnectWithOpenIDAuthorizationCodeStorageCreatesO public function testMultipleValuedResponseTypeOrderDoesntMatter() { - $responseType = $this->getMock('OAuth2\OpenID\ResponseType\IdTokenTokenInterface'); + $responseType = $this->createMock('OAuth2\OpenID\ResponseType\IdTokenTokenInterface'); $server = new Server(array(), array(), array(), array( 'token id_token' => $responseType, )); @@ -659,7 +623,7 @@ public function testMultipleValuedResponseTypeOrderDoesntMatter() public function testAddGrantTypeWithoutKey() { $server = new Server(); - $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->getMock('OAuth2\Storage\AuthorizationCodeInterface'))); + $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->createMock('OAuth2\Storage\AuthorizationCodeInterface'))); $grantTypes = $server->getGrantTypes(); $this->assertEquals('authorization_code', key($grantTypes)); @@ -668,7 +632,7 @@ public function testAddGrantTypeWithoutKey() public function testAddGrantTypeWithKey() { $server = new Server(); - $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->getMock('OAuth2\Storage\AuthorizationCodeInterface')), 'ac'); + $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->createMock('OAuth2\Storage\AuthorizationCodeInterface')), 'ac'); $grantTypes = $server->getGrantTypes(); $this->assertEquals('ac', key($grantTypes)); @@ -677,7 +641,7 @@ public function testAddGrantTypeWithKey() public function testAddGrantTypeWithKeyNotString() { $server = new Server(); - $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->getMock('OAuth2\Storage\AuthorizationCodeInterface')), 42); + $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($this->createMock('OAuth2\Storage\AuthorizationCodeInterface')), 42); $grantTypes = $server->getGrantTypes(); $this->assertEquals('authorization_code', key($grantTypes)); diff --git a/test/OAuth2/Storage/PdoTest.php b/test/OAuth2/Storage/PdoTest.php index 4599f69bf..9a7630423 100644 --- a/test/OAuth2/Storage/PdoTest.php +++ b/test/OAuth2/Storage/PdoTest.php @@ -2,8 +2,12 @@ namespace OAuth2\Storage; +use Yoast\PHPUnitPolyfills\Polyfills\ExpectPHPException; + class PdoTest extends BaseTest { + use ExpectPHPException; + public function testCreatePdoStorageUsingPdoClass() { $dsn = sprintf('sqlite:%s', Bootstrap::getInstance()->getSqliteDir()); @@ -30,11 +34,9 @@ public function testCreatePdoStorageUsingConfig() $this->assertNotNull($storage->getClientDetails('oauth_test_client')); } - /** - * @expectedException InvalidArgumentException dsn - */ public function testCreatePdoStorageWithoutDSNThrowsException() { + $this->expectErrorMessage('dsn'); $config = array('username' => 'brent', 'password' => 'brentisaballer'); $storage = new Pdo($config); } diff --git a/test/OAuth2/TokenType/BearerTest.php b/test/OAuth2/TokenType/BearerTest.php index 71cca3bd9..021fc7f5a 100644 --- a/test/OAuth2/TokenType/BearerTest.php +++ b/test/OAuth2/TokenType/BearerTest.php @@ -35,6 +35,17 @@ public function testInvalidContentType() $this->assertEquals($response->getParameter('error_description'), 'The content type for POST requests must be "application/x-www-form-urlencoded"'); } + public function testMissingContentTypeExpectsToBeCorrectContent() + { + $bearer = new Bearer(); + $request = TestRequest::createPost(array( + 'access_token' => 'ThisIsMyAccessToken' + )); + + $param = $bearer->getAccessTokenParameter($request, $response = new Response()); + $this->assertEquals($param, 'ThisIsMyAccessToken'); + } + public function testValidRequestUsingAuthorizationHeader() { $bearer = new Bearer(); diff --git a/test/cleanup.php b/test/cleanup.php deleted file mode 100644 index 8663a901b..000000000 --- a/test/cleanup.php +++ /dev/null @@ -1,15 +0,0 @@ -cleanupTravisDynamoDb(); diff --git a/test/lib/OAuth2/Storage/Bootstrap.php b/test/lib/OAuth2/Storage/Bootstrap.php index 8e428f9b5..66c93ae5b 100755 --- a/test/lib/OAuth2/Storage/Bootstrap.php +++ b/test/lib/OAuth2/Storage/Bootstrap.php @@ -68,7 +68,7 @@ public function getPostgresPdo() public function getPostgresDriver() { try { - $pdo = new \PDO('pgsql:host=localhost;dbname=oauth2_server_php', 'postgres'); + $pdo = new \PDO('pgsql:host=localhost;dbname=oauth2_server_php', 'postgres', 'postgres'); return $pdo; } catch (\PDOException $e) { @@ -118,7 +118,7 @@ public function getMysqlPdo() if (!$this->mysql) { $pdo = null; try { - $pdo = new \PDO('mysql:host=localhost;', 'root'); + $pdo = new \PDO('mysql:host=localhost;', 'root', 'root'); } catch (\PDOException $e) { $this->mysql = new NullStorage('MySQL', 'Unable to connect to MySQL on root@localhost'); } @@ -352,11 +352,11 @@ private function removeMysqlDb(\PDO $pdo) private function createPostgresDb() { - if (!`psql postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='postgres'"`) { - `createuser -s -r postgres`; + if (!`PGPASSWORD=postgres psql postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='postgres'" -h localhost -U postgres`) { + `PGPASSWORD=postgres createuser -s -r postgres -h localhost -U postgres`; } - `createdb -O postgres oauth2_server_php`; + `PGPASSWORD=postgres createdb -O postgres oauth2_server_php -h localhost -U postgres`; } private function populatePostgresDb(\PDO $pdo) @@ -366,8 +366,8 @@ private function populatePostgresDb(\PDO $pdo) private function removePostgresDb() { - if (trim(`psql -l | grep oauth2_server_php | wc -l`)) { - `dropdb oauth2_server_php`; + if (trim(`PGPASSWORD=postgres psql -l -h localhost -U postgres | grep oauth2_server_php | wc -l`)) { + `PGPASSWORD=postgres dropdb oauth2_server_php -h localhost -U postgres`; } } @@ -945,21 +945,6 @@ private function populateDynamoDb($client, $prefix = null) )); } - public function cleanupTravisDynamoDb($prefix = null) - { - if (is_null($prefix)) { - // skip this when not applicable - if (!$this->getEnvVar('TRAVIS') || self::DYNAMODB_PHP_VERSION != $this->getEnvVar('TRAVIS_PHP_VERSION')) { - return; - } - - $prefix = sprintf('build_%s_', $this->getEnvVar('TRAVIS_JOB_NUMBER')); - } - - $client = $this->getDynamoDbClient(); - $this->deleteDynamoDb($client, $prefix); - } - private function getEnvVar($var, $default = null) { return isset($_SERVER[$var]) ? $_SERVER[$var] : (getenv($var) ?: $default);