Added Origin checking for implicit clients (JS)
* Added origin checking at resource server * Added to instrospection endpoint allowed origin/return uris info Change-Id: I85086408a981c4f003503cf03f9e32542a8698ab
This commit is contained in:
parent
cf9a093ed2
commit
8c15d9888d
@ -409,8 +409,16 @@ class TestSeeder extends Seeder {
|
||||
//add uris
|
||||
ClientAuthorizedUri::create(
|
||||
array(
|
||||
'uri'=>'https://www.test.com/oauth2',
|
||||
'client_id'=>$client_confidential->id
|
||||
'uri' => 'https://www.test.com/oauth2',
|
||||
'client_id' => $client_confidential->id
|
||||
)
|
||||
);
|
||||
|
||||
//add uris
|
||||
ClientAllowedOrigin::create(
|
||||
array(
|
||||
'allowed_origin' => 'https://www.test.com/oauth2',
|
||||
'client_id' => $client_confidential->id
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -10,6 +10,8 @@ use oauth2\exceptions\OAuth2ResourceServerException;
|
||||
use oauth2\exceptions\InvalidGrantTypeException;
|
||||
use utils\services\ICheckPointService;
|
||||
use oauth2\IResourceServerContext;
|
||||
use oauth2\services\IClientService;
|
||||
use oauth2\models\IClient;
|
||||
/**
|
||||
* Class OAuth2BearerAccessTokenRequestValidator
|
||||
* this class implements the logic to Accessing to Protected Resources
|
||||
@ -21,13 +23,13 @@ class OAuth2BearerAccessTokenRequestValidator {
|
||||
|
||||
protected function getHeaders()
|
||||
{
|
||||
$headers = array();
|
||||
$headers = array();
|
||||
|
||||
if (function_exists('getallheaders')) {
|
||||
// @codeCoverageIgnoreStart
|
||||
foreach(getallheaders() as $name => $value){
|
||||
$headers[strtolower($name)] = $value;
|
||||
}
|
||||
foreach(getallheaders() as $name => $value){
|
||||
$headers[strtolower($name)] = $value;
|
||||
}
|
||||
} else {
|
||||
// @codeCoverageIgnoreEnd
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
@ -37,10 +39,10 @@ class OAuth2BearerAccessTokenRequestValidator {
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Request::header() as $name => $value){
|
||||
if(!array_key_exists($name,$headers))
|
||||
$headers[strtolower($name)] = $value[0];
|
||||
}
|
||||
foreach(Request::header() as $name => $value){
|
||||
if(!array_key_exists($name,$headers))
|
||||
$headers[strtolower($name)] = $value[0];
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
@ -51,14 +53,16 @@ class OAuth2BearerAccessTokenRequestValidator {
|
||||
private $checkpoint_service;
|
||||
private $resource_server_context;
|
||||
private $headers;
|
||||
private $client_service;
|
||||
|
||||
public function __construct(IResourceServerContext $resource_server_context,IApiEndpointService $api_endpoint_service, ITokenService $token_service, ILogService $log_service, ICheckPointService $checkpoint_service){
|
||||
public function __construct(IResourceServerContext $resource_server_context, IApiEndpointService $api_endpoint_service, ITokenService $token_service, ILogService $log_service, ICheckPointService $checkpoint_service, IClientService $client_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;
|
||||
$this->headers = $this->getHeaders();
|
||||
$this->client_service = $client_service;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,6 +78,8 @@ class OAuth2BearerAccessTokenRequestValidator {
|
||||
}
|
||||
$method = $request->getMethod();
|
||||
$realm = $request->getHost();
|
||||
// http://tools.ietf.org/id/draft-abarth-origin-03.html
|
||||
$origin = $request->headers->has('Origin') ? $request->headers->get('Origin') : null;
|
||||
|
||||
try{
|
||||
$endpoint = $this->api_endpoint_service->getApiEndpointByUrlAndMethod($url, $method);
|
||||
@ -107,6 +113,18 @@ class OAuth2BearerAccessTokenRequestValidator {
|
||||
if((!in_array($realm,$audience)))
|
||||
throw new OAuth2ResourceServerException(401,OAuth2Protocol::OAuth2Protocol_Error_InvalidToken,'access token audience does not match');
|
||||
|
||||
//check client existence
|
||||
$client_id = $access_token->getClientId();
|
||||
$client = $this->client_service->getClientById($client_id);
|
||||
|
||||
if(is_null($client))
|
||||
throw new OAuth2ResourceServerException(400,OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, 'invalid client');
|
||||
|
||||
//if js client , then check if the origin is allowed ....
|
||||
if($client->getApplicationType() == IClient::ApplicationType_JS_Client){
|
||||
if(!$client->isOriginAllowed($origin))
|
||||
throw new OAuth2ResourceServerException(403,OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient, 'invalid origin');
|
||||
}
|
||||
//check scopes
|
||||
$endpoint_scopes = explode(' ',$endpoint->getScope());
|
||||
$token_scopes = explode(' ',$access_token->getScope());
|
||||
@ -123,7 +141,7 @@ class OAuth2BearerAccessTokenRequestValidator {
|
||||
$context = array(
|
||||
'access_token' => $access_token_value,
|
||||
'expires_in' => $access_token->getRemainingLifetime(),
|
||||
'client_id' => $access_token->getClientId(),
|
||||
'client_id' => $client_id,
|
||||
'scope' => $access_token->getScope()
|
||||
);
|
||||
|
||||
|
@ -126,7 +126,17 @@ class ValidateBearerTokenGrantType extends AbstractGrantType
|
||||
throw new BearerTokenDisclosureAttemptException($this->current_client_id,sprintf('access token current audience does not match with current request ip %s', $current_ip));
|
||||
}
|
||||
|
||||
return new OAuth2AccessTokenValidationResponse( $token_value, $access_token->getScope(), $access_token->getAudience(), $access_token->getClientId(), $access_token->getRemainingLifetime(), $access_token->getUserId());
|
||||
$allowed_origins = array();
|
||||
foreach($this->current_client->getClientAllowedOrigins() as $origin){
|
||||
array_push($allowed_origins, $origin->allowed_origin);
|
||||
}
|
||||
|
||||
$allowed_urls = array();
|
||||
foreach($this->current_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(), $allowed_urls, $allowed_origins);
|
||||
}
|
||||
catch(InvalidAccessTokenException $ex1){
|
||||
$this->log_service->error($ex1);
|
||||
|
@ -6,7 +6,7 @@ use oauth2\OAuth2Protocol;
|
||||
|
||||
class OAuth2AccessTokenValidationResponse extends OAuth2DirectResponse {
|
||||
|
||||
public function __construct($access_token,$scope, $audience,$client_id,$expires_in, $user_id = null)
|
||||
public function __construct($access_token,$scope, $audience,$client_id,$expires_in, $user_id = null, $allowed_urls = array(), $allowed_origins = array())
|
||||
{
|
||||
// Successful Responses: A server receiving a valid request MUST send a
|
||||
// response with an HTTP status code of 200.
|
||||
@ -21,5 +21,13 @@ class OAuth2AccessTokenValidationResponse extends OAuth2DirectResponse {
|
||||
if(!is_null($user_id)){
|
||||
$this[OAuth2Protocol::OAuth2Protocol_UserId] = $user_id;
|
||||
}
|
||||
|
||||
if(count($allowed_urls)){
|
||||
$this['allowed_return_uris'] = implode(' ', $allowed_urls);
|
||||
}
|
||||
|
||||
if(count($allowed_origins)){
|
||||
$this['allowed_origins'] = implode(' ', $allowed_origins);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,19 +7,19 @@ class Client extends BaseModelEloquent implements IClient {
|
||||
|
||||
protected $table = 'oauth2_client';
|
||||
|
||||
public function getActiveAttribute(){
|
||||
return (bool) $this->attributes['active'];
|
||||
}
|
||||
public function getActiveAttribute(){
|
||||
return (bool) $this->attributes['active'];
|
||||
}
|
||||
|
||||
public function getIdAttribute(){
|
||||
return (int) $this->attributes['id'];
|
||||
}
|
||||
public function getIdAttribute(){
|
||||
return (int) $this->attributes['id'];
|
||||
}
|
||||
|
||||
public function getLockedAttribute(){
|
||||
return (int) $this->attributes['locked'];
|
||||
}
|
||||
public function getLockedAttribute(){
|
||||
return (int) $this->attributes['locked'];
|
||||
}
|
||||
|
||||
public function access_tokens()
|
||||
public function access_tokens()
|
||||
{
|
||||
return $this->hasMany('AccessToken');
|
||||
}
|
||||
@ -202,13 +202,13 @@ class Client extends BaseModelEloquent implements IClient {
|
||||
switch($this->application_type){
|
||||
case IClient::ApplicationType_JS_Client:
|
||||
return 'Client Side (JS)';
|
||||
break;
|
||||
break;
|
||||
case IClient::ApplicationType_Service:
|
||||
return 'Service Account';
|
||||
break;
|
||||
break;
|
||||
case IClient::ApplicationType_Web_App:
|
||||
return 'Web Server Application';
|
||||
break;
|
||||
break;
|
||||
}
|
||||
throw new Exception('Invalid Application Type');
|
||||
}
|
||||
@ -232,13 +232,13 @@ class Client extends BaseModelEloquent implements IClient {
|
||||
}
|
||||
if($parts['scheme']!=='https')
|
||||
return false;
|
||||
$origin_without_port = sprinf("%sː//%s",$parts['scheme'],$parts['host']);
|
||||
$origin_without_port = $parts['scheme'].'://'.$parts['host'];
|
||||
$client_allowed_origin = $this->allowed_origins()->where('allowed_origin','=',$origin_without_port)->first();
|
||||
if(!is_null($client_allowed_origin)) return true;
|
||||
if(isset($parts['port'])){
|
||||
$origin_with_port = sprinf("%sː//%s:%s",$parts['scheme'],$parts['host'],$parts['port']);
|
||||
$client_authorized_uri = $this->allowed_origins()->where('allowed_origin','=',$origin_with_port)->first();;
|
||||
return !is_null($client_authorized_uri);
|
||||
$origin_with_port = $parts['scheme'].'://'.$parts['host'].':'.$parts['port'];
|
||||
$client_authorized_uri = $this->allowed_origins()->where('allowed_origin','=',$origin_with_port)->first();;
|
||||
return !is_null($client_authorized_uri);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ class CORSMiddleware {
|
||||
|
||||
if (!$this->checkOrigin($request)) {
|
||||
$response->headers->set('Access-Control-Allow-Origin', 'null');
|
||||
$response->setStatusCode(403);
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user