Implements: blueprint openid-oauth2-implicit-client-flow

Change-Id: Iee3c9412a3f75a4aba5421e8c5f881a60b396df0
This commit is contained in:
smarcet 2014-01-06 18:07:55 -03:00
parent 946b7dd8ae
commit ad1844984e
32 changed files with 556 additions and 79 deletions

10
.gitignore vendored
View File

@ -5,8 +5,8 @@ composer.lock
.DS_Storeapp/storage
/app/storage/*
.idea/*
app/config/dev/database.php
app/config/testing/database.php
app/config/local/database.php
app/config/production/database.php
app/config/staging/database.php
app/config/dev/*
app/config/testing/*
app/config/local/*
app/config/production/*
app/config/staging/*

View File

@ -310,17 +310,40 @@ class TestSeeder extends Seeder {
)
);
$client = Client::where('app_name','=','oauth2_test_app')->first();
Client::create(
array(
'app_name' => 'oauth2_test_app_public',
'app_description' => 'oauth2_test_app_public',
'app_logo' => null,
'client_id' => 'Jiz87D8/Vcvr6fvQbH4HyNgwKlfSyQ3x.openstack.client',
'client_secret' => 'ITK/6Y5N7kOtGKhg',
'client_type' => IClient::ClientType_Public,
'user_id' => $user->id,
'rotate_refresh_token' => true,
'use_refresh_token' => true
)
);
$client_confidential = Client::where('app_name','=','oauth2_test_app')->first();
$client_public = Client::where('app_name','=','oauth2_test_app_public')->first();
//attach scopes
$scopes = ApiScope::get();
foreach($scopes as $scope){
$client->scopes()->attach($scope->id);
$client_confidential->scopes()->attach($scope->id);
$client_public->scopes()->attach($scope->id);
}
//add uris
ClientAuthorizedUri::create(
array(
'uri'=>'https://www.test.com/oauth2',
'client_id'=>$client->id
'client_id'=>$client_confidential->id
)
);
ClientAuthorizedUri::create(
array(
'uri'=>'https://www.test.com/oauth2',
'client_id'=>$client_public->id
)
);

View File

@ -11,7 +11,7 @@ interface IOAuth2Protocol {
* @param OAuth2Request $request
* @return mixed
*/
public function authorize(OAuth2Request $request);
public function authorize(OAuth2Request $request = null);
/**
* Token Endpoint
@ -19,7 +19,7 @@ interface IOAuth2Protocol {
* @param OAuth2Request $request
* @return mixed
*/
public function token(OAuth2Request $request);
public function token(OAuth2Request $request = null);
/**
* Get all available grant types set on the protocol

View File

@ -21,6 +21,7 @@ use oauth2\exceptions\UnsupportedResponseTypeException;
use oauth2\exceptions\UriNotAllowedException;
use oauth2\grant_types\AuthorizationCodeGrantType;
use oauth2\grant_types\ImplicitGrantType;
use oauth2\grant_types\ValidateBearerTokenGrantType;
use oauth2\grant_types\RefreshBearerTokenGrantType;
@ -34,6 +35,7 @@ use oauth2\services\ITokenService;
use oauth2\services\IApiScopeService;
use oauth2\strategies\IOAuth2AuthenticationStrategy;
use oauth2\strategies\OAuth2IndirectErrorResponseFactoryMethod;
use utils\services\IAuthService;
use utils\services\ICheckPointService;
@ -50,15 +52,15 @@ use utils\services\ILogService;
class OAuth2Protocol implements IOAuth2Protocol
{
const OAuth2Protocol_GrantType_AuthCode = 'authorization_code';
const OAuth2Protocol_GrantType_Implicit = 'implicit';
const OAuth2Protocol_GrantType_AuthCode = 'authorization_code';
const OAuth2Protocol_GrantType_Implicit = 'implicit';
const OAuth2Protocol_GrantType_ResourceOwner_Password = 'password';
const OAuth2Protocol_GrantType_ClientCredentials = 'client_credentials';
const OAuth2Protocol_GrantType_RefreshToken = 'refresh_token';
const OAuth2Protocol_ResponseType_Code = 'code';
const OAuth2Protocol_ResponseType_Token = 'token';
const OAuth2Protocol_ResponseType = "response_type";
const OAuth2Protocol_ClientId = "client_id";
const OAuth2Protocol_GrantType_ClientCredentials = 'client_credentials';
const OAuth2Protocol_GrantType_RefreshToken = 'refresh_token';
const OAuth2Protocol_ResponseType_Code = 'code';
const OAuth2Protocol_ResponseType_Token = 'token';
const OAuth2Protocol_ResponseType = 'response_type';
const OAuth2Protocol_ClientId = 'client_id';
const OAuth2Protocol_ClientSecret = "client_secret";
const OAuth2Protocol_AccessToken = "access_token";
const OAuth2Protocol_Token = "token";
@ -121,11 +123,13 @@ class OAuth2Protocol implements IOAuth2Protocol
//todo: add dynamic creation logic (configure grants types from db)
$authorization_code_grant_type = new AuthorizationCodeGrantType($scope_service,$client_service, $token_service, $auth_service, $memento_service, $auth_strategy, $log_service);
$authorization_code_grant_type = new AuthorizationCodeGrantType($scope_service, $client_service, $token_service, $auth_service, $memento_service, $auth_strategy, $log_service);
$implicit_grant_type = new ImplicitGrantType($scope_service, $client_service, $token_service, $auth_service, $memento_service, $auth_strategy, $log_service);
$validate_bearer_token_grant_type = new ValidateBearerTokenGrantType($client_service, $token_service, $log_service);
$refresh_bearer_token_grant_type = new RefreshBearerTokenGrantType($client_service,$token_service,$log_service);
$this->grant_types[$authorization_code_grant_type->getType()] = $authorization_code_grant_type;
$this->grant_types[$implicit_grant_type->getType()] = $implicit_grant_type;
$this->grant_types[$validate_bearer_token_grant_type->getType()] = $validate_bearer_token_grant_type;
$this->grant_types[$refresh_bearer_token_grant_type->getType()] = $refresh_bearer_token_grant_type;
@ -143,7 +147,7 @@ class OAuth2Protocol implements IOAuth2Protocol
* @throws \Exception
* @throws exceptions\UriNotAllowedException
*/
public function authorize(OAuth2Request $request)
public function authorize(OAuth2Request $request = null)
{
try {
if (is_null($request) || !$request->isValid())
@ -152,12 +156,11 @@ class OAuth2Protocol implements IOAuth2Protocol
} catch (InvalidOAuth2Request $ex1) {
$this->log_service->error($ex1);
$this->checkpoint_service->trackException($ex1);
$redirect_uri = $this->validateRedirectUri($request);
if (is_null($redirect_uri))
throw $ex1;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request, OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, $redirect_uri);
} catch (UnsupportedResponseTypeException $ex2) {
$this->log_service->error($ex2);
$this->checkpoint_service->trackException($ex2);
@ -166,7 +169,7 @@ class OAuth2Protocol implements IOAuth2Protocol
if (is_null($redirect_uri))
throw $ex2;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_UnsupportedResponseType, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request, OAuth2Protocol::OAuth2Protocol_Error_UnsupportedResponseType, $redirect_uri);
} catch (InvalidClientException $ex3) {
$this->log_service->error($ex3);
$this->checkpoint_service->trackException($ex3);
@ -175,12 +178,13 @@ class OAuth2Protocol implements IOAuth2Protocol
if (is_null($redirect_uri))
throw $ex3;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request,OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient, $redirect_uri);
} catch (UriNotAllowedException $ex4) {
$this->log_service->error($ex4);
$this->checkpoint_service->trackException($ex4);
throw $ex4;
} catch (ScopeNotAllowedException $ex5) {
$this->log_service->error($ex5);
$this->checkpoint_service->trackException($ex5);
@ -188,7 +192,7 @@ class OAuth2Protocol implements IOAuth2Protocol
if (is_null($redirect_uri))
throw $ex5;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_InvalidScope, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request,OAuth2Protocol::OAuth2Protocol_Error_InvalidScope, $redirect_uri);
} catch (UnAuthorizedClientException $ex6) {
$this->log_service->error($ex6);
$this->checkpoint_service->trackException($ex6);
@ -197,7 +201,7 @@ class OAuth2Protocol implements IOAuth2Protocol
if (is_null($redirect_uri))
throw $ex6;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request,OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient, $redirect_uri);
} catch (AccessDeniedException $ex7) {
$this->log_service->error($ex7);
$this->checkpoint_service->trackException($ex7);
@ -206,7 +210,7 @@ class OAuth2Protocol implements IOAuth2Protocol
if (is_null($redirect_uri))
throw $ex7;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_AccessDenied, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request,OAuth2Protocol::OAuth2Protocol_Error_AccessDenied, $redirect_uri);
} catch (OAuth2GenericException $ex8) {
$this->log_service->error($ex8);
$this->checkpoint_service->trackException($ex8);
@ -215,7 +219,7 @@ class OAuth2Protocol implements IOAuth2Protocol
if (is_null($redirect_uri))
throw $ex8;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_ServerError, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request,OAuth2Protocol::OAuth2Protocol_Error_ServerError, $redirect_uri);
} catch (Exception $ex) {
$this->log_service->error($ex);
$this->checkpoint_service->trackException($ex);
@ -224,19 +228,21 @@ class OAuth2Protocol implements IOAuth2Protocol
if (is_null($redirect_uri))
throw $ex;
return new OAuth2IndirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_ServerError, $redirect_uri);
return OAuth2IndirectErrorResponseFactoryMethod::buildResponse($request,OAuth2Protocol::OAuth2Protocol_Error_ServerError, $redirect_uri);
}
}
private function validateRedirectUri(OAuth2Request $request)
private function validateRedirectUri(OAuth2Request $request = null)
{
if(is_null($request))
return null;
$redirect_uri = $request->getRedirectUri();
if (is_null($redirect_uri))
return null;
$client_id = $request->getClientId();
if (is_null($client_id))
return null;
$client = $this->client_service->getClientByIdentifier($client_id);
$client = $this->client_service->getClientById($client_id);
if (is_null($client))
return null;
if (!$client->isUriAllowed($redirect_uri))
@ -248,7 +254,7 @@ class OAuth2Protocol implements IOAuth2Protocol
* @param OAuth2Request $request
* @return OAuth2DirectErrorResponse|void
*/
public function token(OAuth2Request $request)
public function token(OAuth2Request $request = null)
{
try {
if (is_null($request) || !$request->isValid())

View File

@ -10,6 +10,12 @@ use oauth2\exceptions\InvalidGrantTypeException;
/**
* Class AuthorizationEndpoint
* Authorization Endpoint Implementation
* The authorization endpoint is used to interact with the resource
* owner and obtain an authorization grant. The authorization server
* MUST first verify the identity of the resource owner. The way in
* which the authorization server authenticates the resource owner
* (e.g., username and password login, session cookies) is beyond the
* scope of this specification.
* http://tools.ietf.org/html/rfc6749#section-3.1
* @package oauth2\endpoints
*/

View File

@ -11,6 +11,10 @@ use oauth2\requests\OAuth2Request;
/**
* Class TokenEndpoint
* Token Endpoint Implementation
* The token endpoint is used by the client to obtain an access token by
* presenting its authorization grant or refresh token. The token
* endpoint is used with every authorization grant except for the
* implicit grant type (since an access token is issued directly).
* http://tools.ietf.org/html/rfc6749#section-3.2
* @package oauth2\endpoints
*/

View File

@ -33,13 +33,17 @@ use utils\services\ILogService;
/**
* Class AuthorizationCodeGrantType
* Authorization Code Grant Implementation
* The authorization code grant type is used to obtain both access
* tokens and refresh tokens and is optimized for confidential clients.
* 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.
* http://tools.ietf.org/html/rfc6749#section-4.1
* @package oauth2\grant_types
*/
class AuthorizationCodeGrantType extends AbstractGrantType
{
private $auth_service;
private $auth_strategy;
private $memento_service;
@ -59,7 +63,7 @@ class AuthorizationCodeGrantType extends AbstractGrantType
$reflector = new ReflectionClass($request);
$class_name = $reflector->getName();
return
($class_name == 'oauth2\requests\OAuth2AuthorizationRequest' && $request->isValid()) ||
($class_name == 'oauth2\requests\OAuth2AuthorizationRequest' && $request->isValid() && $request->getResponseType() === $this->getResponseType()) ||
($class_name == 'oauth2\requests\OAuth2TokenRequest' && $request->isValid() && $request->getGrantType() === $this->getType());
}
@ -88,7 +92,8 @@ class AuthorizationCodeGrantType extends AbstractGrantType
$reflector = new ReflectionClass($request);
$class_name = $reflector->getName();
if ($class_name == 'oauth2\requests\OAuth2AuthorizationRequest') {
$client_id = $request->getClientId();
$client_id = $request->getClientId();
$response_type = $request->getResponseType();
@ -126,26 +131,14 @@ class AuthorizationCodeGrantType extends AbstractGrantType
} else if ($authorization_response === IAuthService::AuthorizationResponse_DenyOnce) {
throw new AccessDeniedException;
}
$response = new OAuth2AuthorizationResponse();
// build current audience ...
$audiences = $this->scope_service->getAudienceByScopeNames(explode(' ',$scope));
$audience = '';
foreach($audiences as $resource_server_host => $ip){
$audience = $audience . $resource_server_host .' ';
}
$audience = trim($audience);
$audience = $this->scope_service->getStrAudienceByScopeNames(explode(' ',$scope));
$auth_code = $this->token_service->createAuthorizationCode($client_id, $scope, $audience, $redirect_uri);
if (is_null($auth_code))
throw new OAuth2GenericException("Invalid Auth Code");
$response->setAuthorizationCode($auth_code->getValue());
$response->setReturnTo($redirect_uri);
//if state is present, return it on response
if (!is_null($state))
$response->setState($state);
return $response;
return new OAuth2AuthorizationResponse($redirect_uri, $auth_code->getValue(), $state);
}
throw new InvalidOAuth2Request;
}

View File

@ -0,0 +1,166 @@
<?php
namespace oauth2\grant_types;
use oauth2\exceptions\AccessDeniedException;
use oauth2\exceptions\InvalidClientException;
use oauth2\exceptions\InvalidOAuth2Request;
use oauth2\exceptions\ScopeNotAllowedException;
use oauth2\exceptions\UnAuthorizedClientException;
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;
/**
* 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)
{
parent::__construct($client_service, $token_service, $log_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;
}
/** defines entry point for first request processing
* @param OAuth2Request $request
* @return mixed
*/
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(sprintf("client_id %s", $client_id));
//check client type
if ($client->getClientType() !== IClient::ClientType_Public)
throw new UnAuthorizedClientException();
//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("redirect_to %s", $redirect_uri));
$state = $request->getState();
//check user logged
if (!$this->auth_service->isUserLogged()) {
$this->memento_service->saveCurrentAuthorizationRequest();
return $this->auth_strategy->doLogin($this->memento_service->getCurrentAuthorizationRequest());
}
//validate authorization
$authorization_response = $this->auth_service->getUserAuthorizationResponse();
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;
}
// build current audience ...
$audience = $this->scope_service->getStrAudienceByScopeNames(explode(' ',$scope));
//build access token
$access_token = $this->token_service->createAccessTokenFromParams($scope, $client_id, $audience);
return new OAuth2AccessTokenFragmentResponse($redirect_uri, $access_token->getValue(), $access_token->getLifetime(), $scope, $state);
}
throw new InvalidOAuth2Request;
}
/**
* get grant type
* @return mixed
*/
public function getType()
{
return OAuth2Protocol::OAuth2Protocol_GrantType_Implicit;
}
/** builds specific Token request
* @param OAuth2Request $request
* @return mixed
*/
public function buildTokenRequest(OAuth2Request $request)
{
throw new \Exception('not implemented!!');
}
}

