/**
  * @param OAuth2Request $request
  * @return mixed|OAuth2AccessTokenResponse|void
  * @throws \oauth2\exceptions\ScopeNotAllowedException
  * @throws \oauth2\exceptions\InvalidOAuth2Request
  * @throws \oauth2\exceptions\InvalidApplicationType
  * @throws \oauth2\exceptions\InvalidGrantTypeException
  */
 public function completeFlow(OAuth2Request $request)
 {
     $reflector = new ReflectionClass($request);
     $class_name = $reflector->getName();
     if ($class_name == 'oauth2\\requests\\OAuth2AccessTokenRequestClientCredentials') {
         if ($request->getGrantType() != $this->getType()) {
             throw new InvalidGrantTypeException();
         }
         parent::completeFlow($request);
         //only confidential clients could use this grant type
         if ($this->current_client->getApplicationType() != IClient::ApplicationType_Service) {
             throw new InvalidApplicationType($this->current_client_id, sprintf('client id %s client type must be SERVICE', $this->current_client_id));
         }
         //check requested scope
         $scope = $request->getScope();
         if (is_null($scope) || empty($scope) || !$this->current_client->isScopeAllowed($scope)) {
             throw new ScopeNotAllowedException(sprintf("scope %s", $scope));
         }
         // build current audience ...
         $audience = $this->scope_service->getStrAudienceByScopeNames(explode(' ', $scope));
         //build access token
         $access_token = $this->token_service->createAccessTokenFromParams($this->current_client_id, $scope, $audience);
         $response = new OAuth2AccessTokenResponse($access_token->getValue(), $access_token->getLifetime(), null);
         return $response;
     }
     throw new InvalidOAuth2Request();
 }
 public function __construct(IApiScopeService $scope_service, IClientService $client_service, ITokenService $token_service, IAuthService $auth_service, IMementoOAuth2AuthenticationRequestService $memento_service, IOAuth2AuthenticationStrategy $auth_strategy, ILogService $log_service, IUserConsentService $user_consent_service)
 {
     parent::__construct($client_service, $token_service, $log_service);
     $this->user_consent_service = $user_consent_service;
     $this->scope_service = $scope_service;
     $this->auth_service = $auth_service;
     $this->memento_service = $memento_service;
     $this->auth_strategy = $auth_strategy;
 }
 /**
  * Access Token issuance using a refresh token
  * The authorization server MUST:
  *
  *  o  require client authentication for confidential clients or for any
  *     client that was issued client credentials (or with other
  *     authentication requirements),
  *  o  authenticate the client if client authentication is included and
  *     ensure that the refresh token was issued to the authenticated
  *     client, and
  * o  validate the refresh token.
  *
  * @param OAuth2Request $request
  * @return mixed|OAuth2AccessTokenResponse|void
  * @throws \oauth2\exceptions\UseRefreshTokenException
  * @throws \oauth2\exceptions\InvalidOAuth2Request
  * @throws \oauth2\exceptions\InvalidApplicationType
  * @throws \oauth2\exceptions\InvalidGrantTypeException
  */
 public function completeFlow(OAuth2Request $request)
 {
     $reflector = new ReflectionClass($request);
     $class_name = $reflector->getName();
     if ($class_name == 'oauth2\\requests\\OAuth2RefreshAccessTokenRequest') {
         parent::completeFlow($request);
         if ($this->current_client->getApplicationType() != IClient::ApplicationType_Web_App) {
             throw new InvalidApplicationType($this->current_client_id, sprintf('client id %s client type must be WEB_APPLICATION', $this->current_client_id));
         }
         if (!$this->current_client->use_refresh_token) {
             throw new UseRefreshTokenException("current client id %s could not use refresh tokens", $this->current_client_id);
         }
         $refresh_token_value = $request->getRefreshToken();
         $scope = $request->getScope();
         $refresh_token = $this->token_service->getRefreshToken($refresh_token_value);
         if (is_null($refresh_token)) {
             throw new InvalidGrantTypeException(sprintf("refresh token %s does not exists!", $refresh_token_value));
         }
         if ($refresh_token->getClientId() !== $this->current_client->client_id) {
             throw new InvalidGrantTypeException(sprintf("refresh token %s does not belongs to client %s", $refresh_token_value, $this->current_client->client_id));
         }
         $access_token = $this->token_service->createAccessTokenFromRefreshToken($refresh_token, $scope);
         $new_refresh_token = null;
         /*
          * the authorization server could employ refresh token
          * rotation in which a new refresh token is issued with every access
          * token refresh response.  The previous refresh token is invalidated
          * but retained by the authorization server.  If a refresh token is
          * compromised and subsequently used by both the attacker and the
          * legitimate client, one of them will present an invalidated refresh
          * token, which will inform the authorization server of the breach.
          */
         if ($this->current_client->rotate_refresh_token) {
             $this->token_service->invalidateRefreshToken($refresh_token_value);
             $new_refresh_token = $this->token_service->createRefreshToken($access_token);
         }
         $response = new OAuth2AccessTokenResponse($access_token->getValue(), $access_token->getLifetime(), !is_null($new_refresh_token) ? $new_refresh_token->getValue() : $scope);
         return $response;
     }
     throw new InvalidOAuth2Request();
 }
 /**
  * @param OAuth2Request $request
  * @return mixed|OAuth2AccessTokenValidationResponse|void
  * @throws \oauth2\exceptions\InvalidOAuth2Request
  * @throws \oauth2\exceptions\LockedClientException
  * @throws \oauth2\exceptions\InvalidApplicationType
  * @throws \oauth2\exceptions\BearerTokenDisclosureAttemptException
  */
 public function completeFlow(OAuth2Request $request)
 {
     $reflector = new ReflectionClass($request);
     $class_name = $reflector->getName();
     if ($class_name == 'oauth2\\requests\\OAuth2AccessTokenValidationRequest') {
         parent::completeFlow($request);
         $token_value = $request->getToken();
         try {
             $access_token = $this->token_service->getAccessToken($token_value);
             if (is_null($access_token)) {
                 throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $token_value));
             }
             if (!$this->current_client->isResourceServerClient()) {
                 // if current client is not a resource server, then we could only access to our own tokens
                 if ($access_token->getClientId() !== $this->current_client_id) {
                     throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('access token %s does not belongs to client id %s', $token_value, $this->current_client_id));
                 }
             } else {
                 // current client is a resource server, validate client type (must be confidential)
                 if ($this->current_client->getClientType() !== IClient::ClientType_Confidential) {
                     throw new InvalidApplicationType($this->current_client_id, 'resource server client is not of confidential type!');
                 }
                 //validate resource server IP address
                 $current_ip = IPHelper::getUserIp();
                 $resource_server = $this->current_client->getResourceServer();
                 //check if resource server is active
                 if (!$resource_server->active) {
                     throw new LockedClientException($this->current_client_id, 'resource server is disabled!');
                 }
                 //check resource server ip address
                 if ($current_ip !== $resource_server->ip) {
                     throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('resource server ip (%s) differs from current request ip %s', $resource_server->ip, $current_ip));
                 }
                 // check if current ip belongs to a registered resource server audience
                 if (!$this->token_service->checkAccessTokenAudience($access_token, $current_ip)) {
                     throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('access token current audience does not match with current request ip %s', $current_ip));
                 }
             }
             $allowed_origins = array();
             $allowed_urls = array();
             $issued_client = $this->client_service->getClientById($access_token->getClientId());
             if (is_null($issued_client)) {
                 throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('access token %s does not belongs to client id %s', $token_value, $access_token->getClientId()));
             }
             foreach ($issued_client->getClientAllowedOrigins() as $origin) {
                 array_push($allowed_origins, $origin->allowed_origin);
             }
             foreach ($issued_client->getClientRegisteredUris() as $url) {
                 array_push($allowed_urls, $url->uri);
             }
             return new OAuth2AccessTokenValidationResponse($token_value, $access_token->getScope(), $access_token->getAudience(), $access_token->getClientId(), $access_token->getRemainingLifetime(), $access_token->getUserId(), $issued_client->getApplicationType(), $allowed_urls, $allowed_origins);
         } catch (InvalidAccessTokenException $ex1) {
             $this->log_service->error($ex1);
             throw new BearerTokenDisclosureAttemptException($this->current_client_id, $ex1->getMessage());
         } catch (InvalidGrantTypeException $ex2) {
             $this->log_service->error($ex2);
             throw new BearerTokenDisclosureAttemptException($this->current_client_id, $ex2->getMessage());
         }
     }
     throw new InvalidOAuth2Request();
 }
 /**
  * Implements last request processing for Authorization code (Access Token Request processing)
  * http://tools.ietf.org/html/rfc6749#section-4.1.3 and
  * http://tools.ietf.org/html/rfc6749#section-4.1.4
  * @param OAuth2Request $request
  * @return OAuth2AccessTokenResponse
  * @throws \oauth2\exceptions\InvalidAuthorizationCodeException
  * @throws \oauth2\exceptions\ExpiredAuthorizationCodeException
  * @throws \Exception
  * @throws \oauth2\exceptions\InvalidClientException
  * @throws \oauth2\exceptions\UnAuthorizedClientException
  * @throws \oauth2\exceptions\UriNotAllowedException
  */
 public function completeFlow(OAuth2Request $request)
 {
     $reflector = new ReflectionClass($request);
     $class_name = $reflector->getName();
     try {
         if ($class_name == 'oauth2\\requests\\OAuth2AccessTokenRequestAuthCode') {
             parent::completeFlow($request);
             //only confidential clients could use this grant type
             if ($this->current_client->getApplicationType() != IClient::ApplicationType_Web_App) {
                 throw new InvalidApplicationType($this->current_client_id, sprintf("client id %s - Application type must be WEB_APPLICATION", $this->current_client_id));
             }
             $current_redirect_uri = $request->getRedirectUri();
             //verify redirect uri
             if (!$this->current_client->isUriAllowed($current_redirect_uri)) {
                 throw new UriNotAllowedException(sprintf('redirect url %s is not allowed for cliend id %s', $current_redirect_uri, $this->current_client_id));
             }
             $code = $request->getCode();
             // verify that the authorization code is valid
             // 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.  The authorization code is bound to
             // the client identifier and redirection URI.
             $auth_code = $this->token_service->getAuthorizationCode($code);
             $client_id = $auth_code->getClientId();
             //ensure that the authorization code was issued to the authenticated
             //confidential client, or if the client is public, ensure that the
             //code was issued to "client_id" in the request
             if ($client_id != $this->current_client_id) {
                 throw new InvalidRedeemAuthCodeException($this->current_client_id, sprintf("auth code was issued for another client id!."));
             }
             // ensure that the "redirect_uri" parameter is present if the
             // "redirect_uri" parameter was included in the initial authorization
             // and if included ensure that their values are identical.
             $redirect_uri = $auth_code->getRedirectUri();
             if (!empty($redirect_uri) && $redirect_uri !== $current_redirect_uri) {
                 throw new UriNotAllowedException();
             }
             $access_token = $this->token_service->createAccessToken($auth_code, $current_redirect_uri);
             $refresh_token = $access_token->getRefreshToken();
             $response = new OAuth2AccessTokenResponse($access_token->getValue(), $access_token->getLifetime(), !is_null($refresh_token) ? $refresh_token->getValue() : null);
             return $response;
         }
     } catch (InvalidAuthorizationCodeException $ex) {
         $this->log_service->error($ex);
         throw new InvalidRedeemAuthCodeException($this->current_client_id, $ex->getMessage());
     }
     throw new InvalidOAuth2Request();
 }
 public function completeFlow(OAuth2Request $request)
 {
     $reflector = new ReflectionClass($request);
     $class_name = $reflector->getName();
     if ($class_name == 'oauth2\\requests\\OAuth2TokenRevocationRequest') {
         parent::completeFlow($request);
         $token_value = $request->getToken();
         $token_hint = $request->getTokenHint();
         try {
             if (!is_null($token_hint) && !empty($token_hint)) {
                 //we have been provided with a token hint...
                 switch ($token_hint) {
                     case OAuth2Protocol::OAuth2Protocol_AccessToken:
                         //check ownership
                         $access_token = $this->token_service->getAccessToken($token_value);
                         if (is_null($access_token)) {
                             throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $token_value));
                         }
                         if ($access_token->getClientId() !== $this->current_client_id) {
                             throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('access token %s does not belongs to client id %s', $token_value, $this->current_client_id));
                         }
                         $this->token_service->revokeAccessToken($token_value, false);
                         break;
                     case OAuth2Protocol::OAuth2Protocol_RefreshToken:
                         //check ownership
                         $refresh_token = $this->token_service->getRefreshToken($token_value);
                         if ($refresh_token->getClientId() !== $this->current_client_id) {
                             throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('refresh token %s does not belongs to client id %s', $token_value, $this->current_client_id));
                         }
                         $this->token_service->revokeRefreshToken($token_value, false);
                         break;
                 }
             } else {
                 /*
                  * no token hint given :(
                  * if the server is unable to locate the token using
                  * the given hint, it MUST extend its search across all of its
                  * supported token types.
                  */
                 //check and handle access token first ..
                 try {
                     //check ownership
                     $access_token = $this->token_service->getAccessToken($token_value);
                     if (is_null($access_token)) {
                         throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $token_value));
                     }
                     if ($access_token->getClientId() !== $this->current_client_id) {
                         throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('access token %s does not belongs to client id %s', $token_value, $this->current_client_id));
                     }
                     $this->token_service->revokeAccessToken($token_value, false);
                 } catch (UnAuthorizedClientException $ex1) {
                     $this->log_service->error($ex1);
                     throw $ex1;
                 } catch (Exception $ex) {
                     $this->log_service->warning($ex);
                     //access token was not found, check refresh token
                     //check ownership
                     $refresh_token = $this->token_service->getRefreshToken($token_value);
                     if ($refresh_token->getClientId() !== $this->current_client_id) {
                         throw new BearerTokenDisclosureAttemptException($this->current_client_id, sprintf('refresh token %s does not belongs to client id %s', $token_value, $this->current_client_id));
                     }
                     $this->token_service->revokeRefreshToken($token_value, false);
                 }
             }
             return new OAuth2TokenRevocationResponse();
         } catch (InvalidGrantTypeException $ex) {
             throw new BearerTokenDisclosureAttemptException($this->current_client_id, $ex->getMessage());
         }
     }
     throw new InvalidOAuth2Request();
 }