private function _handleAuthorize(IResourceOwner $resourceOwner, array $get) { try { $clientId = Utils::getParameter($get, 'client_id'); $responseType = Utils::getParameter($get, 'response_type'); $redirectUri = Utils::getParameter($get, 'redirect_uri'); // FIXME: scope can never be empty, if the client requests no scope we should have a default scope! $scope = new Scope(Utils::getParameter($get, 'scope')); $state = Utils::getParameter($get, 'state'); if (NULL === $clientId) { throw new ResourceOwnerException('client_id missing'); } if (NULL === $responseType) { throw new ResourceOwnerException('response_type missing'); } $client = $this->_storage->getClient($clientId); if (FALSE === $client) { throw new ResourceOwnerException('client not registered'); } if (NULL !== $redirectUri) { if ($client['redirect_uri'] !== $redirectUri) { throw new ResourceOwnerException('specified redirect_uri not the same as registered redirect_uri'); } } // we need to make sure the client can only request the grant types belonging to its profile $allowedClientProfiles = array("web_application" => array("code"), "native_application" => array("token", "code"), "user_agent_based_application" => array("token")); if (!in_array($responseType, $allowedClientProfiles[$client['type']])) { throw new ClientException("unsupported_response_type", "response_type not supported by client profile", $client, $state); } if (!$scope->isSubsetOf(new Scope($client['allowed_scope']))) { throw new ClientException("invalid_scope", "not authorized to request this scope", $client, $state); } $this->_storage->updateResourceOwner($resourceOwner); $approvedScope = $this->_storage->getApprovalByResourceOwnerId($clientId, $resourceOwner->getId()); if (FALSE === $approvedScope || FALSE === $scope->isSubsetOf(new Scope($approvedScope['scope']))) { $ar = new AuthorizeResult(AuthorizeResult::ASK_APPROVAL); $ar->setClient(ClientRegistration::fromArray($client)); $ar->setScope($scope); return $ar; } else { if ("token" === $responseType) { // implicit grant // FIXME: return existing access token if it exists for this exact client, resource owner and scope? $accessToken = Utils::randomHex(16); $this->_storage->storeAccessToken($accessToken, time(), $clientId, $resourceOwner->getId(), $scope->getScope(), $this->_config->getValue('accessTokenExpiry')); $token = array("access_token" => $accessToken, "expires_in" => $this->_config->getValue('accessTokenExpiry'), "token_type" => "bearer"); $s = $scope->getScope(); if (!empty($s)) { $token += array("scope" => $s); } if (NULL !== $state) { $token += array("state" => $state); } $ar = new AuthorizeResult(AuthorizeResult::REDIRECT); $ar->setRedirectUri(new Uri($client['redirect_uri'] . "#" . http_build_query($token))); return $ar; } else { // authorization code grant $authorizationCode = Utils::randomHex(16); $this->_storage->storeAuthorizationCode($authorizationCode, $resourceOwner->getId(), time(), $clientId, $redirectUri, $scope->getScope()); $token = array("code" => $authorizationCode); if (NULL !== $state) { $token += array("state" => $state); } $ar = new AuthorizeResult(AuthorizeResult::REDIRECT); $separator = FALSE === strpos($client['redirect_uri'], "?") ? "?" : "&"; $ar->setRedirectUri(new Uri($client['redirect_uri'] . $separator . http_build_query($token))); return $ar; } } } catch (ScopeException $e) { throw new ClientException("invalid_scope", "malformed scope", $client, $state); } }
public function testFailingScope() { $s = new Scope("read write delete"); $this->assertFalse($s->isSubsetOf(new Scope("read write update"))); $this->assertFalse($s->hasScope(new Scope("foo"))); }
private function _handleToken(array $post, $user = NULL, $pass = NULL) { // exchange authorization code for access token $grantType = Utils::getParameter($post, 'grant_type'); $code = Utils::getParameter($post, 'code'); $redirectUri = Utils::getParameter($post, 'redirect_uri'); $refreshToken = Utils::getParameter($post, 'refresh_token'); $token = Utils::getParameter($post, 'token'); $clientId = Utils::getParameter($post, 'client_id'); $scope = Utils::getParameter($post, 'scope'); if (NULL !== $user && !empty($user) && NULL !== $pass && !empty($pass)) { // client provided authentication, it MUST be valid now... $client = $this->_storage->getClient($user); if (FALSE === $client) { throw new TokenException("invalid_client", "client authentication failed"); } // check pass if ($pass !== $client['secret']) { throw new TokenException("invalid_client", "client authentication failed"); } // if client_id in POST is set, it must match the user if (NULL !== $clientId && $clientId !== $user) { throw new TokenException("invalid_grant", "client_id inconsistency: authenticating user must match POST body client_id"); } $hasAuthenticated = TRUE; } else { // client provided no authentication, client_id must be in POST body if (NULL === $clientId || empty($clientId)) { throw new TokenException("invalid_request", "no client authentication used nor client_id POST parameter"); } $client = $this->_storage->getClient($clientId); if (FALSE === $client) { throw new TokenException("invalid_client", "client identity could not be established"); } $hasAuthenticated = FALSE; } if ("user_agent_based_application" === $client['type']) { throw new TokenException("unauthorized_client", "this client type is not allowed to use the token endpoint"); } if ("web_application" === $client['type'] && !$hasAuthenticated) { // web_application type MUST have authenticated throw new TokenException("invalid_client", "client authentication failed"); } if (NULL === $grantType) { throw new TokenException("invalid_request", "the grant_type parameter is missing"); } switch ($grantType) { case "authorization_code": if (NULL === $code) { throw new TokenException("invalid_request", "the code parameter is missing"); } // If the redirect_uri was present in the authorize request, it MUST also be there // in the token request. If it was not there in authorize request, it MUST NOT be // there in the token request (this is not explicit in the spec!) $result = $this->_storage->getAuthorizationCode($client['id'], $code, $redirectUri); if (FALSE === $result) { throw new TokenException("invalid_grant", "the authorization code was not found"); } if (time() > $result['issue_time'] + 600) { throw new TokenException("invalid_grant", "the authorization code expired"); } // we MUST be able to delete the authorization code, otherwise it was used before if (FALSE === $this->_storage->deleteAuthorizationCode($client['id'], $code, $redirectUri)) { // check to prevent deletion race condition throw new TokenException("invalid_grant", "this authorization code grant was already used"); } $approval = $this->_storage->getApprovalByResourceOwnerId($client['id'], $result['resource_owner_id']); $token = array(); $token['access_token'] = Utils::randomHex(16); $token['expires_in'] = intval($this->_config->getValue('accessTokenExpiry')); // we always grant the scope the user authorized, no further restrictions here... // FIXME: the merging of authorized scopes in the authorize function is a bit of a mess! // we should deal with that there and come up with a good solution... $token['scope'] = $result['scope']; $token['refresh_token'] = $approval['refresh_token']; $token['token_type'] = "bearer"; $this->_storage->storeAccessToken($token['access_token'], time(), $client['id'], $result['resource_owner_id'], $token['scope'], $token['expires_in']); break; case "refresh_token": if (NULL === $refreshToken) { throw new TokenException("invalid_request", "the refresh_token parameter is missing"); } $result = $this->_storage->getApprovalByRefreshToken($client['id'], $refreshToken); if (FALSE === $result) { throw new TokenException("invalid_grant", "the refresh_token was not found"); } $token = array(); $token['access_token'] = Utils::randomHex(16); $token['expires_in'] = intval($this->_config->getValue('accessTokenExpiry')); if (NULL !== $scope) { // the client wants to obtain a specific scope $requestedScope = new Scope($scope); $authorizedScope = new Scope($result['scope']); if ($requestedScope->isSubsetOf($authorizedScope)) { // if it is a subset of the authorized scope we honor that $token['scope'] = $requestedScope->getScope(); } else { // if not the client gets the authorized scope $token['scope'] = $result['scope']; } } else { $token['scope'] = $result['scope']; } $token['token_type'] = "bearer"; $this->_storage->storeAccessToken($token['access_token'], time(), $client['id'], $result['resource_owner_id'], $token['scope'], $token['expires_in']); break; default: throw new TokenException("unsupported_grant_type", "the requested grant type is not supported"); } return $token; }