diff --git a/.travis.yml b/.travis.yml index f383552..6c9d90a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ language: php php: - - 5.3 - - 5.4 - 5.5 - 5.6 + - 7.0 + - 7.1 + - 7.2 - hhvm install: diff --git a/composer.json b/composer.json index 286a924..d2fa88b 100644 --- a/composer.json +++ b/composer.json @@ -16,8 +16,8 @@ } ], "require": { - "php": ">=5.3.2", - "symfony/http-foundation": "~2.0|~3.0|~4.0" + "php": "^5.5.9|>=7.0.8", + "symfony/http-foundation": "~3.0|~4.0" }, "require-dev": { "phpunit/phpunit": "~4.0" diff --git a/lib/OAuth2.php b/lib/OAuth2.php index 31da279..b28d4c5 100644 --- a/lib/OAuth2.php +++ b/lib/OAuth2.php @@ -269,6 +269,8 @@ class OAuth2 * * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.1.2 * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-5.2 + * + * @deprecated Use status code from Symfony Response class instead */ const HTTP_FOUND = '302 Found'; const HTTP_BAD_REQUEST = '400 Bad Request'; @@ -494,24 +496,24 @@ public function verifyAccessToken($tokenParam, $scope = null) $realm = $this->getVariable(self::CONFIG_WWW_REALM); if (!$tokenParam) { // Access token was not provided - throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.', $scope); + throw new OAuth2AuthenticateException(Response::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.', $scope); } // Get the stored token data (from the implementing subclass) $token = $this->storage->getAccessToken($tokenParam); if (!$token) { - throw new OAuth2AuthenticateException(self::HTTP_UNAUTHORIZED, $tokenType, $realm, self::ERROR_INVALID_GRANT, 'The access token provided is invalid.', $scope); + throw new OAuth2AuthenticateException(Response::HTTP_UNAUTHORIZED, $tokenType, $realm, self::ERROR_INVALID_GRANT, 'The access token provided is invalid.', $scope); } // Check token expiration (expires is a mandatory paramter) if ($token->hasExpired()) { - throw new OAuth2AuthenticateException(self::HTTP_UNAUTHORIZED, $tokenType, $realm, self::ERROR_INVALID_GRANT, 'The access token provided has expired.', $scope); + throw new OAuth2AuthenticateException(Response::HTTP_UNAUTHORIZED, $tokenType, $realm, self::ERROR_INVALID_GRANT, 'The access token provided has expired.', $scope); } // Check scope, if provided // If token doesn't have a scope, it's null/empty, or it's insufficient, then throw an error if ($scope && (!$token->getScope() || !$this->checkScope($scope, $token->getScope()))) { - throw new OAuth2AuthenticateException(self::HTTP_FORBIDDEN, $tokenType, $realm, self::ERROR_INSUFFICIENT_SCOPE, 'The request requires higher privileges than provided by the access token.', $scope); + throw new OAuth2AuthenticateException(Response::HTTP_FORBIDDEN, $tokenType, $realm, self::ERROR_INSUFFICIENT_SCOPE, 'The request requires higher privileges than provided by the access token.', $scope); } return $token; @@ -567,7 +569,7 @@ public function getBearerToken(Request $request = null, $removeFromRequest = fal if (count($tokens) > 1) { $realm = $this->getVariable(self::CONFIG_WWW_REALM); $tokenType = $this->getVariable(self::CONFIG_TOKEN_TYPE); - throw new OAuth2AuthenticateException(self::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'Only one method may be used to authenticate at a time (Auth header, GET or POST).'); + throw new OAuth2AuthenticateException(Response::HTTP_BAD_REQUEST, $tokenType, $realm, self::ERROR_INVALID_REQUEST, 'Only one method may be used to authenticate at a time (Auth header, GET or POST).'); } if (count($tokens) < 1) { @@ -784,7 +786,7 @@ public function grantAccessToken(Request $request = null) // Grant Type must be specified. if (!$input["grant_type"]) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing'); } // Authorize the client @@ -793,15 +795,15 @@ public function grantAccessToken(Request $request = null) $client = $this->storage->getClient($clientCredentials[0]); if (!$client) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client credentials are invalid'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client credentials are invalid'); } if ($this->storage->checkClientCredentials($client, $clientCredentials[1]) === false) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client credentials are invalid'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client credentials are invalid'); } if (!$this->storage->checkRestrictedGrantType($client, $input["grant_type"])) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNAUTHORIZED_CLIENT, 'The grant type is unauthorized for this client_id'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_UNAUTHORIZED_CLIENT, 'The grant type is unauthorized for this client_id'); } // Do the granting @@ -827,7 +829,7 @@ public function grantAccessToken(Request $request = null) && !filter_var($input["grant_type"], FILTER_VALIDATE_URL) ) { throw new OAuth2ServerException( - self::HTTP_BAD_REQUEST, + Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing' ); @@ -851,7 +853,7 @@ public function grantAccessToken(Request $request = null) if ($input["scope"]) { // Check scope, if provided if (!isset($stored["scope"]) || !$this->checkScope($input["scope"], $stored["scope"])) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_SCOPE, 'An unsupported scope was requested.'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_SCOPE, 'An unsupported scope was requested.'); } $scope = $input["scope"]; } @@ -870,22 +872,22 @@ public function grantAccessToken(Request $request = null) protected function grantAccessTokenAuthCode(IOAuth2Client $client, array $input) { if (!($this->storage instanceof IOAuth2GrantCode)) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); } if (!$input["code"]) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Missing parameter. "code" is required'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Missing parameter. "code" is required'); } if ($this->getVariable(self::CONFIG_ENFORCE_INPUT_REDIRECT) && !$input["redirect_uri"]) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, "The redirect URI parameter is required."); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, "The redirect URI parameter is required."); } $authCode = $this->storage->getAuthCode($input["code"]); // Check the code exists if ($authCode === null || $client->getPublicId() !== $authCode->getClientId()) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "Code doesn't exist or is invalid for the client"); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "Code doesn't exist or is invalid for the client"); } // Validate the redirect URI. If a redirect URI has been provided on input, it must be validated @@ -894,11 +896,11 @@ protected function grantAccessTokenAuthCode(IOAuth2Client $client, array $input) $authCode->getRedirectUri() ) ) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, "The redirect URI is missing or do not match"); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, "The redirect URI is missing or do not match"); } if ($authCode->hasExpired()) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "The authorization code has expired"); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "The authorization code has expired"); } $this->usedAuthCode = $authCode; @@ -919,17 +921,17 @@ protected function grantAccessTokenAuthCode(IOAuth2Client $client, array $input) protected function grantAccessTokenUserCredentials(IOAuth2Client $client, array $input) { if (!($this->storage instanceof IOAuth2GrantUser)) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); } if (!$input["username"] || !$input["password"]) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Missing parameters. "username" and "password" required'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Missing parameters. "username" and "password" required'); } $stored = $this->storage->checkUserCredentials($client, $input["username"], $input["password"]); if ($stored === false) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "Invalid username and password combination"); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, "Invalid username and password combination"); } return $stored; @@ -946,17 +948,17 @@ protected function grantAccessTokenUserCredentials(IOAuth2Client $client, array protected function grantAccessTokenClientCredentials(IOAuth2Client $client, array $input, array $clientCredentials) { if (!($this->storage instanceof IOAuth2GrantClient)) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); } if (empty($clientCredentials[1])) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client_secret is mandatory for the "client_credentials" grant type'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'The client_secret is mandatory for the "client_credentials" grant type'); } $stored = $this->storage->checkClientCredentialsGrant($client, $clientCredentials[1]); if ($stored === false) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT); } if (!is_array($stored)) { @@ -976,21 +978,21 @@ protected function grantAccessTokenClientCredentials(IOAuth2Client $client, arra protected function grantAccessTokenRefreshToken(IOAuth2Client $client, array $input) { if (!($this->storage instanceof IOAuth2RefreshTokens)) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); } if (!$input["refresh_token"]) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'No "refresh_token" parameter found'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'No "refresh_token" parameter found'); } $token = $this->storage->getRefreshToken($input["refresh_token"]); if ($token === null || $client->getPublicId() !== $token->getClientId()) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, 'Invalid refresh token'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, 'Invalid refresh token'); } if ($token->hasExpired()) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, 'Refresh token has expired'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT, 'Refresh token has expired'); } // store the refresh token locally so we can delete it when a new refresh token is generated @@ -1005,7 +1007,7 @@ protected function grantAccessTokenRefreshToken(IOAuth2Client $client, array $in protected function grantAccessTokenExtension(IOAuth2Client $client, array $inputData, array $authHeaders) { if (!($this->storage instanceof IOAuth2GrantExtension)) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE); } $uri = $inputData["grant_type"]; @@ -1016,7 +1018,7 @@ protected function grantAccessTokenExtension(IOAuth2Client $client, array $input $stored = $this->storage->checkGrantExtension($client, $uri, $inputData, $authHeaders); if ($stored === false) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_GRANT); } return $stored; @@ -1051,7 +1053,7 @@ protected function getClientCredentials(array $inputData, array $authHeaders) if (!empty($authHeaders['PHP_AUTH_USER'])) { return array($authHeaders['PHP_AUTH_USER'], $authHeaders['PHP_AUTH_PW']); } elseif (empty($inputData['client_id'])) { // No credentials were specified - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'Client id was not found in the headers or body'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'Client id was not found in the headers or body'); } else { // This method is not recommended, but is supported by specification $client_id = $inputData['client_id']; @@ -1110,13 +1112,13 @@ protected function getAuthorizeParams(Request $request = null) // Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI) if (!$input["client_id"]) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, "No client id supplied"); // We don't have a good URI to use + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, "No client id supplied"); // We don't have a good URI to use } // Get client details $client = $this->storage->getClient($input["client_id"]); if (!$client) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'Unknown client'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_INVALID_CLIENT, 'Unknown client'); } $input["redirect_uri"] = $this->getRedirectUri($input["redirect_uri"], $client); @@ -1169,13 +1171,13 @@ protected function getRedirectUri($redirectUri, IOAuth2Client $client) if (empty($redirectUri)) { if (!$client->getRedirectUris()) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'No redirect URL was supplied or registered.'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'No redirect URL was supplied or registered.'); } if (count($client->getRedirectUris()) > 1) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'No redirect URL was supplied and more than one is registered.'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'No redirect URL was supplied and more than one is registered.'); } if ($this->getVariable(self::CONFIG_ENFORCE_INPUT_REDIRECT)) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'The redirect URI is mandatory and was not supplied.'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'The redirect URI is mandatory and was not supplied.'); } $redirectUri = current($client->getRedirectUris()); @@ -1183,7 +1185,7 @@ protected function getRedirectUri($redirectUri, IOAuth2Client $client) } else { // Only need to validate if redirect_uri is provided on input and stored if (!$this->validateRedirectUri($redirectUri, $client->getRedirectUris())) { - throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'The redirect URI provided does not match registered URI(s).'); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, self::ERROR_REDIRECT_URI_MISMATCH, 'The redirect URI provided does not match registered URI(s).'); } } diff --git a/lib/OAuth2RedirectException.php b/lib/OAuth2RedirectException.php index 6de516e..6e9e89d 100644 --- a/lib/OAuth2RedirectException.php +++ b/lib/OAuth2RedirectException.php @@ -2,6 +2,8 @@ namespace OAuth2; +use Symfony\Component\HttpFoundation\Response; + /** * Redirect the end-user's user agent with error message. * @@ -37,7 +39,7 @@ class OAuth2RedirectException extends OAuth2ServerException */ public function __construct($redirectUri, $error, $errorDescription = null, $state = null, $method = OAuth2::TRANSPORT_QUERY) { - parent::__construct(OAuth2::HTTP_FOUND, $error, $errorDescription); + parent::__construct(Response::HTTP_FOUND, $error, $errorDescription); $this->method = $method; $this->redirectUri = $redirectUri; diff --git a/tests/Fixtures/OAuth2GrantExtensionJwtBearer.php b/tests/Fixtures/OAuth2GrantExtensionJwtBearer.php index 06a4976..6f0d139 100644 --- a/tests/Fixtures/OAuth2GrantExtensionJwtBearer.php +++ b/tests/Fixtures/OAuth2GrantExtensionJwtBearer.php @@ -7,6 +7,7 @@ use OAuth2\OAuth2ServerException; use OAuth2\Model\IOAuth2Client; use OAuth2\Tests\Fixtures\OAuth2StorageStub; +use Symfony\Component\HttpFoundation\Response; class OAuth2GrantExtensionJwtBearer extends OAuth2StorageStub implements IOAuth2GrantExtension { @@ -15,7 +16,7 @@ class OAuth2GrantExtensionJwtBearer extends OAuth2StorageStub implements IOAuth2 public function checkGrantExtension(IOAuth2Client $client, $uri, array $inputData, array $authHeaders) { if ('urn:ietf:params:oauth:grant-type:jwt-bearer' !== $uri) { - throw new OAuth2ServerException(OAuth2::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE); } if (!isset($inputData['jwt'])) { diff --git a/tests/Fixtures/OAuth2GrantExtensionLifetimeStub.php b/tests/Fixtures/OAuth2GrantExtensionLifetimeStub.php index f343cc9..444cb9e 100644 --- a/tests/Fixtures/OAuth2GrantExtensionLifetimeStub.php +++ b/tests/Fixtures/OAuth2GrantExtensionLifetimeStub.php @@ -6,6 +6,7 @@ use OAuth2\IOAuth2GrantExtension; use OAuth2\OAuth2ServerException; use OAuth2\Model\IOAuth2Client; +use Symfony\Component\HttpFoundation\Response; class OAuth2GrantExtensionLifetimeStub extends OAuth2StorageStub implements IOAuth2GrantExtension { @@ -14,7 +15,7 @@ class OAuth2GrantExtensionLifetimeStub extends OAuth2StorageStub implements IOAu public function checkGrantExtension(IOAuth2Client $client, $uri, array $inputData, array $authHeaders) { if ('http://company.com/fb_access_token_time_limited' !== $uri) { - throw new OAuth2ServerException(OAuth2::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE); } if (!isset($inputData['fb_access_token'])) { diff --git a/tests/Fixtures/OAuth2GrantExtensionStub.php b/tests/Fixtures/OAuth2GrantExtensionStub.php index fa03f87..67b7037 100644 --- a/tests/Fixtures/OAuth2GrantExtensionStub.php +++ b/tests/Fixtures/OAuth2GrantExtensionStub.php @@ -7,6 +7,7 @@ use OAuth2\OAuth2ServerException; use OAuth2\Model\IOAuth2Client; use OAuth2\Tests\Fixtures\OAuth2StorageStub; +use Symfony\Component\HttpFoundation\Response; class OAuth2GrantExtensionStub extends OAuth2StorageStub implements IOAuth2GrantExtension { @@ -15,7 +16,7 @@ class OAuth2GrantExtensionStub extends OAuth2StorageStub implements IOAuth2Grant public function checkGrantExtension(IOAuth2Client $client, $uri, array $inputData, array $authHeaders) { if ('http://company.com/fb_access_token' !== $uri) { - throw new OAuth2ServerException(OAuth2::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE); + throw new OAuth2ServerException(Response::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE); } if (!isset($inputData['fb_access_token'])) {