View File

@ -28,6 +28,17 @@ class AccessToken extends Token {
return $instance;
}
public static function createFromParams($scope, $client_id, $audience,$lifetime){
$instance = new self();
$instance->value = Rand::getString($instance->len,null,true);
$instance->scope = $scope;
$instance->client_id = $client_id;
$instance->auth_code = null;
$instance->audience = $audience;
$instance->lifetime = $lifetime;
return $instance;
}
public static function createFromRefreshToken(RefreshToken $refresh_token,$scope = null, $lifetime = 3600){
$instance = new self();
$instance->value = Rand::getString($instance->len,null,true);

View File

@ -7,6 +7,23 @@ use Zend\Math\Rand;
/**
* Class RefreshToken
* http://tools.ietf.org/html/rfc6749#section-1.5
*
* The refresh token is also a secret bound to the client identifier and
* client instance that originally requested the authorization; the
* refresh token also represents the original resource owner grant.
* This is ensured by the authorization process as follows:
* 1. The resource owner and user agent safely deliver the
* authorization "code" to the client instance in the first place.
* 2. The client uses it immediately in secure transport-level
* communications to the authorization server and then securely
* stores the long-lived refresh token.
* 3. The client always uses the refresh token in secure transport-
* level communications to the authorization server to get an access
* token (and optionally roll over the refresh token).
* So, as long as the confidentiality of the particular token can be
* ensured by the client, a refresh token can also be used as an
* alternative means to authenticate the client instance itself.
* from http://tools.ietf.org/html/rfc6819#section-3.3
* @package oauth2\models
*/
class RefreshToken extends Token {

View File

@ -56,9 +56,6 @@ class OAuth2AuthorizationRequest extends OAuth2Request {
if(is_null($this->getRedirectUri()))
return false;
if(is_null($this->getScope()))
return false;
return true;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace oauth2\responses;
use oauth2\OAuth2Protocol;
class OAuth2AccessTokenFragmentResponse extends OAuth2IndirectFragmentResponse {
public function __construct($return_to, $access_token, $expires_in, $scope=null, $state=null)
{
parent::__construct();
$this->setReturnTo($return_to);
$this[OAuth2Protocol::OAuth2Protocol_AccessToken] = $access_token;
$this[OAuth2Protocol::OAuth2Protocol_AccessToken_ExpiresIn] = $expires_in;
$this[OAuth2Protocol::OAuth2Protocol_TokenType] = 'Bearer';
if(!is_null($scope) && !empty($scope))
$this[OAuth2Protocol::OAuth2Protocol_Scope] = $scope;
if(!is_null($state) && !empty($state))
$this[OAuth2Protocol::OAuth2Protocol_State] = $state;
}
}

View File

@ -10,12 +10,14 @@ use oauth2\OAuth2Protocol;
*/
class OAuth2AuthorizationResponse extends OAuth2IndirectResponse {
public function setAuthorizationCode($code){
$this[OAuth2Protocol::OAuth2Protocol_ResponseType_Code] = $code;
}
public function setState($state){
$this[OAuth2Protocol::OAuth2Protocol_State] = $state;
public function __construct($return_url, $code, $state=null)
{
parent::__construct();
$this[OAuth2Protocol::OAuth2Protocol_ResponseType_Code] = $code;
$this->setReturnTo($return_url);
if(!is_null($state))
$this[OAuth2Protocol::OAuth2Protocol_State] = $state;
}
}

View File

@ -7,8 +7,8 @@ use oauth2\OAuth2Protocol;
class OAuth2DirectErrorResponse extends OAuth2DirectResponse {
public function __construct($error)
{
// Successful Responses: A server receiving a valid request MUST send a
// response with an HTTP status code of 200.
// Error Response: A server receiving an invalid request MUST send a
// response with an HTTP status code of 400.
parent::__construct(self::HttpErrorResponse, self::DirectResponseContentType);
$this[OAuth2Protocol::OAuth2Protocol_Error] = $error;
}

View File

@ -5,7 +5,7 @@ namespace oauth2\responses;
class OAuth2DirectResponse extends OAuth2Response {
const DirectResponseContentType = "application/json;charset=UTF-8";
const OAuth2DirectResponse ='OAuth2DirectResponse';
const OAuth2DirectResponse = 'OAuth2DirectResponse';
public function __construct($http_code=self::HttpOkResponse, $content_type=self::DirectResponseContentType)
{

View File

@ -7,7 +7,7 @@ use openid\responses\OpenIdIndirectResponse;
class OAuth2IndirectErrorResponse extends OAuth2IndirectResponse {
public function __construct($error,$return_to=null){
public function __construct($error, $return_to=null){
$this[OAuth2Protocol::OAuth2Protocol_Error] = $error;
$this->return_to = $return_to;
}

View File

@ -0,0 +1,19 @@
<?php
namespace oauth2\responses;
use oauth2\OAuth2Protocol;
class OAuth2IndirectFragmentErrorResponse extends OAuth2IndirectFragmentResponse {
public function __construct($error, $return_to=null){
parent::__construct();
$this->setError($error);
$this->setReturnTo($return_to);
}
public function setError($error){
$this[OAuth2Protocol::OAuth2Protocol_Error] = $error;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace oauth2\responses;
class OAuth2IndirectFragmentResponse extends OAuth2IndirectResponse {
const OAuth2IndirectFragmentResponse ='OAuth2IndirectFragmentResponse';
public function __construct(){
parent::__construct();
}
public function getType()
{
return self::OAuth2IndirectFragmentResponse;
}
}

View File

@ -7,7 +7,7 @@ abstract class OAuth2IndirectResponse extends OAuth2Response {
protected $return_to;
const IndirectResponseContentType = "application/x-www-form-urlencoded";
const OAuth2IndirectResponse ='OAuth2IndirectResponse';
const OAuth2IndirectResponse ='OAuth2IndirectResponse';
public function __construct()
{

View File

@ -14,4 +14,6 @@ interface IApiScopeService {
public function getAvailableScopes();
public function getAudienceByScopeNames(array $scopes_names);
public function getStrAudienceByScopeNames(array $scopes_names);
}

View File

@ -38,6 +38,15 @@ interface ITokenService {
public function createAccessToken(AuthorizationCode $auth_code,$redirect_uri=null);
/**
* @param $scope
* @param $client_id
* @param $audience
* @return mixed
*/
public function createAccessTokenFromParams($scope, $client_id, $audience);
/** Creates a new Access Token from a given refresh token, and invalidate former associated
* Access Token
* @param RefreshToken $refresh_token

View File

@ -0,0 +1,40 @@
<?php
namespace oauth2\strategies;
use oauth2\requests\OAuth2Request;
use oauth2\responses\OAuth2IndirectErrorResponse;
use oauth2\responses\OAuth2IndirectFragmentErrorResponse;
use oauth2\responses\OAuth2IndirectResponse;
use oauth2\OAuth2Protocol;
use ReflectionClass;
class OAuth2IndirectErrorResponseFactoryMethod {
/**
* @param OAuth2Request $request
* @param $error
* @param $return_url
* @return OAuth2IndirectResponse
*/
public static function buildResponse(OAuth2Request $request = null,$error, $return_url){
$response = null;
$reflector = new ReflectionClass($request);
$class_name = $reflector->getName();
if($class_name ==='oauth2\requests\OAuth2AuthorizationRequest'){
$response_type = $request->getResponseType();
switch($response_type){
case OAuth2Protocol::OAuth2Protocol_ResponseType_Token:
return new OAuth2IndirectFragmentErrorResponse($error,$return_url);
break;
case OAuth2Protocol::OAuth2Protocol_ResponseType_Code:
return new OAuth2IndirectErrorResponse($error,$return_url);
break;
default:
throw new Exception(sprintf("invalid response type %s",$response_type));
break;
}
}
return $response;
}
}

View File

@ -3,6 +3,7 @@
namespace oauth2\strategies;
use oauth2\responses\OAuth2DirectResponse;
use oauth2\responses\OAuth2IndirectFragmentResponse;
use oauth2\responses\OAuth2IndirectResponse;
use oauth2\responses\OAuth2Response;
use utils\services\Registry;
@ -18,6 +19,13 @@ class OAuth2ResponseStrategyFactoryMethod {
return Registry::getInstance()->get(OAuth2IndirectResponse::OAuth2IndirectResponse);
}
break;
case OAuth2IndirectFragmentResponse::OAuth2IndirectFragmentResponse:
{
return Registry::getInstance()->get(OAuth2IndirectFragmentResponse::OAuth2IndirectFragmentResponse);
}
break;
case OAuth2DirectResponse::OAuth2DirectResponse:
{
return Registry::getInstance()->get(OAuth2DirectResponse::OAuth2DirectResponse);

View File

@ -1,6 +1,11 @@
<?php
namespace utils;
/**
* Interface IHttpResponseStrategy
* Defines an interface to handle http responses
* @package utils
*/
interface IHttpResponseStrategy {
public function handle($response);
}

View File

@ -46,5 +46,15 @@ class ApiScopeService implements IApiScopeService {
return $audience;
}
public function getStrAudienceByScopeNames(array $scopes_names){
$audiences = $this->getAudienceByScopeNames($scopes_names);
$audience = '';
foreach($audiences as $resource_server_host => $ip){
$audience = $audience . $resource_server_host .' ';
}
$audience = trim($audience);
return $audience;
}
}

View File

@ -22,6 +22,8 @@ use oauth2\services\OAuth2ServiceCatalog;
class ClientService implements IClientService
{
const PrintableNonWhitespaceCharactersUrl = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~.-_';
private $auth_service;
public function __construct(IAuthService $auth_service)
@ -79,10 +81,10 @@ class ClientService implements IClientService
$client = new Client;
$client->app_name = $app_name;
$client->app_logo = $app_logo;
$client->client_id = Rand::getString(32) . '.openstack.client';
$client->client_id = Rand::getString(32, self::PrintableNonWhitespaceCharactersUrl,true) . '.openstack.client';
//only generates secret for confidential clients
if($client_type==IClient::ClientType_Confidential)
$client->client_secret = Rand::getString(16);
$client->client_secret = Rand::getString(16, self::PrintableNonWhitespaceCharactersUrl,true);
$client->client_type = $client_type;
$client->user_id = $user_id;
$client->active = true;
@ -168,7 +170,7 @@ class ClientService implements IClientService
$client = Client::find($id);
if (!is_null($client)) {
$client_secret = Rand::getString(16);
$client_secret = Rand::getString(16, self::PrintableNonWhitespaceCharactersUrl,true);
$client->client_secret = $client_secret;
$client->Save();
$token_service = Registry::getInstance()->get(OAuth2ServiceCatalog::TokenService);

View File

@ -64,7 +64,7 @@ class MementoOAuth2AuthenticationRequestService implements IMementoOAuth2Authent
}
}
if (count($oauth2_params) > 0) {
$msg = new OAuth2AuthorizationRequest($oauth2_params);
$msg = new OAuth2AuthorizationRequest(new OAuth2Message($oauth2_params));
}
}
return $msg;

View File

@ -121,16 +121,14 @@ class TokenService implements ITokenService
}
/**
* @param $auth_code
* @param $client_id
* @param $scope
* @param AuthorizationCode $auth_code
* @param null $redirect_uri
* @return Token
* @return AccessToken
*/
public function createAccessToken(AuthorizationCode $auth_code, $redirect_uri = null)
{
$access_token = AccessToken::create($auth_code, $this->configuration_service->getConfigValue('OAuth2.AccessToken.Lifetime'));
$value = $access_token->getValue();
$value = $access_token->getValue();
$hashed_value = Hash::compute('sha256', $value);
$this->storesAccessTokenOnRedis($access_token);
@ -154,6 +152,32 @@ class TokenService implements ITokenService
return $access_token;
}
public function createAccessTokenFromParams($scope, $client_id, $audience){
$access_token = AccessToken::createFromParams($scope, $client_id, $audience, $this->configuration_service->getConfigValue('OAuth2.AccessToken.Lifetime'));
$value = $access_token->getValue();
$hashed_value = Hash::compute('sha256', $value);
$this->storesAccessTokenOnRedis($access_token);
$client_id = $access_token->getClientId();
$client = $this->client_service->getClientById($client_id);
//stores in DB
$access_token_db = new DBAccessToken;
$access_token_db->value = $hashed_value;
$access_token_db->from_ip = IPHelper::getUserIp();
$access_token_db->associated_authorization_code = null;
$access_token_db->lifetime = $access_token->getLifetime();
$access_token_db->scope = $access_token->getScope();
$access_token_db->client_id = $client->getId();
$access_token_db->audience = $access_token->getAudience();
$access_token_db->Save();
//stores brand new access token hash value on a set by client id...
$this->redis->sadd($client_id . self::ClientAccessTokenPrefixList, $hashed_value);
return $access_token;
}
/**
* @param AccessToken $access_token
* @throws \oauth2\exceptions\InvalidAccessTokenException

View File

@ -7,13 +7,23 @@ use utils\IHttpResponseStrategy;
use Redirect;
use Response;
class IndirectResponseStrategy implements IHttpResponseStrategy
/**
* Class IndirectResponseQueryStringStrategy
* Redirect and http response using a 302 adding params on query string
* @package strategies
*/
class IndirectResponseQueryStringStrategy implements IHttpResponseStrategy
{
/**
* @param $response
* @return mixed
*/
public function handle($response)
{
$query_string = $response->getContent();
$return_to = $response->getReturnTo();
$return_to = $response->getReturnTo();
if (is_null($return_to) || empty($return_to)) {
return \View::make('404');
}

View File

@ -0,0 +1,33 @@
<?php
namespace strategies;
use utils\IHttpResponseStrategy;
use Redirect;
use Response;
/**
* Class IndirectResponseUrlFragmentStrategy
* Redirect and http response using a 302 adding params on url fragment
* @package strategies
*/
class IndirectResponseUrlFragmentStrategy implements IHttpResponseStrategy
{
/**
* @param $response
* @return mixed
*/
public function handle($response)
{
$fragment = $response->getContent();
$return_to = $response->getReturnTo();
if (is_null($return_to) || empty($return_to)) {
return \View::make('404');
}
$return_to = (strpos($return_to, "#") === false) ? $return_to . "#" . $fragment : $return_to . "&" . $fragment;
return Redirect::to($return_to);
}
}

View File

@ -8,19 +8,20 @@ use oauth2\responses\OAuth2IndirectResponse;
use openid\responses\OpenIdDirectResponse;
use openid\responses\OpenIdIndirectResponse;
use utils\services\Registry;
use oauth2\responses\OAuth2IndirectFragmentResponse;
class StrategyProvider extends ServiceProvider
{
public function boot()
{
//direct response strategy
$this->app->singleton(OAuth2DirectResponse::OAuth2DirectResponse, 'strategies\\DirectResponseStrategy');
$this->app->singleton(OpenIdDirectResponse::OpenIdDirectResponse, 'strategies\\DirectResponseStrategy');
//indirect response strategy
$this->app->singleton(OpenIdIndirectResponse::OpenIdIndirectResponse, 'strategies\\IndirectResponseStrategy');
$this->app->singleton(OAuth2IndirectResponse::OAuth2IndirectResponse, 'strategies\\IndirectResponseStrategy');
$this->app->singleton(OpenIdIndirectResponse::OpenIdIndirectResponse, 'strategies\\IndirectResponseQueryStringStrategy');
$this->app->singleton(OAuth2IndirectResponse::OAuth2IndirectResponse, 'strategies\\IndirectResponseQueryStringStrategy');
$this->app->singleton(OAuth2IndirectFragmentResponse::OAuth2IndirectFragmentResponse,'strategies\\IndirectResponseUrlFragmentStrategy');
$this->app->singleton('oauth2\\strategies\\IOAuth2AuthenticationStrategy', 'strategies\\OAuth2AuthenticationStrategy');
@ -29,6 +30,7 @@ class StrategyProvider extends ServiceProvider
Registry::getInstance()->set(OpenIdIndirectResponse::OpenIdIndirectResponse, $this->app->make(OpenIdIndirectResponse::OpenIdIndirectResponse));
Registry::getInstance()->set(OAuth2IndirectResponse::OAuth2IndirectResponse, $this->app->make(OAuth2IndirectResponse::OAuth2IndirectResponse));
Registry::getInstance()->set(OAuth2IndirectFragmentResponse::OAuth2IndirectFragmentResponse, $this->app->make(OAuth2IndirectFragmentResponse::OAuth2IndirectFragmentResponse));
}
public function register()

View File

@ -37,9 +37,10 @@ class OAuth2TokenEndpointTest extends TestCase
array(),
array());
$status = $response->getStatusCode();
$url = $response->getTargetUrl();
$url = $response->getTargetUrl();
$content = $response->getContent();
$this->assertResponseStatus(302);
}
/** Get Token Test
@ -469,4 +470,47 @@ class OAuth2TokenEndpointTest extends TestCase
throw $ex;
}
}
public function testImplicitFlow(){
$client_id = 'Jiz87D8/Vcvr6fvQbH4HyNgwKlfSyQ3x.openstack.client';
//do login and consent ...
$user = OpenIdUser::where('external_id', '=', 'smarcet@gmail.com')->first();
Auth::login($user);
Session::set("openid.authorization.response", IAuthService::AuthorizationResponse_AllowOnce);
$params = array(
'client_id' => $client_id,
'redirect_uri' => 'https://www.test.com/oauth2',
'response_type' => OAuth2Protocol::OAuth2Protocol_ResponseType_Token,
'scope' => 'https://www.test.com/users/activities.read',
'state' => '123456'
);
$response = $this->action("POST", "OAuth2ProviderController@authorize",
$params,
array(),
array(),
array());
$this->assertResponseStatus(302);
$url = $response->getTargetUrl();
// get auth code ...
$comps = @parse_url($url);
$fragment = $comps['fragment'];
$response = array();
parse_str($fragment, $response);
$this->assertTrue(isset($response['access_token']) && !empty($response['access_token']));
$this->assertTrue(isset($response['expires_in']));
$this->assertTrue(isset($response['scope']));
$this->assertTrue(isset($response['state']));
$this->assertTrue($response['state']==='123456');
$this->assertTrue(isset($response['token_type']));
$this->assertTrue($response['token_type']==='Bearer');
}
}