openstackid/app/libs/oauth2/grant_types/ImplicitGrantType.php

206 lines
8.7 KiB
PHP

<?php
namespace oauth2\grant_types;
use oauth2\exceptions\AccessDeniedException;
use oauth2\exceptions\InvalidClientException;
use oauth2\exceptions\InvalidOAuth2Request;
use oauth2\exceptions\ScopeNotAllowedException;
use oauth2\exceptions\OAuth2GenericException;
use oauth2\exceptions\InvalidApplicationType;
use oauth2\exceptions\LockedClientException;
use oauth2\exceptions\UnsupportedResponseTypeException;
use oauth2\exceptions\UriNotAllowedException;
use oauth2\models\IClient;
use oauth2\OAuth2Protocol;
use oauth2\requests\OAuth2Request;
use oauth2\responses\OAuth2AccessTokenFragmentResponse;
use oauth2\services\IApiScopeService;
use oauth2\services\IClientService;
use oauth2\services\ITokenService;
use oauth2\services\IMementoOAuth2AuthenticationRequestService;
use oauth2\strategies\IOAuth2AuthenticationStrategy;
use ReflectionClass;
use utils\services\IAuthService;
use utils\services\ILogService;
use oauth2\services\IUserConsentService;
/**
* Class ImplicitGrantType
* http://tools.ietf.org/html/rfc6749#section-4.2
* The implicit grant type is used to obtain access tokens (it does not
* support the issuance of refresh tokens) and is optimized for public
* clients known to operate a particular redirection URI. These clients
* are typically implemented in a browser using a scripting language
* such as JavaScript.
* Since this is a redirection-based flow, the client must be capable of
* interacting with the resource owner's user-agent (typically a web
* browser) and capable of receiving incoming requests (via redirection)
* from the authorization server.
* Unlike the authorization code grant type, in which the client makes
* separate requests for authorization and for an access token, the
* client receives the access token as the result of the authorization
* request.
* The implicit grant type does not include client authentication, and
* relies on the presence of the resource owner and the registration of
* the redirection URI. Because the access token is encoded into the
* redirection URI, it may be exposed to the resource owner and other
* applications residing on the same device.
* @package oauth2\grant_types
*/
class ImplicitGrantType extends AbstractGrantType
{
private $auth_service;
private $auth_strategy;
private $scope_service;
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;
}
/** Given an OAuth2Request, returns true if it can handle it, false otherwise
* @param OAuth2Request $request
* @return boolean
*/
public function canHandle(OAuth2Request $request)
{
$reflector = new ReflectionClass($request);
$class_name = $reflector->getName();
return
($class_name == 'oauth2\requests\OAuth2AuthorizationRequest' && $request->isValid() && $request->getResponseType() === $this->getResponseType());
}
/** get grant type response type
* @return mixed
*/
public function getResponseType()
{
return OAuth2Protocol::OAuth2Protocol_ResponseType_Token;
}
/**
* @param OAuth2Request $request
* @return mixed|OAuth2AccessTokenFragmentResponse
* @throws \oauth2\exceptions\UnsupportedResponseTypeException
* @throws \oauth2\exceptions\LockedClientException
* @throws \oauth2\exceptions\InvalidClientException
* @throws \oauth2\exceptions\ScopeNotAllowedException
* @throws \oauth2\exceptions\OAuth2GenericException
* @throws \oauth2\exceptions\InvalidApplicationType
* @throws \oauth2\exceptions\AccessDeniedException
* @throws \oauth2\exceptions\UriNotAllowedException
* @throws \oauth2\exceptions\InvalidOAuth2Request
*/
public function handle(OAuth2Request $request)
{
$reflector = new ReflectionClass($request);
$class_name = $reflector->getName();
if ($class_name == 'oauth2\requests\OAuth2AuthorizationRequest') {
$client_id = $request->getClientId();
$response_type = $request->getResponseType();
if ($response_type !== $this->getResponseType())
throw new UnsupportedResponseTypeException(sprintf("response_type %s", $response_type));
$client = $this->client_service->getClientById($client_id);
if (is_null($client))
throw new InvalidClientException($client_id, sprintf("client_id %s", $client_id));
if (!$client->isActive() || $client->isLocked()) {
throw new LockedClientException($client,sprintf('client id %s',$client));
}
//check client type
// only public clients could use this grant type
if ($client->getApplicationType() != IClient::ApplicationType_JS_Client)
throw new InvalidApplicationType($client_id,sprintf('client id %s client type must be JS CLIENT',$client_id));
//check redirect uri
$redirect_uri = $request->getRedirectUri();
if (!$client->isUriAllowed($redirect_uri))
throw new UriNotAllowedException(sprintf("redirect_to %s", $redirect_uri));
//check requested scope
$scope = $request->getScope();
if (is_null($scope) || empty($scope) || !$client->isScopeAllowed($scope))
throw new ScopeNotAllowedException(sprintf("scope %s", $scope));
$state = $request->getState();
//check user logged
if (!$this->auth_service->isUserLogged()) {
$this->memento_service->saveCurrentAuthorizationRequest();
return $this->auth_strategy->doLogin($this->memento_service->getCurrentAuthorizationRequest());
}
$approval_prompt = $request->getApprovalPrompt();
$user = $this->auth_service->getCurrentUser();
if(is_null($user))
throw new OAuth2GenericException("Invalid Current User");
//validate authorization
//check for former user consents
$authorization_response = $this->auth_service->getUserAuthorizationResponse();
$former_user_consent = $this->user_consent_service->get($user->getId(),$client->getId(),$scope);
if( !(!is_null($former_user_consent) && $approval_prompt == OAuth2Protocol::OAuth2Protocol_Approval_Prompt_Auto)){
if ($authorization_response == IAuthService::AuthorizationResponse_None) {
$this->memento_service->saveCurrentAuthorizationRequest();
return $this->auth_strategy->doConsent($this->memento_service->getCurrentAuthorizationRequest());
}
else if ($authorization_response == IAuthService::AuthorizationResponse_DenyOnce) {
throw new AccessDeniedException;
}
//save possitive consent
if(is_null($former_user_consent))
$this->user_consent_service->add($user->getId(),$client->getId(),$scope);
}
// build current audience ...
$audience = $this->scope_service->getStrAudienceByScopeNames(explode(' ',$scope));
//build access token
$access_token = $this->token_service->createAccessTokenFromParams($client_id,$scope, $audience,$user->getId());
//clear saved data ...
$this->memento_service->clearCurrentRequest();
$this->auth_service->clearUserAuthorizationResponse();
return new OAuth2AccessTokenFragmentResponse($redirect_uri, $access_token->getValue(), $access_token->getLifetime(), $scope, $state);
}
throw new InvalidOAuth2Request;
}
public function completeFlow(OAuth2Request $request){
throw new InvalidOAuth2Request('not implemented!');
}
/**
* get grant type
* @return mixed
*/
public function getType()
{
return OAuth2Protocol::OAuth2Protocol_GrantType_Implicit;
}
/**
* @param OAuth2Request $request
* @return mixed|void
* @throws \oauth2\exceptions\InvalidOAuth2Request
*/
public function buildTokenRequest(OAuth2Request $request)
{
throw new InvalidOAuth2Request('not implemented!');
}
}