/** * @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(); }