diff options
59 files changed, 521 insertions, 299 deletions
@@ -174,6 +174,10 @@ Supported grant types are as follows: The client can use their credentials to retrieve an access token directly, which will allow access to resources under the client's control + 4. [JWT Authorization Grant](http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-4) + + The client can submit a JWT (JSON Web Token) in a request to the token endpoint. An access token (without a refresh token) is then returned directly. + Create a custom grant type by implementing the `OAuth2_GrantTypeInterface` and adding it to the OAuth2 Server object. Acknowledgements diff --git a/src/OAuth2/Autoloader.php b/src/OAuth2/Autoloader.php index a9673d8..40a42fb 100644 --- a/src/OAuth2/Autoloader.php +++ b/src/OAuth2/Autoloader.php @@ -20,7 +20,7 @@ class OAuth2_Autoloader /** * Registers AdobeDigitalMarketing_Autoloader as an SPL autoloader. */ - static public function register($dir = null) + public static function register($dir = null) { ini_set('unserialize_callback_func', 'spl_autoload_call'); spl_autoload_register(array(new self($dir), 'autoload')); diff --git a/src/OAuth2/Controller/AccessController.php b/src/OAuth2/Controller/AccessController.php index 883c64a..3f2e9e2 100644 --- a/src/OAuth2/Controller/AccessController.php +++ b/src/OAuth2/Controller/AccessController.php @@ -1,16 +1,17 @@ <?php /** -* -*/ + * @see OAuth2_Controller_AccessControllerInterface + */ class OAuth2_Controller_AccessController implements OAuth2_Controller_AccessControllerInterface { private $response; private $tokenType; private $tokenStorage; private $config; + private $scopeUtil; - public function __construct(OAuth2_TokenTypeInterface $tokenType, OAuth2_Storage_AccessTokenInterface $tokenStorage, $config = array(), $util = null) + public function __construct(OAuth2_TokenTypeInterface $tokenType, OAuth2_Storage_AccessTokenInterface $tokenStorage, $config = array(), $scopeUtil = null) { $this->tokenType = $tokenType; $this->tokenStorage = $tokenStorage; @@ -19,10 +20,10 @@ class OAuth2_Controller_AccessController implements OAuth2_Controller_AccessCont 'www_realm' => 'Service', ), $config); - if (is_null($util)) { - $util = new OAuth2_Util(); + if (is_null($scopeUtil)) { + $scopeUtil = new OAuth2_Util_Scope(); } - $this->util = $util; + $this->scopeUtil = $scopeUtil; } public function verifyAccessRequest(OAuth2_RequestInterface $request) @@ -30,7 +31,7 @@ class OAuth2_Controller_AccessController implements OAuth2_Controller_AccessCont $token = $this->tokenType->getAccessTokenParameter($request); $this->response = $this->tokenType->getResponse(); if ($token) { - $access_token = $this->getAccessTokenData($token, $request->query('scope')); + $access_token = $this->getAccessTokenData($token, $this->scopeUtil->getScopeFromRequest($request)); return (bool) $access_token; } @@ -65,7 +66,7 @@ class OAuth2_Controller_AccessController implements OAuth2_Controller_AccessCont // 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 && (!isset($token["scope"]) || !$token["scope"] || !$this->util->checkScope($scope, $token["scope"]))) { + if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->scopeUtil->checkScope($scope, $token["scope"]))) { $this->response = new OAuth2_Response_AuthenticationError(401, 'insufficient_scope', 'The request requires higher privileges than provided by the access token', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope); return null; } @@ -77,4 +78,4 @@ class OAuth2_Controller_AccessController implements OAuth2_Controller_AccessCont { return $this->response; } -}
\ No newline at end of file +} diff --git a/src/OAuth2/Controller/AccessControllerInterface.php b/src/OAuth2/Controller/AccessControllerInterface.php index a7b77c8..9571b9d 100644 --- a/src/OAuth2/Controller/AccessControllerInterface.php +++ b/src/OAuth2/Controller/AccessControllerInterface.php @@ -1,8 +1,21 @@ <?php +/** + * This controller is called when a "resource" is requested. + * call verifyAccessRequest in order to determine if the request + * contains a valid token. + * + * ex: + * > if (!$accessController->verifyAccessRequest(OAuth2_Request::createFromGlobals())) { + * > $accessController->getResponse()->send(); // authorization failed + * > die(); + * > } + * > return json_encode($resource); // valid token! Send the stuff! + * + */ interface OAuth2_Controller_AccessControllerInterface extends OAuth2_Response_ProviderInterface { public function verifyAccessRequest(OAuth2_RequestInterface $request); public function getAccessTokenData($token_param, $scope = null); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Controller/AuthorizeController.php b/src/OAuth2/Controller/AuthorizeController.php index 09ff56c..0a443f7 100644 --- a/src/OAuth2/Controller/AuthorizeController.php +++ b/src/OAuth2/Controller/AuthorizeController.php @@ -1,17 +1,17 @@ <?php /** -* -*/ + * @see OAuth2_Controller_AuthorizeControllerInterface + */ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_AuthorizeControllerInterface { private $response; private $clientStorage; private $responseTypes; private $config; - private $util; + private $scopeUtil; - public function __construct(OAuth2_Storage_ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), $util = null) + public function __construct(OAuth2_Storage_ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), $scopeUtil = null) { $this->clientStorage = $clientStorage; $this->responseTypes = $responseTypes; @@ -21,10 +21,10 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori 'enforce_state' => false, ), $config); - if (is_null($util)) { - $util = new OAuth2_Util(); + if (is_null($scopeUtil)) { + $scopeUtil = new OAuth2_Util_Scope(); } - $this->util = $util; + $this->scopeUtil = $scopeUtil; } public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null) @@ -50,7 +50,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori } list($redirect_uri, $result) = $authResult; - $uri = $this->util->buildUri($redirect_uri, $result); + $uri = $this->buildUri($redirect_uri, $result); // return redirect response return new OAuth2_Response_Redirect($uri); @@ -94,7 +94,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori } // Only need to validate if redirect_uri provided on input and clientData. - if ($clientData["redirect_uri"] && $redirect_uri && !$this->util->validateRedirectUri($redirect_uri, $clientData["redirect_uri"])) { + if ($clientData["redirect_uri"] && $redirect_uri && !$this->validateRedirectUri($redirect_uri, $clientData["redirect_uri"])) { $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match'); return false; } @@ -103,7 +103,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori $redirect_uri = $redirect_uri ? $redirect_uri : $clientData["redirect_uri"]; $response_type = $request->query('response_type'); $state = $request->query('state'); - $scope = $request->query('scope'); + $scope = $this->scopeUtil->getScopeFromRequest($request); // type and client_id are required if (!$response_type || !in_array($response_type, array(self::RESPONSE_TYPE_AUTHORIZATION_CODE, self::RESPONSE_TYPE_ACCESS_TOKEN))) { @@ -127,7 +127,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori } // Validate that the requested scope is supported - if ($scope && !$this->util->checkScope($scope, $this->config['supported_scopes'])) { + if ($scope && !$this->scopeUtil->checkScope($scope, $this->config['supported_scopes'])) { $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_scope', 'An unsupported scope was requested', $state); return false; } @@ -142,8 +142,60 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori return ((array)$request->getAllQueryParameters() + $clientData + array('state' => null)); } + /** + * Build the absolute URI based on supplied URI and parameters. + * + * @param $uri + * An absolute URI. + * @param $params + * Parameters to be append as GET. + * + * @return + * An absolute URI with supplied parameters. + * + * @ingroup oauth2_section_4 + */ + private function buildUri($uri, $params) + { + $parse_url = parse_url($uri); + + // Add our params to the parsed uri + foreach ( $params as $k => $v ) { + if (isset($parse_url[$k])) { + $parse_url[$k] .= "&" . http_build_query($v); + } else { + $parse_url[$k] = http_build_query($v); + } + } + + // Put humpty dumpty back together + return + ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "") + . ((isset($parse_url["user"])) ? $parse_url["user"] + . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "") + . ((isset($parse_url["host"])) ? $parse_url["host"] : "") + . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "") + . ((isset($parse_url["path"])) ? $parse_url["path"] : "") + . ((isset($parse_url["query"])) ? "?" . $parse_url["query"] : "") + . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "") + ; + } + + /** + * Internal method for validating redirect URI supplied + * @param string $inputUri + * @param string $storedUri + */ + private function validateRedirectUri($inputUri, $storedUri) + { + if (!$inputUri || !$storedUri) { + return false; // if either one is missing, assume INVALID + } + return strcasecmp(substr($inputUri, 0, strlen($storedUri)), $storedUri) === 0; + } + public function getResponse() { return $this->response; } -}
\ No newline at end of file +} diff --git a/src/OAuth2/Controller/AuthorizeControllerInterface.php b/src/OAuth2/Controller/AuthorizeControllerInterface.php index 4110297..ed01b10 100644 --- a/src/OAuth2/Controller/AuthorizeControllerInterface.php +++ b/src/OAuth2/Controller/AuthorizeControllerInterface.php @@ -1,5 +1,21 @@ <?php +/** + * This controller is called when a user should be authorized + * by an authorization server. As OAuth2 does not handle + * authorization directly, this controller ensures the request is valid, but + * requires the application to determine the value of $is_authorized + * + * ex: + * > $user_id = $this->somehowDetermineUserId(); + * > $is_authorized = $this->somehowDetermineUserAuthorization(); + * > $response = $authorizeController->handleAuthorizeRequest( + * > OAuth2_Request::createFromGlobals(), + * > $is_authorized, + * > $user_id); + * > $response->send(); + * + */ interface OAuth2_Controller_AuthorizeControllerInterface extends OAuth2_Response_ProviderInterface { /** @@ -17,4 +33,4 @@ interface OAuth2_Controller_AuthorizeControllerInterface extends OAuth2_Response public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null); public function validateAuthorizeRequest(OAuth2_RequestInterface $request); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Controller/GrantController.php b/src/OAuth2/Controller/GrantController.php index ba7017b..45a08c3 100644 --- a/src/OAuth2/Controller/GrantController.php +++ b/src/OAuth2/Controller/GrantController.php @@ -1,14 +1,17 @@ <?php +/** + * @see OAuth2_Controller_GrantControllerInterface + */ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantControllerInterface { private $response; private $clientStorage; private $accessToken; private $grantTypes; - private $util; + private $scopeUtil; - public function __construct(OAuth2_Storage_ClientCredentialsInterface $clientStorage, OAuth2_ResponseType_AccessTokenInterface $accessToken, array $grantTypes = array(), $util = null) + public function __construct(OAuth2_Storage_ClientCredentialsInterface $clientStorage, OAuth2_ResponseType_AccessTokenInterface $accessToken, array $grantTypes = array(), $scopeUtil = null) { $this->clientStorage = $clientStorage; $this->accessToken = $accessToken; @@ -16,10 +19,10 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro $this->addGrantType($grantType); } - if (is_null($util)) { - $util = new OAuth2_Util(); + if (is_null($scopeUtil)) { + $scopeUtil = new OAuth2_Util_Scope(); } - $this->util = $util; + $this->scopeUtil = $scopeUtil; } public function handleGrantRequest(OAuth2_RequestInterface $request) @@ -134,8 +137,10 @@ class OAuth2_Controller_GrantController implements OAuth2_Controller_GrantContro $tokenData["scope"] = null; } + $scope = $this->scopeUtil->getScopeFromRequest($request); // Check scope, if provided - if (null != $request->query('scope') && (!is_array($tokenData) || !isset($tokenData["scope"]) || !$this->util->checkScope($request->query('scope'), $tokenData["scope"]))) { + // @TODO: ScopeStorage + if (null != $scope && (!is_array($tokenData) || !isset($tokenData["scope"]) || !$this->scopeUtil->checkScope($scope, $tokenData["scope"]))) { $this->response = new OAuth2_Response_Error(400, 'invalid_scope', 'An unsupported scope was requested.'); return null; } diff --git a/src/OAuth2/Controller/GrantControllerInterface.php b/src/OAuth2/Controller/GrantControllerInterface.php index 2bdcfd0..eec76a9 100644 --- a/src/OAuth2/Controller/GrantControllerInterface.php +++ b/src/OAuth2/Controller/GrantControllerInterface.php @@ -1,5 +1,15 @@ <?php +/** + * This controller is called when a token is being requested. + * it is called to handle all grant types the application supports. + * It also validates the client's credentials + * + * ex: + * > $response = $grantController->handleGrantRequest(OAuth2_Request::createFromGlobals()); + * > $response->send(); + * + */ interface OAuth2_Controller_GrantControllerInterface extends OAuth2_Response_ProviderInterface { /** @@ -14,4 +24,4 @@ interface OAuth2_Controller_GrantControllerInterface extends OAuth2_Response_Pro public function grantAccessToken(OAuth2_RequestInterface $request); public function getClientCredentials(OAuth2_RequestInterface $request); -}
\ No newline at end of file +} diff --git a/src/OAuth2/GrantType/AuthorizationCode.php b/src/OAuth2/GrantType/AuthorizationCode.php index 2a40b2f..218867b 100644 --- a/src/OAuth2/GrantType/AuthorizationCode.php +++ b/src/OAuth2/GrantType/AuthorizationCode.php @@ -7,6 +7,7 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O { private $storage; private $response; + private $code; public function __construct(OAuth2_Storage_AuthorizationCodeInterface $storage) { @@ -20,7 +21,7 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O public function validateRequest($request) { - if (!$request->query('code')) { + if (!$request->request('code')) { $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "code" is required'); return false; } @@ -30,7 +31,8 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O public function getTokenDataFromRequest($request) { - if (!$tokenData = $this->storage->getAuthorizationCode($request->query('code'))) { + $this->code = $request->request('code'); + if (!$tokenData = $this->storage->getAuthorizationCode($this->code)) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Authorization code doesn't exist or is invalid for the client"); return null; } @@ -40,7 +42,7 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O * @uri - http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.1.3 */ if (isset($tokenData['redirect_uri']) && $tokenData['redirect_uri']) { - if (!$request->query('redirect_uri') || urldecode($request->query('redirect_uri')) != $tokenData['redirect_uri']) { + if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != $tokenData['redirect_uri']) { $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3"); return false; } @@ -68,7 +70,10 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData) { - return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']); + $this->storage->expireAuthorizationCode($this->code); + $token = $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']); + + return $token; } public function getResponse() diff --git a/src/OAuth2/GrantType/ClientCredentials.php b/src/OAuth2/GrantType/ClientCredentials.php index 2dae255..a315717 100644 --- a/src/OAuth2/GrantType/ClientCredentials.php +++ b/src/OAuth2/GrantType/ClientCredentials.php @@ -16,7 +16,7 @@ class OAuth2_GrantType_ClientCredentials implements OAuth2_GrantTypeInterface public function getTokenDataFromRequest($request) { // the only piece to pull is the "scope" parameter - $scope = $request->headers('REQUEST_METHOD') == 'POST' ? $request->request('scope') : $request->query('scope'); + $scope = $request->server('REQUEST_METHOD') == 'POST' ? $request->request('scope') : $request->query('scope'); return array( "scope" => $scope ); @@ -37,4 +37,4 @@ class OAuth2_GrantType_ClientCredentials implements OAuth2_GrantTypeInterface $includeRefreshToken = false; return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope'], $includeRefreshToken); } -}
\ No newline at end of file +} diff --git a/src/OAuth2/GrantType/JWTBearer.php b/src/OAuth2/GrantType/JWTBearer.php index 81c139e..1572346 100644 --- a/src/OAuth2/GrantType/JWTBearer.php +++ b/src/OAuth2/GrantType/JWTBearer.php @@ -1,33 +1,54 @@ <?php /** -* +* The JWT bearer authorization grant implements JWT (JSON Web Tokens) as a grant type per the IETF draft. +* @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-4 */ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface, OAuth2_ClientAssertionTypeInterface { private $storage; private $response; - private $audience = NULL; - private $jwt = NULL; + private $audience = null; + private $jwt = null; private $jwtUtil; + /** + * Creates an instance of the JWT bearer grant type. + * + * @param OAuth2_Storage_JWTBearerInterface $storage + * A valid storage interface that implements storage hooks for the JWT bearer grant type. + * @param string $audience + * The audience to validate the token against. This is usually the full URI of the OAuth grant requests endpoint. + * @param OAuth2_Util_JWT OPTIONAL $jwtUtil + * The class used to decode, encode and verify JWTs. + */ public function __construct(OAuth2_Storage_JWTBearerInterface $storage, $audience, OAuth2_Util_JWT $jwtUtil = null) { $this->storage = $storage; $this->audience = $audience; - - if (is_null($jwtUtil)) {
- $jwtUtil = new OAuth2_Util_JWT();
- }
-
+ + if (is_null($jwtUtil)) { + $jwtUtil = new OAuth2_Util_JWT(); + } + $this->jwtUtil = $jwtUtil; } + /** + * Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant. + * @return The string identifier for grant_type. + * @see OAuth2_GrantTypeInterface::getQuerystringIdentifier() + */ public function getQuerystringIdentifier() { return 'urn:ietf:params:oauth:grant-type:jwt-bearer'; } + /** + * Validates the request by making share all GET parameters exists. + * @return TRUE if the request is valid, otherwise FALSE. + * @see OAuth2_GrantTypeInterface::validateRequest() + */ public function validateRequest($request) { if (!$request->query("assertion")) { @@ -39,6 +60,11 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re return true; } + /** + * Gets the data from the decoded JWT. + * @return Array containing the token data if the JWT can be decoded. Otherwise, NULL is returned. + * @see OAuth2_GrantTypeInterface::getTokenDataFromRequest() + */ public function getTokenDataFromRequest($request) { @@ -50,7 +76,7 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re //Decode the JWT try { - $jwt = $this->jwtUtil->decode($request->query('assertion'), NULL, FALSE); + $jwt = $this->jwtUtil->decode($request->query('assertion'), null, false); } catch (Exception $e) { $this->response = new OAuth2_Response_Error(400, 'invalid_request', "JWT is malformed"); @@ -81,25 +107,47 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re return $tokenData; } + /** + * Store a copy of the JWT + * @param string $jwt + */ private function setJWT($jwt) { $this->jwt = $jwt; } - private function getJWTParameter($parameter, $default = NULL) + /** + * Helper function to make it easier to return a JWT parameter. + * @param string $parameter + * The JWT parameter to get. + * @param mixed $default + * The value to return if the JWT parameter does not exist. + * @return mixed + * The JWT parameter. + */ + private function getJWTParameter($parameter, $default = null) { - return isset($this->jwt->$parameter) ? $this->jwt->$parameter : NULL; + return isset($this->jwt->$parameter) ? $this->jwt->$parameter : null; } + /** + * Return the data used to verify the request. For JWT bearer authorization grants, this is the 'iss' which is synonymous to the 'client_id'. + * @return array An array of the client data containing the client_id. + * @see OAuth2_ClientAssertionTypeInterface::getClientDataFromRequest() + */ public function getClientDataFromRequest(OAuth2_RequestInterface $request) { $tokenData = $this->getTokenDataFromRequest($request); if($tokenData){ - return array('client_id' => $tokenData['iss']); + return array('client_id' => $tokenData['iss'], 'subject' => $tokenData['sub']); } } + /** + * Validates the client data by checking if the client_id exists in storage. It also checks to see if the client_id is associated with a key to decode the JWT. + * @see OAuth2_ClientAssertionTypeInterface::validateClientData() + */ public function validateClientData(array $clientData) { //Get the iss's public key (http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1) @@ -110,10 +158,10 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re return null; } - $publicKey = $this->storage->getClientKey($clientData['client_id']); + $publicKey = $this->storage->getClientKey($clientData['client_id'], $clientData['subject']); if (!$publicKey) { - $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid issuer (iss) provided"); + $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided"); return null; } @@ -121,6 +169,11 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re return true; } + /** + * Validates the token data using the rules in the IETF draft. + * @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-3 + * @see OAuth2_GrantTypeInterface::validateTokenData() + */ public function validateTokenData($tokenData, array $clientData) { // Note: Scope is validated in the client class @@ -177,7 +230,7 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re //Verify the JWT try { - $this->jwtUtil->decode($this->jwt, $clientData['client_secret'], TRUE); + $this->jwtUtil->decode($this->jwt, $clientData['client_secret'], true); } catch (Exception $e) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "JWT failed signature verification"); @@ -188,19 +241,22 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re return true; } + /** + * Creates an access token that is NOT associated with a refresh token. + * If a subject (sub) the name of the user/account we are accessing data on behalf of. + * @see OAuth2_GrantTypeInterface::createAccessToken() + */ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData) { $includeRefreshToken = false; - if (isset($tokenData['sub'])) { - $user = $tokenData['sub']; - } else { - $user = $tokenData['iss']; - } - - return $accessToken->createAccessToken($clientData['client_id'], $user, $tokenData['scope'], $includeRefreshToken); + return $accessToken->createAccessToken($clientData['client_id'], $tokenData['sub'], $tokenData['scope'], $includeRefreshToken); } + /** + * Returns the response of this grant type. + * @see OAuth2_Response_ProviderInterface::getResponse() + */ public function getResponse() { return $this->response; diff --git a/src/OAuth2/GrantType/RefreshToken.php b/src/OAuth2/GrantType/RefreshToken.php index 5bc9732..2c54d79 100644 --- a/src/OAuth2/GrantType/RefreshToken.php +++ b/src/OAuth2/GrantType/RefreshToken.php @@ -25,7 +25,7 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2 public function validateRequest($request) { - if (!$request->query("refresh_token")) { + if (!$request->request("refresh_token")) { $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "refresh_token" is required'); return false; } @@ -35,7 +35,7 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2 public function getTokenDataFromRequest($request) { - if (!$stored = $this->storage->getRefreshToken($request->query("refresh_token"))) { + if (!$stored = $this->storage->getRefreshToken($request->request("refresh_token"))) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid refresh token'); return false; } @@ -82,4 +82,4 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2 { return $this->response; } -}
\ No newline at end of file +} diff --git a/src/OAuth2/GrantType/UserCredentials.php b/src/OAuth2/GrantType/UserCredentials.php index 51d05aa..0d4638e 100644 --- a/src/OAuth2/GrantType/UserCredentials.php +++ b/src/OAuth2/GrantType/UserCredentials.php @@ -20,7 +20,7 @@ class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAu public function validateRequest($request) { - if (!$request->query("password") || !$request->query("username")) { + if (!$request->request("password") || !$request->request("username")) { $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "username" and "password" required'); return false; } @@ -30,12 +30,12 @@ class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAu public function getTokenDataFromRequest($request) { - if (!$this->storage->checkUserCredentials($request->query("username"), $request->query("password"))) { + if (!$this->storage->checkUserCredentials($request->request("username"), $request->request("password"))) { $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid username and password combination'); return false; } - $tokenData = $this->storage->getUserDetails($request->query("username")); + $tokenData = $this->storage->getUserDetails($request->request("username")); // tokenData can be an empty array if (false === $tokenData || is_null($tokenData)) { diff --git a/src/OAuth2/GrantTypeInterface.php b/src/OAuth2/GrantTypeInterface.php index 0573f9d..c029a1d 100644 --- a/src/OAuth2/GrantTypeInterface.php +++ b/src/OAuth2/GrantTypeInterface.php @@ -7,4 +7,4 @@ interface OAuth2_GrantTypeInterface public function getTokenDataFromRequest($request); public function validateTokenData($tokenData, array $clientData); public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Request.php b/src/OAuth2/Request.php index 3ddc3ff..d0ed7fe 100644 --- a/src/OAuth2/Request.php +++ b/src/OAuth2/Request.php @@ -180,7 +180,7 @@ class OAuth2_Request implements OAuth2_RequestInterface * * @api */ - static public function createFromGlobals() + public static function createFromGlobals() { $class = __CLASS__; $request = new $class($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); diff --git a/src/OAuth2/RequestInterface.php b/src/OAuth2/RequestInterface.php index 0195605..61eb2d2 100644 --- a/src/OAuth2/RequestInterface.php +++ b/src/OAuth2/RequestInterface.php @@ -11,4 +11,4 @@ interface OAuth2_RequestInterface public function headers($name, $default = null); public function getAllQueryParameters(); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Response.php b/src/OAuth2/Response.php index 183cd3f..4cc9b2b 100644 --- a/src/OAuth2/Response.php +++ b/src/OAuth2/Response.php @@ -10,7 +10,7 @@ class OAuth2_Response protected $parameters = array(); protected $httpHeaders = array(); - static public $statusTexts = array( + public static $statusTexts = array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', diff --git a/src/OAuth2/Response/AuthenticationError.php b/src/OAuth2/Response/AuthenticationError.php index c3c5a62..f558401 100644 --- a/src/OAuth2/Response/AuthenticationError.php +++ b/src/OAuth2/Response/AuthenticationError.php @@ -15,4 +15,3 @@ class OAuth2_Response_AuthenticationError extends OAuth2_Response_Error $this->setHttpHeader('WWW-Authenticate', $authHeader); } } - diff --git a/src/OAuth2/Response/Error.php b/src/OAuth2/Response/Error.php index 1eaa26d..bacda74 100644 --- a/src/OAuth2/Response/Error.php +++ b/src/OAuth2/Response/Error.php @@ -46,4 +46,3 @@ class OAuth2_Response_Error extends OAuth2_Response return $this->parameters['error_uri']; } } - diff --git a/src/OAuth2/Response/ProviderInterface.php b/src/OAuth2/Response/ProviderInterface.php index b23c332..9b147c0 100644 --- a/src/OAuth2/Response/ProviderInterface.php +++ b/src/OAuth2/Response/ProviderInterface.php @@ -2,5 +2,5 @@ interface OAuth2_Response_ProviderInterface { - function getResponse(); -}
\ No newline at end of file + public function getResponse(); +} diff --git a/src/OAuth2/Response/Redirect.php b/src/OAuth2/Response/Redirect.php index 4f6a0a2..86ec291 100644 --- a/src/OAuth2/Response/Redirect.php +++ b/src/OAuth2/Response/Redirect.php @@ -42,4 +42,3 @@ class OAuth2_Response_Redirect extends OAuth2_Response } } } - diff --git a/src/OAuth2/ResponseType/AccessToken.php b/src/OAuth2/ResponseType/AccessToken.php index 37b5e54..070fbbe 100644 --- a/src/OAuth2/ResponseType/AccessToken.php +++ b/src/OAuth2/ResponseType/AccessToken.php @@ -122,4 +122,4 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken { return $this->generateAccessToken(); // let's reuse the same scheme for token generation } -}
\ No newline at end of file +} diff --git a/src/OAuth2/ResponseType/AccessTokenInterface.php b/src/OAuth2/ResponseType/AccessTokenInterface.php index 457c3b8..d362893 100644 --- a/src/OAuth2/ResponseType/AccessTokenInterface.php +++ b/src/OAuth2/ResponseType/AccessTokenInterface.php @@ -3,4 +3,4 @@ interface OAuth2_ResponseType_AccessTokenInterface extends OAuth2_ResponseTypeInterface { public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true); -}
\ No newline at end of file +} diff --git a/src/OAuth2/ResponseType/AuthorizationCode.php b/src/OAuth2/ResponseType/AuthorizationCode.php index 3f8b65c..d8a2626 100644 --- a/src/OAuth2/ResponseType/AuthorizationCode.php +++ b/src/OAuth2/ResponseType/AuthorizationCode.php @@ -3,7 +3,7 @@ /** * */ -class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseTypeInterface +class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_AuthorizationCodeInterface { private $storage; private $config; @@ -83,4 +83,4 @@ class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseTypeInterf } return substr(hash('sha512', $randomData), 0, $tokenLen); } -}
\ No newline at end of file +} diff --git a/src/OAuth2/ResponseType/AuthorizationCodeInterface.php b/src/OAuth2/ResponseType/AuthorizationCodeInterface.php index 04067c1..f139ae3 100644 --- a/src/OAuth2/ResponseType/AuthorizationCodeInterface.php +++ b/src/OAuth2/ResponseType/AuthorizationCodeInterface.php @@ -1,6 +1,6 @@ <?php -interface OAuth2_GrantType_AuthorizationCodeInterface extends OAuth2_GrantTypeInterface +interface OAuth2_ResponseType_AuthorizationCodeInterface extends OAuth2_ResponseTypeInterface { /** * @return @@ -10,4 +10,3 @@ interface OAuth2_GrantType_AuthorizationCodeInterface extends OAuth2_GrantTypeIn public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null); } - diff --git a/src/OAuth2/ResponseTypeInterface.php b/src/OAuth2/ResponseTypeInterface.php index 4eccaed..3df6821 100644 --- a/src/OAuth2/ResponseTypeInterface.php +++ b/src/OAuth2/ResponseTypeInterface.php @@ -3,4 +3,4 @@ interface OAuth2_ResponseTypeInterface { public function getAuthorizeResponse($params, $user_id = null); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Server.php b/src/OAuth2/Server.php index c67057d..51c8eb9 100644 --- a/src/OAuth2/Server.php +++ b/src/OAuth2/Server.php @@ -108,7 +108,7 @@ class OAuth2_Server implements OAuth2_Controller_AccessControllerInterface, if ($this->config['token_type'] == 'bearer') { $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_param_name token_bearer_header_name'))); $tokenType = new OAuth2_TokenType_Bearer($config); - } else if ($this->config['token_type'] == 'mac') { + } elseif ($this->config['token_type'] == 'mac') { $tokenType = new OAuth2_TokenType_MAC(); } else { throw new LogicException('unrecognized token type: '.$this->config['token_type']); diff --git a/src/OAuth2/Storage/AccessTokenInterface.php b/src/OAuth2/Storage/AccessTokenInterface.php index df3120e..d22be0d 100644 --- a/src/OAuth2/Storage/AccessTokenInterface.php +++ b/src/OAuth2/Storage/AccessTokenInterface.php @@ -45,4 +45,4 @@ interface OAuth2_Storage_AccessTokenInterface * @ingroup oauth2_section_4 */ public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Storage/AuthorizationCodeInterface.php b/src/OAuth2/Storage/AuthorizationCodeInterface.php index 4a90946..4b3ed9e 100644 --- a/src/OAuth2/Storage/AuthorizationCodeInterface.php +++ b/src/OAuth2/Storage/AuthorizationCodeInterface.php @@ -69,4 +69,17 @@ interface OAuth2_Storage_AuthorizationCodeInterface */ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null); -}
\ No newline at end of file + /** + * once an Authorization Code is used, it must be exipired + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.1.2 + * + * The client MUST NOT use the authorization code + * more than once. If an authorization code is used more than + * once, the authorization server MUST deny the request and SHOULD + * revoke (when possible) all tokens previously issued based on + * that authorization code + * + */ + public function expireAuthorizationCode($code); +} diff --git a/src/OAuth2/Storage/ClientCredentialsInterface.php b/src/OAuth2/Storage/ClientCredentialsInterface.php index 374d628..155506b 100644 --- a/src/OAuth2/Storage/ClientCredentialsInterface.php +++ b/src/OAuth2/Storage/ClientCredentialsInterface.php @@ -44,4 +44,4 @@ interface OAuth2_Storage_ClientCredentialsInterface extends OAuth2_Storage_Clien * @ingroup oauth2_section_4 */ public function checkRestrictedGrantType($client_id, $grant_type); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Storage/ClientInterface.php b/src/OAuth2/Storage/ClientInterface.php index dca3c3d..9020f43 100644 --- a/src/OAuth2/Storage/ClientInterface.php +++ b/src/OAuth2/Storage/ClientInterface.php @@ -18,4 +18,4 @@ interface OAuth2_Storage_ClientInterface * @ingroup oauth2_section_4 */ public function getClientDetails($client_id); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Storage/JWTBearerInterface.php b/src/OAuth2/Storage/JWTBearerInterface.php index 58f70fa..8ba8522 100644 --- a/src/OAuth2/Storage/JWTBearerInterface.php +++ b/src/OAuth2/Storage/JWTBearerInterface.php @@ -1,11 +1,10 @@ <?php /** - * All storage engines need to implement this interface in order to use OAuth2 server - * - * @author David Rochwerger <catch.dave@gmail.com> + * All storage engines need to implement this interface in order to use the JWT Authorization Grant. + * @author F21 */ -interface OAuth2_Storage_JWTBearerInterface extends OAuth2_Storage_ClientInterface +interface OAuth2_Storage_JWTBearerInterface { /** @@ -18,5 +17,5 @@ interface OAuth2_Storage_JWTBearerInterface extends OAuth2_Storage_ClientInterfa * STRING Return the public key for the client_id if it exists, and MUST return FALSE if it doesn't. * @endcode */ - public function getClientKey($client_id); + public function getClientKey($client_id, $subject); } diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 3d3412f..62ac28e 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -53,6 +53,11 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface $this->authorizationCodes = $authorization_codes; } + public function expireAuthorizationCode($code) + { + unset($this->authorizationCodes[$code]); + } + /* UserCredentialsInterface */ public function checkUserCredentials($username, $password) { @@ -105,7 +110,7 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface return isset($this->refreshTokens[$refresh_token]) ? $this->refreshTokens[$refresh_token] : null; } - public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = NULL) + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) { $this->refreshTokens[$refresh_token] = compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'); } @@ -132,8 +137,20 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface } /*JWTBearerInterface */ - public function getClientKey($client_id) + public function getClientKey($client_id, $subject) { - return isset($this->jwt[$client_id]) ? $this->jwt[$client_id] : null; + if (isset($this->jwt[$client_id])) { + + $jwt = $this->jwt[$client_id]; + + if ($jwt) { + + if ($jwt["subject"] == $subject) { + return $jwt["key"]; + } + } + } + + return null; } } diff --git a/src/OAuth2/Storage/Pdo.php b/src/OAuth2/Storage/Pdo.php index 49fa4cf..54035f9 100644 --- a/src/OAuth2/Storage/Pdo.php +++ b/src/OAuth2/Storage/Pdo.php @@ -44,8 +44,8 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, /* ClientCredentialsInterface */ public function checkClientCredentials($client_id, $client_secret = null) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = "%s"', $this->config['client_table'], $client_id)); - $stmt->execute(); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); + $stmt->execute(compact('client_id')); $result = $stmt->fetch(); // make this extensible @@ -54,8 +54,8 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, public function getClientDetails($client_id) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = "%s"', $this->config['client_table'], $client_id)); - $stmt->execute(); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); + $stmt->execute(compact('client_id')); return $stmt->fetch(); } @@ -74,9 +74,9 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, /* AccessTokenInterface */ public function getAccessToken($access_token) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = "%s"', $this->config['access_token_table'], $access_token)); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = :access_token', $this->config['access_token_table'])); - $token = $stmt->execute(); + $token = $stmt->execute(compact('access_token')); if ($token = $stmt->fetch()) { // convert date string back to timestamp $token['expires'] = strtotime($token['expires']); @@ -102,8 +102,8 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, /* AuthorizationCodeInterface */ public function getAuthorizationCode($code) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = "%s"', $this->config['code_table'], $code)); - $stmt->execute(); + $stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = :code', $this->config['code_table'])); + $stmt->execute(compact('code')); if ($code = $stmt->fetch()) { // convert date string back to timestamp @@ -127,6 +127,13 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); } + public function expireAuthorizationCode($code) + { + $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE authorization_code = :code', $this->config['code_table'])); + + return $stmt->execute(compact('code')); + } + /* UserCredentialsInterface */ public function checkUserCredentials($username, $password) { @@ -144,9 +151,9 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, /* RefreshTokenInterface */ public function getRefreshToken($refresh_token) { - $stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = "%s"', $this->config['refresh_token_table'], $refresh_token)); + $stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table'])); - $token = $stmt->execute(); + $token = $stmt->execute(compact('refresh_token')); if ($token = $stmt->fetch()) { // convert expires to epoch time $token['expires'] = strtotime($token['expires']); @@ -155,7 +162,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, return $token; } - public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = NULL) + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) { // convert expires to datestring $expires = date('Y-m-d H:i:s', $expires); @@ -167,9 +174,9 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, public function unsetRefreshToken($refresh_token) { - $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE refresh_token = "%s"', $this->config['refresh_token_table'], $refresh_token)); + $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table'])); - return $stmt->execute(); + return $stmt->execute(compact('refresh_token')); } // plaintext passwords are bad! Override this for your application @@ -196,11 +203,11 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface, return $stmt->execute(compact('username', 'password', 'firstName', 'lastName')); } - public function getClientKey($client_id) + public function getClientKey($client_id, $subject) { - $stmt = $this->db->prepare($sql = sprintf('SELECT public_key from %s where client_id=:client_id', $this->config['jwt_table'])); + $stmt = $this->db->prepare($sql = sprintf('SELECT public_key from %s where client_id=:client_id AND subject=:subject', $this->config['jwt_table'])); - $stmt->execute(array('client_id' => $client_id)); + $stmt->execute(array('client_id' => $client_id, 'subject' => $subject)); return $stmt->fetch(); } } diff --git a/src/OAuth2/Storage/RefreshTokenInterface.php b/src/OAuth2/Storage/RefreshTokenInterface.php index d1a32f7..1dbcdb6 100644 --- a/src/OAuth2/Storage/RefreshTokenInterface.php +++ b/src/OAuth2/Storage/RefreshTokenInterface.php @@ -75,4 +75,4 @@ interface OAuth2_Storage_RefreshTokenInterface * @ingroup oauth2_section_6 */ public function unsetRefreshToken($refresh_token); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Storage/UserCredentialsInterface.php b/src/OAuth2/Storage/UserCredentialsInterface.php index 9fff562..4ca7671 100644 --- a/src/OAuth2/Storage/UserCredentialsInterface.php +++ b/src/OAuth2/Storage/UserCredentialsInterface.php @@ -50,4 +50,4 @@ interface OAuth2_Storage_UserCredentialsInterface * */ public function getUserDetails($username); -}
\ No newline at end of file +} diff --git a/src/OAuth2/TokenType/Bearer.php b/src/OAuth2/TokenType/Bearer.php index 4b4135b..4773ad1 100644 --- a/src/OAuth2/TokenType/Bearer.php +++ b/src/OAuth2/TokenType/Bearer.php @@ -91,4 +91,4 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo { return $this->response; } -}
\ No newline at end of file +} diff --git a/src/OAuth2/TokenType/MAC.php b/src/OAuth2/TokenType/MAC.php index 6d47932..c6c8fe3 100644 --- a/src/OAuth2/TokenType/MAC.php +++ b/src/OAuth2/TokenType/MAC.php @@ -15,4 +15,4 @@ class OAuth2_TokenType_MAC implements OAuth2_TokenTypeInterface throw new LogicException("Not supported"); } -}
\ No newline at end of file +} diff --git a/src/OAuth2/TokenTypeInterface.php b/src/OAuth2/TokenTypeInterface.php index 27ca292..8960e9e 100644 --- a/src/OAuth2/TokenTypeInterface.php +++ b/src/OAuth2/TokenTypeInterface.php @@ -4,4 +4,4 @@ interface OAuth2_TokenTypeInterface { public function getTokenType(); public function getAccessTokenParameter(OAuth2_RequestInterface $request); -}
\ No newline at end of file +} diff --git a/src/OAuth2/Util.php b/src/OAuth2/Util.php deleted file mode 100644 index 9e1a12d..0000000 --- a/src/OAuth2/Util.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -/** -* Class for common functions used across the Controller classes -* This may be better off in a "BaseController" class -*/ -class OAuth2_Util -{ - /** - * Check if everything in required scope is contained in available scope. - * - * @param $required_scope - * Required scope to be check with. - * - * @return - * TRUE if everything in required scope is contained in available scope, - * and FALSE if it isn't. - * - * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-7 - * - * @ingroup oauth2_section_7 - */ - public function checkScope($required_scope, $available_scope) - { - // The required scope should match or be a subset of the available scope - if (!is_array($required_scope)) { - $required_scope = explode(' ', trim($required_scope)); - } - - if (!is_array($available_scope)) { - $available_scope = explode(' ', trim($available_scope)); - } - - return (count(array_diff($required_scope, $available_scope)) == 0); - } - - /** - * Internal method for validating redirect URI supplied - * @param string $inputUri - * @param string $storedUri - */ - public function validateRedirectUri($inputUri, $storedUri) { - if (!$inputUri || !$storedUri) { - return false; // if either one is missing, assume INVALID - } - return strcasecmp(substr($inputUri, 0, strlen($storedUri)), $storedUri) === 0; - } - - /** - * Build the absolute URI based on supplied URI and parameters. - * - * @param $uri - * An absolute URI. - * @param $params - * Parameters to be append as GET. - * - * @return - * An absolute URI with supplied parameters. - * - * @ingroup oauth2_section_4 - */ - public function buildUri($uri, $params) { - $parse_url = parse_url($uri); - - // Add our params to the parsed uri - foreach ( $params as $k => $v ) { - if (isset($parse_url[$k])) { - $parse_url[$k] .= "&" . http_build_query($v); - } else { - $parse_url[$k] = http_build_query($v); - } - } - - // Put humpty dumpty back together - return - ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "") - . ((isset($parse_url["user"])) ? $parse_url["user"] - . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "") - . ((isset($parse_url["host"])) ? $parse_url["host"] : "") - . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "") - . ((isset($parse_url["path"])) ? $parse_url["path"] : "") - . ((isset($parse_url["query"])) ? "?" . $parse_url["query"] : "") - . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "") - ; - } -}
\ No newline at end of file diff --git a/src/OAuth2/Util/Scope.php b/src/OAuth2/Util/Scope.php new file mode 100644 index 0000000..cd6a4d8 --- /dev/null +++ b/src/OAuth2/Util/Scope.php @@ -0,0 +1,41 @@ +<?php + +/** +* Class for common functions used across the Controller classes +* This may be better off in a "BaseController" class +*/ +class OAuth2_Util_Scope +{ + /** + * Check if everything in required scope is contained in available scope. + * + * @param $required_scope + * Required scope to be check with. + * + * @return + * TRUE if everything in required scope is contained in available scope, + * and FALSE if it isn't. + * + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-7 + * + * @ingroup oauth2_section_7 + */ + public function checkScope($required_scope, $available_scope) + { + // The required scope should match or be a subset of the available scope + if (!is_array($required_scope)) { + $required_scope = explode(' ', trim($required_scope)); + } + + if (!is_array($available_scope)) { + $available_scope = explode(' ', trim($available_scope)); + } + + return (count(array_diff($required_scope, $available_scope)) == 0); + } + + public function getScopeFromRequest(OAuth2_RequestInterface $request) + { + return $request->server('REQUEST_METHOD') == 'POST' ? $request->request('scope') : $request->query('scope'); + } +} diff --git a/test/OAuth2/AutoloadTest.php b/test/OAuth2/AutoloadTest.php index b7c2e10..eee1a00 100644 --- a/test/OAuth2/AutoloadTest.php +++ b/test/OAuth2/AutoloadTest.php @@ -11,4 +11,4 @@ class OAuth2_AutoloadTest extends PHPUnit_Framework_TestCase $this->assertTrue(class_exists('OAuth2_GrantType_UserCredentials')); $this->assertTrue(interface_exists('OAuth2_Storage_AccessTokenInterface')); } -}
\ No newline at end of file +} diff --git a/test/OAuth2/GrantType/AuthorizationCodeTest.php b/test/OAuth2/GrantType/AuthorizationCodeTest.php index b4af1d5..783a3ee 100644 --- a/test/OAuth2/GrantType/AuthorizationCodeTest.php +++ b/test/OAuth2/GrantType/AuthorizationCodeTest.php @@ -6,9 +6,9 @@ class OAuth2_GrantType_AuthorizationCodeTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'authorization_code'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret + $request->request['grant_type'] = 'authorization_code'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -20,10 +20,32 @@ class OAuth2_GrantType_AuthorizationCodeTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'authorization_code'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['code'] = 'InvalidCode'; // invalid authorization code + $request->request['grant_type'] = 'authorization_code'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['code'] = 'InvalidCode'; // invalid authorization code + $response = $server->handleGrantRequest($request); + + $this->assertEquals($response->getStatusCode(), 400); + $this->assertEquals($response->getParameter('error'), 'invalid_grant'); + $this->assertEquals($response->getParameter('error_description'), 'Authorization code doesn\'t exist or is invalid for the client'); + } + + public function testCodeCannotBeUsedTwice() + { + $server = $this->getTestServer(); + $request = OAuth2_Request::createFromGlobals(); + $request->request['grant_type'] = 'authorization_code'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['code'] = 'testcode'; // valid code + + $response = $server->handleGrantRequest($request); + + $this->assertEquals($response->getStatusCode(), 200); + $this->assertNotNull($response->getParameter('access_token')); + + // try to use the same code again $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -39,4 +61,4 @@ class OAuth2_GrantType_AuthorizationCodeTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/GrantType/ClientCredentialsTest.php b/test/OAuth2/GrantType/ClientCredentialsTest.php index f6934f8..93d6051 100644 --- a/test/OAuth2/GrantType/ClientCredentialsTest.php +++ b/test/OAuth2/GrantType/ClientCredentialsTest.php @@ -6,9 +6,9 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'client_credentials'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'FakeSecret'; // valid client secret + $request->request['grant_type'] = 'client_credentials'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'FakeSecret'; // valid client secret $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -20,10 +20,11 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'client_credentials'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['scope'] = 'scope'; + $request->server['REQUEST_METHOD'] = 'POST'; + $request->request['grant_type'] = 'client_credentials'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['scope'] = 'scope'; $token = $server->grantAccessToken($request); $this->assertNotNull($token); @@ -36,9 +37,9 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'client_credentials'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret + $request->request['grant_type'] = 'client_credentials'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret $token = $server->grantAccessToken($request); $this->assertNotNull($token); @@ -51,8 +52,8 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase // create with HTTP_AUTHORIZATION in header $server = $this->getTestServer(); $headers = array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('Test Client ID:TestSecret')); - $query = array('grant_type' => 'client_credentials'); - $request = new OAuth2_Request($query, array(), array(), array(), array(), $headers); + $params = array('grant_type' => 'client_credentials'); + $request = new OAuth2_Request(array(), $params, array(), array(), array(), $headers); $token = $server->grantAccessToken($request); $this->assertNotNull($token); @@ -61,8 +62,8 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase // create using PHP Authorization Globals $headers = array('PHP_AUTH_USER' => 'Test Client ID', 'PHP_AUTH_PW' => 'TestSecret'); - $query = array('grant_type' => 'client_credentials'); - $request = new OAuth2_Request($query, array(), array(), array(), array(), $headers); + $params = array('grant_type' => 'client_credentials'); + $request = new OAuth2_Request(array(), $params, array(), array(), array(), $headers); $token = $server->grantAccessToken($request); $this->assertNotNull($token); @@ -75,7 +76,7 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); $request->server['REQUEST_METHOD'] = 'POST'; - $request->query['grant_type'] = 'client_credentials'; // valid grant type + $request->request['grant_type'] = 'client_credentials'; // valid grant type $request->request['client_id'] = 'Test Client ID'; // valid client id $request->request['client_secret'] = 'TestSecret'; // valid client secret $token = $server->grantAccessToken($request); @@ -89,9 +90,9 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'client_credentials'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret + $request->request['grant_type'] = 'client_credentials'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret $token = $server->grantAccessToken($request); $this->assertNotNull($token); @@ -107,4 +108,4 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/GrantType/JWTBearerTest.php b/test/OAuth2/GrantType/JWTBearerTest.php index 1d2064c..7bae26e 100644 --- a/test/OAuth2/GrantType/JWTBearerTest.php +++ b/test/OAuth2/GrantType/JWTBearerTest.php @@ -99,7 +99,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase $request->query['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; // valid grant type //Get a future NBF - $jwt = $this->getJWT(NULL, time() + 10000); + $jwt = $this->getJWT(null, time() + 10000); $request->query['assertion'] = $jwt; $server->grantAccessToken($request); @@ -117,7 +117,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase $request->query['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; // valid grant type //Get a non timestamp nbf - $jwt = $this->getJWT(NULL, 'notatimestamp'); + $jwt = $this->getJWT(null, 'notatimestamp'); $request->query['assertion'] = $jwt; $server->grantAccessToken($request); @@ -148,24 +148,40 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); $request->query['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; // valid grant type - $request->query['assertion'] = $this->getJWT(NULL, NULL, 'bad_client_id'); + $request->query['assertion'] = $this->getJWT(null, null, null, 'bad_client_id'); $server->grantAccessToken($request); $response = $server->getResponse(); $this->assertEquals($response->getStatusCode(), 400); $this->assertEquals($response->getParameter('error'), 'invalid_grant'); - $this->assertEquals($response->getParameter('error_description'), 'Invalid issuer (iss) provided'); + $this->assertEquals($response->getParameter('error_description'), 'Invalid issuer (iss) or subject (sub) provided'); + } + + public function testBadSbuject() + { + $server = $this->getTestServer(); + $request = OAuth2_Request::createFromGlobals(); + $request->query['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; // valid grant type + $request->query['assertion'] = $this->getJWT(null, null, 'anotheruser@ourdomain.com'); + + $server->grantAccessToken($request); + $response = $server->getResponse(); + + $this->assertEquals($response->getStatusCode(), 400); + $this->assertEquals($response->getParameter('error'), 'invalid_grant'); + $this->assertEquals($response->getParameter('error_description'), 'Invalid issuer (iss) or subject (sub) provided'); } /** * Generates a JWT - * @param The expiration date. If the current time is greater than the exp, the JWT is invalid. $exp - * @param The "not before" time. If the current time is less than the nbf, the JWT is invalid. $nbf - * @param The issuer, usually the client_id. $iss + * @param $exp The expiration date. If the current time is greater than the exp, the JWT is invalid. + * @param $nbf The "not before" time. If the current time is less than the nbf, the JWT is invalid. + * @param $sub The subject we are acting on behalf of. This could be the email address of the user in the system. + * @param $iss The issuer, usually the client_id. * @return string */ - private function getJWT($exp = NULL, $nbf = NULL, $iss = 'Test Client ID') + private function getJWT($exp = null, $nbf = null, $sub = null, $iss = 'Test Client ID') { $privateKey = "-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCy2DNurVPKjODp af+6DLCeDgnbA85pInA9aUUV9/3YrrStCusIMNRJV66ELT5OOfsurfmAxmuxZrOX eiPgoAcEEVmoE96MRHRn+3XCBmRr3u6ihbYFyXFQyMvqwVb3YEubZ/Y+j9IOMp0Z HC3T6R2PYyCZ2ogO7spM0s9VL/fOTQuf4S233als4OFSWJgeRUXL9lm2hfrqWCAu NlcZ0E/akvH9wBHT0HSbVAMG+TMZ7CYXoeXSBeopIFB6XW+dHw+nZzwWOnewMHrm GJK1eZ9ro2kxkEBvi8Gnf1LJGtteI0fsZtigiIEG2CeFWDjMcAbpu2wTIC2ZUzRo yGfTVModAgMBAAECggEAdqL4q0iLNufxIqVQIEjeuFozq3eA6zkPH42/aG1TlPN/ ovKFKq/JgsWJXODuXdIUj8iUpBn7wniQVcGOHynIvagMw3Q6wu1+EqQ6X3UCFJST 1HfCYWBZrO8mZhnO6NaWEQcL0EBzZTup/sg0lkgjdcSaEbVnYBCDXibRpn7lcZGK MrsHzm6leR9tVbCB7IfT4nPmoWorkJOPcXRmE6kiRYKOKsdvX7p9leMz7ChTL7aF oCSjanO37sx3Vl1Qs8a0hdPO3slXso8/jEyDQEAgOo9SPPF1Eb6ymNIXu83ic/MC LmhFBlb7sjNXP+KCT9dMTSsw6+cYybCh+8Yq6hTgQQKBgQDesyPsWN/pzxT5qHni CngVnoRihK7JDJ2wMAdg853rLFrCn6I4u5jk9U3hDgxdq1N7vOfBtaUnGpeBHM1Q U0oUhukp81I3QyBFgUs1Bjsenu+rQi3zYELKKH/nuTRThKfUQhb5UDwkSKq69Ooc ln2grkOV+UR5nJ3e9aSZvbbXVQKBgQDNlkyso/qm06vzaRu7H6aakhgQb+nMA01z mj5SWimMjd/hv5Mrz3ts1UHqJwT7ElL12pQObDVocLfb5DgX7ttfy36q2OL1+7Pg AxY42lcmXt77olDq+PT4WMzkFbJ2XIeg7aavYIb0EoTxf1rLZNJ+g5gqAb0OCtsp 91NkdygXqQKBgG04kq/Bzt+O7oybMlvIIqkHu4nN5SM5isT0aUoFcTf74u9890S3 zP7NyEpOZ4YxFpqjRU7d1YfeJNvv0kpI7xYuLICkk3gC9frgtI/m5GvaK1Vk+cBZ P6iCQGjXjnA/qUgSuc3Aqh5pfUKAB6nJrQd3MDKHtCuowkytUPMN9nSZAoGAAIu7 nGG7EqJRPI404qRM3vAwXCrP5hjEJeBs9e8DWTovM/w8OcuNOla+nA6KdtgvnDN+ XSNwaNyTwGO0umuJ7ZEuvulggOKwMZIQhwodXR63XNere1Q54kbqFFjyZloMiiLX ZHPPJ+q8q5948D1OScOzrbjfdaAVwODFOekPFDECgYEAnGiC1mwF52JiG6RSwR7M iR1Q2bMJsUW3ofPgOX4diUvg4sihdCj4gmCoKy8Y/rhK9T4Ntti6GPFKqRtjWC7f ja52w2QQbsMYoxeGQoEG4AO92WkJW1Tmj/LufL8C3+Pun+TrSJGreIsMu955v/PF 00cvfSeAGKSc2bXBowyEQQ8= -----END PRIVATE KEY-----"; @@ -173,10 +189,15 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase $exp = time() + 1000; } + if(!$sub){ + $sub = "testuser@ourdomain.com"; + } + $params = array( 'iss' => $iss, 'exp' => $exp, 'iat' => time(), + 'sub' => $sub, 'aud' => 'http://myapp.com/oauth/auth', 'scope' => 'view_friends_list' ); @@ -184,7 +205,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase if ($nbf) { $params['nbf'] = $nbf; } - + $jwtUtil = new OAuth2_Util_JWT(); return $jwtUtil->encode($params, $privateKey); diff --git a/test/OAuth2/GrantType/RefreshTokenTest.php b/test/OAuth2/GrantType/RefreshTokenTest.php index 7c23ca7..181c463 100644 --- a/test/OAuth2/GrantType/RefreshTokenTest.php +++ b/test/OAuth2/GrantType/RefreshTokenTest.php @@ -10,9 +10,9 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase $server->addGrantType(new OAuth2_GrantType_RefreshToken($this->storage)); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'refresh_token'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret + $request->request['grant_type'] = 'refresh_token'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret $server->grantAccessToken($request); $response = $server->getResponse(); @@ -27,10 +27,10 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase $server->addGrantType(new OAuth2_GrantType_RefreshToken($this->storage)); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'refresh_token'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['refresh_token'] = 'fake-token'; // valid client secret + $request->request['grant_type'] = 'refresh_token'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['refresh_token'] = 'fake-token'; // valid client secret $server->grantAccessToken($request); $response = $server->getResponse(); @@ -45,17 +45,18 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase $server->addGrantType(new OAuth2_GrantType_RefreshToken($this->storage, array('always_issue_new_refresh_token' => true))); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'refresh_token'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['refresh_token'] = 'test-refreshtoken'; // valid client secret + $request->server['HTTP_METHOD'] = 'POST'; + $request->request['grant_type'] = 'refresh_token'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['refresh_token'] = 'test-refreshtoken'; // valid client secret $token = $server->grantAccessToken($request); $this->assertTrue(isset($token['refresh_token']), 'refresh token should always refresh'); $refresh_token = $this->storage->getRefreshToken($token['refresh_token']); $this->assertNotNull($refresh_token); $this->assertEquals($refresh_token['refresh_token'], $token['refresh_token']); - $this->assertEquals($refresh_token['client_id'], $request->query('client_id')); + $this->assertEquals($refresh_token['client_id'], $request->request('client_id')); $this->assertTrue($token['refresh_token'] != 'test-refreshtoken', 'the refresh token returned is not the one used'); $used_token = $this->storage->getRefreshToken('test-refreshtoken'); $this->assertNull($used_token, 'the refresh token used is no longer valid'); @@ -67,10 +68,10 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase $server->addGrantType(new OAuth2_GrantType_RefreshToken($this->storage, array('always_issue_new_refresh_token' => false))); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'refresh_token'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['refresh_token'] = 'test-refreshtoken'; // valid client secret + $request->request['grant_type'] = 'refresh_token'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['refresh_token'] = 'test-refreshtoken'; // valid client secret $token = $server->grantAccessToken($request); $this->assertFalse(isset($token['refresh_token']), 'refresh token should not be returned'); @@ -85,4 +86,4 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/GrantType/UserCredentialsTest.php b/test/OAuth2/GrantType/UserCredentialsTest.php index 81dfe15..463baec 100644 --- a/test/OAuth2/GrantType/UserCredentialsTest.php +++ b/test/OAuth2/GrantType/UserCredentialsTest.php @@ -6,10 +6,10 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'password'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['password'] = 'testpass'; // valid password + $request->request['grant_type'] = 'password'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['password'] = 'testpass'; // valid password $server->grantAccessToken($request); $response = $server->getResponse(); @@ -22,10 +22,10 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'password'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['username'] = 'test-username'; // valid username + $request->request['grant_type'] = 'password'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['username'] = 'test-username'; // valid username $server->grantAccessToken($request); $response = $server->getResponse(); @@ -38,11 +38,11 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'password'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['username'] = 'fake-username'; // valid username - $request->query['password'] = 'testpass'; // valid password + $request->request['grant_type'] = 'password'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['username'] = 'fake-username'; // valid username + $request->request['password'] = 'testpass'; // valid password $ret = $server->grantAccessToken($request); $response = $server->getResponse(); @@ -55,11 +55,11 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase { $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'password'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['username'] = 'test-username'; // valid username - $request->query['password'] = 'fakepass'; // valid password + $request->request['grant_type'] = 'password'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['username'] = 'test-username'; // valid username + $request->request['password'] = 'fakepass'; // valid password $ret = $server->grantAccessToken($request); $response = $server->getResponse(); @@ -76,4 +76,4 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/RequestTest.php b/test/OAuth2/RequestTest.php index a362c2f..cd21f57 100644 --- a/test/OAuth2/RequestTest.php +++ b/test/OAuth2/RequestTest.php @@ -18,7 +18,7 @@ class OAuth2_RequestTest extends PHPUnit_Framework_TestCase /*** make some valid requests ***/ // Valid Grant Request - $request->setQuery(array( + $request->setPost(array( 'grant_type' => 'authorization_code', 'client_id' => 'Test Client ID', 'client_secret' => 'TestSecret', @@ -40,4 +40,4 @@ class OAuth2_RequestTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/ResponseTest.php b/test/OAuth2/ResponseTest.php index a6d733b..7a65434 100644 --- a/test/OAuth2/ResponseTest.php +++ b/test/OAuth2/ResponseTest.php @@ -12,4 +12,4 @@ class OAuth2_ResponseTest extends PHPUnit_Framework_TestCase $string = $response->getResponseBody('xml'); $this->assertContains('<response><bar>foo</bar><oates>halland</oates></response>', $string); } -}
\ No newline at end of file +} diff --git a/test/OAuth2/Server/Access/BasicValidationTest.php b/test/OAuth2/Server/Access/BasicValidationTest.php index c599509..f7f75d5 100644 --- a/test/OAuth2/Server/Access/BasicValidationTest.php +++ b/test/OAuth2/Server/Access/BasicValidationTest.php @@ -161,4 +161,4 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/Server/Authorize/BasicValidationTest.php b/test/OAuth2/Server/Authorize/BasicValidationTest.php index acaeb92..127b9fe 100644 --- a/test/OAuth2/Server/Authorize/BasicValidationTest.php +++ b/test/OAuth2/Server/Authorize/BasicValidationTest.php @@ -104,6 +104,21 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test $this->assertEquals($query['error_description'], 'The state parameter is required'); } + public function testValidateRedirectUri() + { + $server = $this->getTestServer(); + $request = OAuth2_Request::createFromGlobals(); + $request->query['client_id'] = 'Test Client ID with Redirect Uri'; // valid client id + $request->query['redirect_uri'] = 'http://adobe.com'; // invalid redirect URI + $request->query['response_type'] = 'code'; + $response = $server->handleAuthorizeRequest($request, true); + + $this->assertEquals($response->getStatusCode(), 400); + + $this->assertEquals($response->getParameter('error'), 'redirect_uri_mismatch'); + $this->assertEquals($response->getParameter('error_description'), 'The redirect URI provided is missing or does not match'); + } + private function getTestServer($config = array()) { $storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage(); @@ -114,4 +129,4 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/Server/Authorize/CodeTest.php b/test/OAuth2/Server/Authorize/CodeTest.php index 62ed80f..c099298 100644 --- a/test/OAuth2/Server/Authorize/CodeTest.php +++ b/test/OAuth2/Server/Authorize/CodeTest.php @@ -112,4 +112,4 @@ class OAuth2_Server_Authorize_CodeTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/Server/Authorize/ImplicitTest.php b/test/OAuth2/Server/Authorize/ImplicitTest.php index 27ba933..30dcb39 100644 --- a/test/OAuth2/Server/Authorize/ImplicitTest.php +++ b/test/OAuth2/Server/Authorize/ImplicitTest.php @@ -126,4 +126,4 @@ class OAuth2_Server_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/Server/Grant/BasicValidationTest.php b/test/OAuth2/Server/Grant/BasicValidationTest.php index c834f04..b30c665 100644 --- a/test/OAuth2/Server/Grant/BasicValidationTest.php +++ b/test/OAuth2/Server/Grant/BasicValidationTest.php @@ -18,7 +18,7 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase // add the test parameters in memory $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'invalid_grant_type'; // invalid grant type + $request->request['grant_type'] = 'invalid_grant_type'; // invalid grant type $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -31,7 +31,7 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase // add the test parameters in memory $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'authorization_code'; // valid grant type + $request->request['grant_type'] = 'authorization_code'; // valid grant type $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -44,8 +44,8 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase // add the test parameters in memory $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'authorization_code'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id + $request->request['grant_type'] = 'authorization_code'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -58,9 +58,9 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase // add the test parameters in memory $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'authorization_code'; // valid grant type - $request->query['client_id'] = 'Fake Client ID'; // invalid client id - $request->query['client_secret'] = 'Fake Client Secret'; // invalid client secret + $request->request['grant_type'] = 'authorization_code'; // valid grant type + $request->request['client_id'] = 'Fake Client ID'; // invalid client id + $request->request['client_secret'] = 'Fake Client Secret'; // invalid client secret $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -68,7 +68,7 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase $this->assertEquals($response->getParameter('error_description'), 'The client credentials are invalid'); // try again with a real client ID, but an invalid secret - $request->query['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_id'] = 'Test Client ID'; // valid client id $response = $server->handleGrantRequest($request); $this->assertEquals($response->getStatusCode(), 400); @@ -81,10 +81,10 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase // add the test parameters in memory $server = $this->getTestServer(); $request = OAuth2_Request::createFromGlobals(); - $request->query['grant_type'] = 'authorization_code'; // valid grant type - $request->query['client_id'] = 'Test Client ID'; // valid client id - $request->query['client_secret'] = 'TestSecret'; // valid client secret - $request->query['code'] = 'testcode'; // valid authorization code + $request->request['grant_type'] = 'authorization_code'; // valid grant type + $request->request['client_id'] = 'Test Client ID'; // valid client id + $request->request['client_secret'] = 'TestSecret'; // valid client secret + $request->request['code'] = 'testcode'; // valid authorization code $response = $server->handleGrantRequest($request); $this->assertTrue($response instanceof OAuth2_Response); @@ -104,4 +104,4 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase return $server; } -}
\ No newline at end of file +} diff --git a/test/OAuth2/Storage/PdoTest.php b/test/OAuth2/Storage/PdoTest.php index a61748b..31c389d 100644 --- a/test/OAuth2/Storage/PdoTest.php +++ b/test/OAuth2/Storage/PdoTest.php @@ -155,4 +155,4 @@ class OAuth2_Storage_PdoTest extends PHPUnit_Framework_TestCase array($mysql), ); } -}
\ No newline at end of file +} diff --git a/test/bootstrap.php b/test/bootstrap.php index 02bcc90..ff91fc0 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -4,4 +4,4 @@ require_once(dirname(__FILE__).'/../src/OAuth2/Autoloader.php'); OAuth2_Autoloader::register(); // register test classes -OAuth2_Autoloader::register(dirname(__FILE__).'/lib');
\ No newline at end of file +OAuth2_Autoloader::register(dirname(__FILE__).'/lib'); diff --git a/test/config/storage.json b/test/config/storage.json index 48541a1..8bd35e6 100644 --- a/test/config/storage.json +++ b/test/config/storage.json @@ -11,6 +11,10 @@ "client_credentials" : { "Test Client ID": { "secret": "TestSecret" + }, + "Test Client ID with Redirect Uri": { + "secret": "TestSecret2", + "redirect_uri": "http://brentertainment.com" } }, "user_credentials" : { @@ -46,6 +50,9 @@ }, "jwt": { - "Test Client ID": "-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAstgzbq1Tyozg6Wn/ugyw ng4J2wPOaSJwPWlFFff92K60rQrrCDDUSVeuhC0+Tjn7Lq35gMZrsWazl3oj4KAH BBFZqBPejER0Z/t1wgZka97uooW2BclxUMjL6sFW92BLm2f2Po/SDjKdGRwt0+kd j2MgmdqIDu7KTNLPVS/3zk0Ln+Ett92pbODhUliYHkVFy/ZZtoX66lggLjZXGdBP 2pLx/cAR09B0m1QDBvkzGewmF6Hl0gXqKSBQel1vnR8Pp2c8Fjp3sDB65hiStXmf a6NpMZBAb4vBp39SyRrbXiNH7GbYoIiBBtgnhVg4zHAG6btsEyAtmVM0aMhn01TK HQIDAQAB -----END PUBLIC KEY----- " + "Test Client ID": { + "key": "-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAstgzbq1Tyozg6Wn/ugyw ng4J2wPOaSJwPWlFFff92K60rQrrCDDUSVeuhC0+Tjn7Lq35gMZrsWazl3oj4KAH BBFZqBPejER0Z/t1wgZka97uooW2BclxUMjL6sFW92BLm2f2Po/SDjKdGRwt0+kd j2MgmdqIDu7KTNLPVS/3zk0Ln+Ett92pbODhUliYHkVFy/ZZtoX66lggLjZXGdBP 2pLx/cAR09B0m1QDBvkzGewmF6Hl0gXqKSBQel1vnR8Pp2c8Fjp3sDB65hiStXmf a6NpMZBAb4vBp39SyRrbXiNH7GbYoIiBBtgnhVg4zHAG6btsEyAtmVM0aMhn01TK HQIDAQAB -----END PUBLIC KEY----- ", + "subject": "testuser@ourdomain.com" + } } }
\ No newline at end of file diff --git a/test/lib/OAuth2/Request/TestRequest.php b/test/lib/OAuth2/Request/TestRequest.php index 623d139..4772e28 100644 --- a/test/lib/OAuth2/Request/TestRequest.php +++ b/test/lib/OAuth2/Request/TestRequest.php @@ -43,4 +43,10 @@ class OAuth2_Request_TestRequest implements OAuth2_RequestInterface { $this->query = $query; } -}
\ No newline at end of file + + public function setPost(array $post) + { + $this->server['HTTP_METHOD'] = 'POST'; + $this->post = $post; + } +} diff --git a/test/lib/OAuth2/Storage/Bootstrap.php b/test/lib/OAuth2/Storage/Bootstrap.php index 4a214b3..354a508 100644 --- a/test/lib/OAuth2/Storage/Bootstrap.php +++ b/test/lib/OAuth2/Storage/Bootstrap.php @@ -91,4 +91,4 @@ class OAuth2_Storage_Bootstrap { return dirname(__FILE__).'/../../../config/test.sqlite'; } -}
\ No newline at end of file +} |
