139 lines
6.3 KiB
PHP
139 lines
6.3 KiB
PHP
<?php
|
|
|
|
use oauth2\services\IApiEndpointService;
|
|
use oauth2\services\ITokenService;
|
|
use oauth2\BearerAccessTokenAuthorizationHeaderParser;
|
|
use oauth2\OAuth2Protocol;
|
|
use oauth2\responses\OAuth2WWWAuthenticateErrorResponse;
|
|
use utils\services\ILogService;
|
|
use oauth2\exceptions\OAuth2ResourceServerException;
|
|
use oauth2\exceptions\InvalidGrantTypeException;
|
|
use utils\services\ICheckPointService;
|
|
use oauth2\IResourceServerContext;
|
|
/**
|
|
* Class OAuth2BearerAccessTokenRequestValidator
|
|
* this class implements the logic to Accessing to Protected Resources
|
|
* http://tools.ietf.org/html/rfc6750
|
|
* http://tools.ietf.org/html/rfc6749#section-7
|
|
*/
|
|
class OAuth2BearerAccessTokenRequestValidator {
|
|
|
|
private $api_endpoint_service;
|
|
private $token_service;
|
|
private $log_service;
|
|
private $checkpoint_service;
|
|
private $resource_server_context;
|
|
|
|
public function __construct(IResourceServerContext $resource_server_context,IApiEndpointService $api_endpoint_service, ITokenService $token_service, ILogService $log_service, ICheckPointService $checkpoint_service){
|
|
$this->api_endpoint_service = $api_endpoint_service;
|
|
$this->token_service = $token_service;
|
|
$this->log_service = $log_service;
|
|
$this->checkpoint_service = $checkpoint_service;
|
|
$this->resource_server_context = $resource_server_context;
|
|
}
|
|
|
|
/**
|
|
* @param $route
|
|
* @param $request
|
|
*/
|
|
public function filter($route, $request)
|
|
{
|
|
|
|
try{
|
|
|
|
$url = $route->getPath();
|
|
$method = $request->getMethod();
|
|
$endpoint = $this->api_endpoint_service->getApiEndpointByUrlAndMethod($url, $method);
|
|
$realm = $request->getHost();
|
|
|
|
//api endpoint must be registered on db and active
|
|
if(is_null($endpoint) || !$endpoint->isActive()){
|
|
throw new OAuth2ResourceServerException(400,OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest,sprintf('API endpoint does not exits! (%s:%s)',$url,$method));
|
|
}
|
|
|
|
//check first http basic auth header
|
|
$auth_header = Request::header('Authorization');
|
|
if(!is_null($auth_header) && !empty($auth_header))
|
|
$access_token_value = BearerAccessTokenAuthorizationHeaderParser::getInstance()->parse($auth_header);
|
|
else{
|
|
// http://tools.ietf.org/html/rfc6750#section-2- 2
|
|
// if access token is not on authorization header check on POST/GET params
|
|
$access_token_value = Input::get(OAuth2Protocol::OAuth2Protocol_AccessToken, '');
|
|
}
|
|
|
|
if(is_null($access_token_value) || empty($access_token_value))
|
|
{
|
|
//if access token value is not set, then error
|
|
throw new OAuth2ResourceServerException(400,OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest,'missing access token');
|
|
}
|
|
|
|
// get access token from service
|
|
$access_token = $this->token_service->getAccessToken($access_token_value);
|
|
|
|
//check token audience
|
|
$audience = explode(' ',$access_token->getAudience());
|
|
if((!in_array($realm,$audience)))
|
|
throw new OAuth2ResourceServerException(401,OAuth2Protocol::OAuth2Protocol_Error_InvalidToken,'access token audience does not match');
|
|
|
|
//check scopes
|
|
$endpoint_scopes = explode(' ',$endpoint->getScope());
|
|
$token_scopes = explode(' ',$access_token->getScope());
|
|
//check token available scopes vs. endpoint scopes
|
|
if (count(array_intersect($endpoint_scopes, $token_scopes)) === 0)
|
|
{
|
|
$this->log_service->error_msg(sprintf('access token scopes (%s) does not allow to access to api url %s , needed scopes %s',$access_token->getScope(),$url,implode(' OR ',$endpoint_scopes) ));
|
|
|
|
throw new OAuth2ResourceServerException(403,OAuth2Protocol::OAuth2Protocol_Error_InsufficientScope,
|
|
'the request requires higher privileges than provided by the access token',
|
|
implode(' ',$endpoint_scopes));
|
|
}
|
|
|
|
$this->resource_server_context->setAuthorizationContext(array(
|
|
'access_token' => $access_token_value,
|
|
'expires_in' => $access_token->getRemainingLifetime(),
|
|
'client_id' => $access_token->getClientId(),
|
|
'scope' => $access_token->getScope()
|
|
));
|
|
|
|
}
|
|
catch(OAuth2ResourceServerException $ex1){
|
|
$this->log_service->error($ex1);
|
|
$this->checkpoint_service->trackException($ex1);
|
|
$response = new OAuth2WWWAuthenticateErrorResponse($realm,
|
|
$ex1->getError(),
|
|
$ex1->getErrorDescription(),
|
|
$ex1->getScope(),
|
|
$ex1->getHttpCode()
|
|
);
|
|
$http_response = Response::json($response->getContent(), $response->getHttpCode());
|
|
$http_response->header('WWW-Authenticate',$response->getWWWAuthenticateHeaderValue());
|
|
return $http_response;
|
|
}
|
|
catch(InvalidGrantTypeException $ex2){
|
|
$this->log_service->error($ex2);
|
|
$this->checkpoint_service->trackException($ex2);
|
|
$response = new OAuth2WWWAuthenticateErrorResponse($realm,
|
|
OAuth2Protocol::OAuth2Protocol_Error_InvalidToken,
|
|
'the access token provided is expired, revoked, malformed, or invalid for other reasons.',
|
|
null,
|
|
401
|
|
);
|
|
$http_response = Response::json($response->getContent(), $response->getHttpCode());
|
|
$http_response->header('WWW-Authenticate',$response->getWWWAuthenticateHeaderValue());
|
|
return $http_response;
|
|
}
|
|
catch(Exception $ex){
|
|
$this->log_service->error($ex);
|
|
$this->checkpoint_service->trackException($ex);
|
|
$response = new OAuth2WWWAuthenticateErrorResponse($realm,
|
|
OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest,
|
|
'invalid request',
|
|
null,
|
|
400
|
|
);
|
|
$http_response = Response::json($response->getContent(), $response->getHttpCode());
|
|
$http_response->header('WWW-Authenticate',$response->getWWWAuthenticateHeaderValue());
|
|
return $http_response;
|
|
}
|
|
}
|
|
} |