2017-08-08 03:01:11 +08:00
< ? php
namespace OAuth2 ;
use OAuth2\Controller\ResourceControllerInterface ;
use OAuth2\Controller\ResourceController ;
2020-04-30 21:43:07 +08:00
use OAuth2\OpenID\Controller\UserInfoControllerInterface ;
use OAuth2\OpenID\Controller\UserInfoController ;
use OAuth2\OpenID\Controller\AuthorizeController as OpenIDAuthorizeController ;
use OAuth2\OpenID\ResponseType\AuthorizationCode as OpenIDAuthorizationCodeResponseType ;
use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface ;
use OAuth2\OpenID\GrantType\AuthorizationCode as OpenIDAuthorizationCodeGrantType ;
2017-08-08 03:01:11 +08:00
use OAuth2\Controller\AuthorizeControllerInterface ;
use OAuth2\Controller\AuthorizeController ;
use OAuth2\Controller\TokenControllerInterface ;
use OAuth2\Controller\TokenController ;
use OAuth2\ClientAssertionType\ClientAssertionTypeInterface ;
use OAuth2\ClientAssertionType\HttpBasic ;
use OAuth2\ResponseType\ResponseTypeInterface ;
use OAuth2\ResponseType\AuthorizationCode as AuthorizationCodeResponseType ;
use OAuth2\ResponseType\AccessToken ;
2020-04-30 21:43:07 +08:00
use OAuth2\ResponseType\JwtAccessToken ;
use OAuth2\OpenID\ResponseType\CodeIdToken ;
use OAuth2\OpenID\ResponseType\IdToken ;
use OAuth2\OpenID\ResponseType\IdTokenToken ;
2017-08-08 03:01:11 +08:00
use OAuth2\TokenType\TokenTypeInterface ;
use OAuth2\TokenType\Bearer ;
use OAuth2\GrantType\GrantTypeInterface ;
2020-04-30 21:43:07 +08:00
use OAuth2\GrantType\UserCredentials ;
use OAuth2\GrantType\ClientCredentials ;
2017-08-08 03:01:11 +08:00
use OAuth2\GrantType\RefreshToken ;
use OAuth2\GrantType\AuthorizationCode ;
2020-04-30 21:43:07 +08:00
use OAuth2\Storage\ClientCredentialsInterface ;
use OAuth2\Storage\ClientInterface ;
use OAuth2\Storage\JwtAccessToken as JwtAccessTokenStorage ;
use OAuth2\Storage\JwtAccessTokenInterface ;
use InvalidArgumentException ;
use LogicException ;
2017-08-08 03:01:11 +08:00
/**
* Server class for OAuth2
* This class serves as a convience class which wraps the other Controller classes
*
2020-04-30 21:43:07 +08:00
* @ see \OAuth2\Controller\ResourceController
* @ see \OAuth2\Controller\AuthorizeController
* @ see \OAuth2\Controller\TokenController
2017-08-08 03:01:11 +08:00
*/
class Server implements ResourceControllerInterface ,
AuthorizeControllerInterface ,
2020-04-30 21:43:07 +08:00
TokenControllerInterface ,
UserInfoControllerInterface
2017-08-08 03:01:11 +08:00
{
/**
2020-04-30 21:43:07 +08:00
* @ var ResponseInterface
2017-08-08 03:01:11 +08:00
*/
protected $response ;
/**
* @ var array
*/
protected $config ;
/**
* @ var array
*/
protected $storages ;
/**
* @ var AuthorizeControllerInterface
*/
protected $authorizeController ;
/**
* @ var TokenControllerInterface
*/
protected $tokenController ;
/**
* @ var ResourceControllerInterface
*/
protected $resourceController ;
/**
* @ var UserInfoControllerInterface
*/
protected $userInfoController ;
2020-04-30 21:43:07 +08:00
/**
* @ var array
*/
protected $grantTypes = array ();
/**
* @ var array
*/
protected $responseTypes = array ();
/**
* @ var TokenTypeInterface
*/
2017-08-08 03:01:11 +08:00
protected $tokenType ;
/**
* @ var ScopeInterface
*/
protected $scopeUtil ;
2020-04-30 21:43:07 +08:00
/**
* @ var ClientAssertionTypeInterface
*/
2017-08-08 03:01:11 +08:00
protected $clientAssertionType ;
2020-04-30 21:43:07 +08:00
/**
* @ var array
*/
2017-08-08 03:01:11 +08:00
protected $storageMap = array (
'access_token' => 'OAuth2\Storage\AccessTokenInterface' ,
'authorization_code' => 'OAuth2\Storage\AuthorizationCodeInterface' ,
'client_credentials' => 'OAuth2\Storage\ClientCredentialsInterface' ,
'client' => 'OAuth2\Storage\ClientInterface' ,
'refresh_token' => 'OAuth2\Storage\RefreshTokenInterface' ,
'user_credentials' => 'OAuth2\Storage\UserCredentialsInterface' ,
2020-04-30 21:43:07 +08:00
'user_claims' => 'OAuth2\OpenID\Storage\UserClaimsInterface' ,
2017-08-08 03:01:11 +08:00
'public_key' => 'OAuth2\Storage\PublicKeyInterface' ,
2020-04-30 21:43:07 +08:00
'jwt_bearer' => 'OAuth2\Storage\JWTBearerInterface' ,
2017-08-08 03:01:11 +08:00
'scope' => 'OAuth2\Storage\ScopeInterface' ,
);
2020-04-30 21:43:07 +08:00
/**
* @ var array
*/
2017-08-08 03:01:11 +08:00
protected $responseTypeMap = array (
'token' => 'OAuth2\ResponseType\AccessTokenInterface' ,
'code' => 'OAuth2\ResponseType\AuthorizationCodeInterface' ,
2020-04-30 21:43:07 +08:00
'id_token' => 'OAuth2\OpenID\ResponseType\IdTokenInterface' ,
'id_token token' => 'OAuth2\OpenID\ResponseType\IdTokenTokenInterface' ,
'code id_token' => 'OAuth2\OpenID\ResponseType\CodeIdTokenInterface' ,
2017-08-08 03:01:11 +08:00
);
/**
2020-04-30 21:43:07 +08:00
* @ param mixed $storage ( array or OAuth2\Storage ) - single object or array of objects implementing the
* required storage types ( ClientCredentialsInterface and AccessTokenInterface as a minimum )
* @ param array $config specify a different token lifetime , token header name , etc
* @ param array $grantTypes An array of OAuth2\GrantType\GrantTypeInterface to use for granting access tokens
* @ param array $responseTypes Response types to use . array keys should be " code " and " token " for
* Access Token and Authorization Code response types
* @ param TokenTypeInterface $tokenType The token type object to use . Valid token types are " bearer " and " mac "
* @ param ScopeInterface $scopeUtil The scope utility class to use to validate scope
* @ param ClientAssertionTypeInterface $clientAssertionType The method in which to verify the client identity . Default is HttpBasic
2017-08-08 03:01:11 +08:00
*
* @ ingroup oauth2_section_7
*/
public function __construct ( $storage = array (), array $config = array (), array $grantTypes = array (), array $responseTypes = array (), TokenTypeInterface $tokenType = null , ScopeInterface $scopeUtil = null , ClientAssertionTypeInterface $clientAssertionType = null )
{
$storage = is_array ( $storage ) ? $storage : array ( $storage );
$this -> storages = array ();
foreach ( $storage as $key => $service ) {
$this -> addStorage ( $service , $key );
}
// merge all config values. These get passed to our controller objects
$this -> config = array_merge ( array (
2020-04-30 21:43:07 +08:00
'use_jwt_access_tokens' => false ,
'jwt_extra_payload_callable' => null ,
2017-08-08 03:01:11 +08:00
'store_encrypted_token_string' => true ,
2020-04-30 21:43:07 +08:00
'use_openid_connect' => false ,
2017-08-08 03:01:11 +08:00
'id_lifetime' => 3600 ,
'access_lifetime' => 3600 ,
'www_realm' => 'Service' ,
'token_param_name' => 'access_token' ,
'token_bearer_header_name' => 'Bearer' ,
'enforce_state' => true ,
'require_exact_redirect_uri' => true ,
'allow_implicit' => false ,
'allow_credentials_in_request_body' => true ,
'allow_public_clients' => true ,
'always_issue_new_refresh_token' => false ,
'unset_refresh_token_after_use' => true ,
), $config );
foreach ( $grantTypes as $key => $grantType ) {
$this -> addGrantType ( $grantType , $key );
}
foreach ( $responseTypes as $key => $responseType ) {
$this -> addResponseType ( $responseType , $key );
}
$this -> tokenType = $tokenType ;
$this -> scopeUtil = $scopeUtil ;
$this -> clientAssertionType = $clientAssertionType ;
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_openid_connect' ]) {
$this -> validateOpenIdConnect ();
}
2017-08-08 03:01:11 +08:00
}
2020-04-30 21:43:07 +08:00
/**
* @ return AuthorizeControllerInterface
*/
2017-08-08 03:01:11 +08:00
public function getAuthorizeController ()
{
if ( is_null ( $this -> authorizeController )) {
$this -> authorizeController = $this -> createDefaultAuthorizeController ();
}
return $this -> authorizeController ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return TokenController
*/
2017-08-08 03:01:11 +08:00
public function getTokenController ()
{
if ( is_null ( $this -> tokenController )) {
$this -> tokenController = $this -> createDefaultTokenController ();
}
return $this -> tokenController ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return ResourceControllerInterface
*/
2017-08-08 03:01:11 +08:00
public function getResourceController ()
{
if ( is_null ( $this -> resourceController )) {
$this -> resourceController = $this -> createDefaultResourceController ();
}
return $this -> resourceController ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return UserInfoControllerInterface
*/
2017-08-08 03:01:11 +08:00
public function getUserInfoController ()
{
if ( is_null ( $this -> userInfoController )) {
$this -> userInfoController = $this -> createDefaultUserInfoController ();
}
return $this -> userInfoController ;
}
/**
* @ param AuthorizeControllerInterface $authorizeController
*/
public function setAuthorizeController ( AuthorizeControllerInterface $authorizeController )
{
$this -> authorizeController = $authorizeController ;
}
/**
* @ param TokenControllerInterface $tokenController
*/
public function setTokenController ( TokenControllerInterface $tokenController )
{
$this -> tokenController = $tokenController ;
}
/**
* @ param ResourceControllerInterface $resourceController
*/
public function setResourceController ( ResourceControllerInterface $resourceController )
{
$this -> resourceController = $resourceController ;
}
/**
* @ param UserInfoControllerInterface $userInfoController
*/
public function setUserInfoController ( UserInfoControllerInterface $userInfoController )
{
$this -> userInfoController = $userInfoController ;
}
2020-04-30 21:43:07 +08:00
/**
* Return claims about the authenticated end - user .
* This would be called from the " /UserInfo " endpoint as defined in the spec .
*
* @ param RequestInterface $request - Request object to grant access token
* @ param ResponseInterface $response - Response object containing error messages ( failure ) or user claims ( success )
* @ return ResponseInterface
*
* @ throws \InvalidArgumentException
* @ throws \LogicException
*
* @ see http :// openid . net / specs / openid - connect - core - 1_0 . html #UserInfo
*/
public function handleUserInfoRequest ( RequestInterface $request , ResponseInterface $response = null )
{
$this -> response = is_null ( $response ) ? new Response () : $response ;
$this -> getUserInfoController () -> handleUserInfoRequest ( $request , $this -> response );
return $this -> response ;
}
2017-08-08 03:01:11 +08:00
/**
* Grant or deny a requested access token .
* This would be called from the " /token " endpoint as defined in the spec .
* Obviously , you can call your endpoint whatever you want .
*
2020-04-30 21:43:07 +08:00
* @ param RequestInterface $request - Request object to grant access token
* @ param ResponseInterface $response - Response object containing error messages ( failure ) or access token ( success )
2017-08-08 03:01:11 +08:00
* @ return ResponseInterface
*
* @ throws \InvalidArgumentException
* @ throws \LogicException
*
* @ see http :// tools . ietf . org / html / rfc6749 #section-4
* @ see http :// tools . ietf . org / html / rfc6749 #section-10.6
* @ see http :// tools . ietf . org / html / rfc6749 #section-4.1.3
*
* @ ingroup oauth2_section_4
*/
public function handleTokenRequest ( RequestInterface $request , ResponseInterface $response = null )
{
$this -> response = is_null ( $response ) ? new Response () : $response ;
$this -> getTokenController () -> handleTokenRequest ( $request , $this -> response );
return $this -> response ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param RequestInterface $request - Request object to grant access token
* @ param ResponseInterface $response - Response object
* @ return mixed
*/
2017-08-08 03:01:11 +08:00
public function grantAccessToken ( RequestInterface $request , ResponseInterface $response = null )
{
$this -> response = is_null ( $response ) ? new Response () : $response ;
$value = $this -> getTokenController () -> grantAccessToken ( $request , $this -> response );
return $value ;
}
/**
* Handle a revoke token request
* This would be called from the " /revoke " endpoint as defined in the draft Token Revocation spec
*
* @ see https :// tools . ietf . org / html / rfc7009 #section-2
*
* @ param RequestInterface $request
* @ param ResponseInterface $response
* @ return Response | ResponseInterface
*/
public function handleRevokeRequest ( RequestInterface $request , ResponseInterface $response = null )
{
$this -> response = is_null ( $response ) ? new Response () : $response ;
$this -> getTokenController () -> handleRevokeRequest ( $request , $this -> response );
return $this -> response ;
}
/**
* Redirect the user appropriately after approval .
*
* After the user has approved or denied the resource request the
* authorization server should call this function to redirect the user
* appropriately .
*
2020-04-30 21:43:07 +08:00
* @ param RequestInterface $request - The request should have the follow parameters set in the querystring :
* - response_type : The requested response : an access token , an authorization code , or both .
2017-08-08 03:01:11 +08:00
* - client_id : The client identifier as described in Section 2.
2020-04-30 21:43:07 +08:00
* - redirect_uri : An absolute URI to which the authorization server will redirect the user - agent to when the
* end - user authorization step is completed .
* - scope : ( optional ) The scope of the resource request expressed as a list of space - delimited strings .
* - state : ( optional ) An opaque value used by the client to maintain state between the request and callback .
2017-08-08 03:01:11 +08:00
*
2020-04-30 21:43:07 +08:00
* @ param ResponseInterface $response - Response object
* @ param bool $is_authorized - TRUE or FALSE depending on whether the user authorized the access .
* @ param mixed $user_id - Identifier of user who authorized the client
* @ return ResponseInterface
2017-08-08 03:01:11 +08:00
*
* @ see http :// tools . ietf . org / html / rfc6749 #section-4
*
* @ ingroup oauth2_section_4
*/
public function handleAuthorizeRequest ( RequestInterface $request , ResponseInterface $response , $is_authorized , $user_id = null )
{
$this -> response = $response ;
$this -> getAuthorizeController () -> handleAuthorizeRequest ( $request , $this -> response , $is_authorized , $user_id );
return $this -> response ;
}
/**
* Pull the authorization request data out of the HTTP request .
* - The redirect_uri is OPTIONAL as per draft 20. But your implementation can enforce it
2020-04-30 21:43:07 +08:00
* by setting $config [ 'enforce_redirect' ] to true .
2017-08-08 03:01:11 +08:00
* - The state is OPTIONAL but recommended to enforce CSRF . Draft 21 states , however , that
2020-04-30 21:43:07 +08:00
* CSRF protection is MANDATORY . You can enforce this by setting the $config [ 'enforce_state' ] to true .
2017-08-08 03:01:11 +08:00
*
* The draft specifies that the parameters should be retrieved from GET , override the Response
* object to change this
*
2020-04-30 21:43:07 +08:00
* @ param RequestInterface $request - Request object
* @ param ResponseInterface $response - Response object
* @ return bool
*
2017-08-08 03:01:11 +08:00
* The authorization parameters so the authorization server can prompt
* the user for approval if valid .
*
* @ see http :// tools . ietf . org / html / rfc6749 #section-4.1.1
* @ see http :// tools . ietf . org / html / rfc6749 #section-10.12
*
* @ ingroup oauth2_section_3
*/
public function validateAuthorizeRequest ( RequestInterface $request , ResponseInterface $response = null )
{
$this -> response = is_null ( $response ) ? new Response () : $response ;
$value = $this -> getAuthorizeController () -> validateAuthorizeRequest ( $request , $this -> response );
return $value ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param RequestInterface $request - Request object
* @ param ResponseInterface $response - Response object
* @ param string $scope - Scope
* @ return mixed
*/
2017-08-08 03:01:11 +08:00
public function verifyResourceRequest ( RequestInterface $request , ResponseInterface $response = null , $scope = null )
{
$this -> response = is_null ( $response ) ? new Response () : $response ;
$value = $this -> getResourceController () -> verifyResourceRequest ( $request , $this -> response , $scope );
return $value ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param RequestInterface $request - Request object
* @ param ResponseInterface $response - Response object
* @ return mixed
*/
2017-08-08 03:01:11 +08:00
public function getAccessTokenData ( RequestInterface $request , ResponseInterface $response = null )
{
$this -> response = is_null ( $response ) ? new Response () : $response ;
$value = $this -> getResourceController () -> getAccessTokenData ( $request , $this -> response );
return $value ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param GrantTypeInterface $grantType
* @ param mixed $identifier
*/
2017-08-08 03:01:11 +08:00
public function addGrantType ( GrantTypeInterface $grantType , $identifier = null )
{
if ( ! is_string ( $identifier )) {
2020-04-30 21:43:07 +08:00
$identifier = $grantType -> getQueryStringIdentifier ();
2017-08-08 03:01:11 +08:00
}
$this -> grantTypes [ $identifier ] = $grantType ;
// persist added grant type down to TokenController
if ( ! is_null ( $this -> tokenController )) {
$this -> getTokenController () -> addGrantType ( $grantType , $identifier );
}
}
/**
* Set a storage object for the server
*
2020-04-30 21:43:07 +08:00
* @ param object $storage - An object implementing one of the Storage interfaces
* @ param mixed $key - If null , the storage is set to the key of each storage interface it implements
2017-08-08 03:01:11 +08:00
*
2020-04-30 21:43:07 +08:00
* @ throws InvalidArgumentException
2017-08-08 03:01:11 +08:00
* @ see storageMap
*/
public function addStorage ( $storage , $key = null )
{
// if explicitly set to a valid key, do not "magically" set below
if ( isset ( $this -> storageMap [ $key ])) {
if ( ! is_null ( $storage ) && ! $storage instanceof $this -> storageMap [ $key ]) {
throw new \InvalidArgumentException ( sprintf ( 'storage of type "%s" must implement interface "%s"' , $key , $this -> storageMap [ $key ]));
}
$this -> storages [ $key ] = $storage ;
// special logic to handle "client" and "client_credentials" strangeness
if ( $key === 'client' && ! isset ( $this -> storages [ 'client_credentials' ])) {
2020-04-30 21:43:07 +08:00
if ( $storage instanceof ClientCredentialsInterface ) {
2017-08-08 03:01:11 +08:00
$this -> storages [ 'client_credentials' ] = $storage ;
}
} elseif ( $key === 'client_credentials' && ! isset ( $this -> storages [ 'client' ])) {
2020-04-30 21:43:07 +08:00
if ( $storage instanceof ClientInterface ) {
2017-08-08 03:01:11 +08:00
$this -> storages [ 'client' ] = $storage ;
}
}
} elseif ( ! is_null ( $key ) && ! is_numeric ( $key )) {
throw new \InvalidArgumentException ( sprintf ( 'unknown storage key "%s", must be one of [%s]' , $key , implode ( ', ' , array_keys ( $this -> storageMap ))));
} else {
$set = false ;
foreach ( $this -> storageMap as $type => $interface ) {
if ( $storage instanceof $interface ) {
$this -> storages [ $type ] = $storage ;
$set = true ;
}
}
if ( ! $set ) {
throw new \InvalidArgumentException ( sprintf ( 'storage of class "%s" must implement one of [%s]' , get_class ( $storage ), implode ( ', ' , $this -> storageMap )));
}
}
}
2020-04-30 21:43:07 +08:00
/**
* @ param ResponseTypeInterface $responseType
* @ param mixed $key
*
* @ throws InvalidArgumentException
*/
2017-08-08 03:01:11 +08:00
public function addResponseType ( ResponseTypeInterface $responseType , $key = null )
{
$key = $this -> normalizeResponseType ( $key );
if ( isset ( $this -> responseTypeMap [ $key ])) {
if ( ! $responseType instanceof $this -> responseTypeMap [ $key ]) {
throw new \InvalidArgumentException ( sprintf ( 'responseType of type "%s" must implement interface "%s"' , $key , $this -> responseTypeMap [ $key ]));
}
$this -> responseTypes [ $key ] = $responseType ;
} elseif ( ! is_null ( $key ) && ! is_numeric ( $key )) {
throw new \InvalidArgumentException ( sprintf ( 'unknown responseType key "%s", must be one of [%s]' , $key , implode ( ', ' , array_keys ( $this -> responseTypeMap ))));
} else {
$set = false ;
foreach ( $this -> responseTypeMap as $type => $interface ) {
if ( $responseType instanceof $interface ) {
$this -> responseTypes [ $type ] = $responseType ;
$set = true ;
}
}
if ( ! $set ) {
throw new \InvalidArgumentException ( sprintf ( 'Unknown response type %s. Please implement one of [%s]' , get_class ( $responseType ), implode ( ', ' , $this -> responseTypeMap )));
}
}
}
2020-04-30 21:43:07 +08:00
/**
* @ return ScopeInterface
*/
2017-08-08 03:01:11 +08:00
public function getScopeUtil ()
{
if ( ! $this -> scopeUtil ) {
$storage = isset ( $this -> storages [ 'scope' ]) ? $this -> storages [ 'scope' ] : null ;
$this -> scopeUtil = new Scope ( $storage );
}
return $this -> scopeUtil ;
}
/**
* @ param ScopeInterface $scopeUtil
*/
public function setScopeUtil ( $scopeUtil )
{
$this -> scopeUtil = $scopeUtil ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return AuthorizeControllerInterface
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function createDefaultAuthorizeController ()
{
if ( ! isset ( $this -> storages [ 'client' ])) {
throw new \LogicException ( 'You must supply a storage object implementing \OAuth2\Storage\ClientInterface to use the authorize server' );
}
if ( 0 == count ( $this -> responseTypes )) {
$this -> responseTypes = $this -> getDefaultResponseTypes ();
}
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_openid_connect' ] && ! isset ( $this -> responseTypes [ 'id_token' ])) {
$this -> responseTypes [ 'id_token' ] = $this -> createDefaultIdTokenResponseType ();
if ( $this -> config [ 'allow_implicit' ]) {
$this -> responseTypes [ 'id_token token' ] = $this -> createDefaultIdTokenTokenResponseType ();
}
}
2017-08-08 03:01:11 +08:00
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'allow_implicit enforce_state require_exact_redirect_uri' )));
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_openid_connect' ]) {
return new OpenIDAuthorizeController ( $this -> storages [ 'client' ], $this -> responseTypes , $config , $this -> getScopeUtil ());
}
2017-08-08 03:01:11 +08:00
return new AuthorizeController ( $this -> storages [ 'client' ], $this -> responseTypes , $config , $this -> getScopeUtil ());
}
2020-04-30 21:43:07 +08:00
/**
* @ return TokenControllerInterface
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function createDefaultTokenController ()
{
if ( 0 == count ( $this -> grantTypes )) {
$this -> grantTypes = $this -> getDefaultGrantTypes ();
}
if ( is_null ( $this -> clientAssertionType )) {
// see if HttpBasic assertion type is requred. If so, then create it from storage classes.
foreach ( $this -> grantTypes as $grantType ) {
if ( ! $grantType instanceof ClientAssertionTypeInterface ) {
if ( ! isset ( $this -> storages [ 'client_credentials' ])) {
throw new \LogicException ( 'You must supply a storage object implementing OAuth2\Storage\ClientCredentialsInterface to use the token server' );
}
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'allow_credentials_in_request_body allow_public_clients' )));
$this -> clientAssertionType = new HttpBasic ( $this -> storages [ 'client_credentials' ], $config );
break ;
}
}
}
if ( ! isset ( $this -> storages [ 'client' ])) {
2020-04-30 21:43:07 +08:00
throw new LogicException ( " You must supply a storage object implementing OAuth2 \ Storage \ ClientInterface to use the token server " );
2017-08-08 03:01:11 +08:00
}
$accessTokenResponseType = $this -> getAccessTokenResponseType ();
return new TokenController ( $accessTokenResponseType , $this -> storages [ 'client' ], $this -> grantTypes , $this -> clientAssertionType , $this -> getScopeUtil ());
}
2020-04-30 21:43:07 +08:00
/**
* @ return ResourceControllerInterface
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function createDefaultResourceController ()
{
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_jwt_access_tokens' ]) {
// overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set
if ( ! isset ( $this -> storages [ 'access_token' ]) || ! $this -> storages [ 'access_token' ] instanceof JwtAccessTokenInterface ) {
$this -> storages [ 'access_token' ] = $this -> createDefaultJwtAccessTokenStorage ();
}
} elseif ( ! isset ( $this -> storages [ 'access_token' ])) {
throw new \LogicException ( 'You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the resource server' );
2017-08-08 03:01:11 +08:00
}
if ( ! $this -> tokenType ) {
$this -> tokenType = $this -> getDefaultTokenType ();
}
$config = array_intersect_key ( $this -> config , array ( 'www_realm' => '' ));
return new ResourceController ( $this -> tokenType , $this -> storages [ 'access_token' ], $config , $this -> getScopeUtil ());
}
2020-04-30 21:43:07 +08:00
/**
* @ return UserInfoControllerInterface
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function createDefaultUserInfoController ()
{
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_jwt_access_tokens' ]) {
// overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set
if ( ! isset ( $this -> storages [ 'access_token' ]) || ! $this -> storages [ 'access_token' ] instanceof JwtAccessTokenInterface ) {
$this -> storages [ 'access_token' ] = $this -> createDefaultJwtAccessTokenStorage ();
}
} elseif ( ! isset ( $this -> storages [ 'access_token' ])) {
throw new \LogicException ( 'You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the UserInfo server' );
}
if ( ! isset ( $this -> storages [ 'user_claims' ])) {
throw new \LogicException ( 'You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use the UserInfo server' );
2017-08-08 03:01:11 +08:00
}
if ( ! $this -> tokenType ) {
$this -> tokenType = $this -> getDefaultTokenType ();
}
$config = array_intersect_key ( $this -> config , array ( 'www_realm' => '' ));
return new UserInfoController ( $this -> tokenType , $this -> storages [ 'access_token' ], $this -> storages [ 'user_claims' ], $config , $this -> getScopeUtil ());
}
2020-04-30 21:43:07 +08:00
/**
* @ return Bearer
*/
2017-08-08 03:01:11 +08:00
protected function getDefaultTokenType ()
{
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'token_param_name token_bearer_header_name' )));
return new Bearer ( $config );
}
2020-04-30 21:43:07 +08:00
/**
* @ return array
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function getDefaultResponseTypes ()
{
$responseTypes = array ();
if ( $this -> config [ 'allow_implicit' ]) {
$responseTypes [ 'token' ] = $this -> getAccessTokenResponseType ();
}
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_openid_connect' ]) {
$responseTypes [ 'id_token' ] = $this -> getIdTokenResponseType ();
if ( $this -> config [ 'allow_implicit' ]) {
$responseTypes [ 'id_token token' ] = $this -> getIdTokenTokenResponseType ();
}
}
2017-08-08 03:01:11 +08:00
if ( isset ( $this -> storages [ 'authorization_code' ])) {
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'enforce_redirect auth_code_lifetime' )));
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_openid_connect' ]) {
if ( ! $this -> storages [ 'authorization_code' ] instanceof OpenIDAuthorizationCodeInterface ) {
throw new \LogicException ( 'Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when "use_openid_connect" is true' );
}
$responseTypes [ 'code' ] = new OpenIDAuthorizationCodeResponseType ( $this -> storages [ 'authorization_code' ], $config );
$responseTypes [ 'code id_token' ] = new CodeIdToken ( $responseTypes [ 'code' ], $responseTypes [ 'id_token' ]);
} else {
$responseTypes [ 'code' ] = new AuthorizationCodeResponseType ( $this -> storages [ 'authorization_code' ], $config );
}
2017-08-08 03:01:11 +08:00
}
if ( count ( $responseTypes ) == 0 ) {
throw new \LogicException ( 'You must supply an array of response_types in the constructor or implement a OAuth2\Storage\AuthorizationCodeInterface storage object or set "allow_implicit" to true and implement a OAuth2\Storage\AccessTokenInterface storage object' );
}
return $responseTypes ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return array
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function getDefaultGrantTypes ()
{
$grantTypes = array ();
if ( isset ( $this -> storages [ 'user_credentials' ])) {
$grantTypes [ 'password' ] = new UserCredentials ( $this -> storages [ 'user_credentials' ]);
}
if ( isset ( $this -> storages [ 'client_credentials' ])) {
$config = array_intersect_key ( $this -> config , array ( 'allow_credentials_in_request_body' => '' ));
$grantTypes [ 'client_credentials' ] = new ClientCredentials ( $this -> storages [ 'client_credentials' ], $config );
}
if ( isset ( $this -> storages [ 'refresh_token' ])) {
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'always_issue_new_refresh_token unset_refresh_token_after_use' )));
$grantTypes [ 'refresh_token' ] = new RefreshToken ( $this -> storages [ 'refresh_token' ], $config );
}
if ( isset ( $this -> storages [ 'authorization_code' ])) {
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_openid_connect' ]) {
if ( ! $this -> storages [ 'authorization_code' ] instanceof OpenIDAuthorizationCodeInterface ) {
throw new \LogicException ( 'Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when "use_openid_connect" is true' );
}
$grantTypes [ 'authorization_code' ] = new OpenIDAuthorizationCodeGrantType ( $this -> storages [ 'authorization_code' ]);
} else {
$grantTypes [ 'authorization_code' ] = new AuthorizationCode ( $this -> storages [ 'authorization_code' ]);
}
2017-08-08 03:01:11 +08:00
}
if ( count ( $grantTypes ) == 0 ) {
throw new \LogicException ( 'Unable to build default grant types - You must supply an array of grant_types in the constructor' );
}
return $grantTypes ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return AccessToken
*/
2017-08-08 03:01:11 +08:00
protected function getAccessTokenResponseType ()
{
if ( isset ( $this -> responseTypes [ 'token' ])) {
return $this -> responseTypes [ 'token' ];
}
2020-04-30 21:43:07 +08:00
if ( $this -> config [ 'use_jwt_access_tokens' ]) {
return $this -> createDefaultJwtAccessTokenResponseType ();
}
2017-08-08 03:01:11 +08:00
return $this -> createDefaultAccessTokenResponseType ();
}
2020-04-30 21:43:07 +08:00
/**
* @ return IdToken
*/
2017-08-08 03:01:11 +08:00
protected function getIdTokenResponseType ()
{
if ( isset ( $this -> responseTypes [ 'id_token' ])) {
return $this -> responseTypes [ 'id_token' ];
}
return $this -> createDefaultIdTokenResponseType ();
}
2020-04-30 21:43:07 +08:00
/**
* @ return IdTokenToken
*/
2017-08-08 03:01:11 +08:00
protected function getIdTokenTokenResponseType ()
{
if ( isset ( $this -> responseTypes [ 'id_token token' ])) {
return $this -> responseTypes [ 'id_token token' ];
}
return $this -> createDefaultIdTokenTokenResponseType ();
}
2020-04-30 21:43:07 +08:00
/**
* For Resource Controller
*
* @ return JwtAccessTokenStorage
* @ throws LogicException
*/
protected function createDefaultJwtAccessTokenStorage ()
{
if ( ! isset ( $this -> storages [ 'public_key' ])) {
throw new \LogicException ( 'You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens' );
}
$tokenStorage = null ;
if ( ! empty ( $this -> config [ 'store_encrypted_token_string' ]) && isset ( $this -> storages [ 'access_token' ])) {
$tokenStorage = $this -> storages [ 'access_token' ];
}
// wrap the access token storage as required.
return new JwtAccessTokenStorage ( $this -> storages [ 'public_key' ], $tokenStorage );
}
/**
* For Authorize and Token Controllers
*
* @ return JwtAccessToken
* @ throws LogicException
*/
protected function createDefaultJwtAccessTokenResponseType ()
{
if ( ! isset ( $this -> storages [ 'public_key' ])) {
throw new \LogicException ( 'You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens' );
}
$tokenStorage = null ;
if ( isset ( $this -> storages [ 'access_token' ])) {
$tokenStorage = $this -> storages [ 'access_token' ];
}
$refreshStorage = null ;
if ( isset ( $this -> storages [ 'refresh_token' ])) {
$refreshStorage = $this -> storages [ 'refresh_token' ];
}
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'store_encrypted_token_string issuer access_lifetime refresh_token_lifetime jwt_extra_payload_callable' )));
return new JwtAccessToken ( $this -> storages [ 'public_key' ], $tokenStorage , $refreshStorage , $config );
}
/**
* @ return AccessToken
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function createDefaultAccessTokenResponseType ()
{
if ( ! isset ( $this -> storages [ 'access_token' ])) {
2020-04-30 21:43:07 +08:00
throw new LogicException ( " You must supply a response type implementing OAuth2 \R esponseType \ AccessTokenInterface, or a storage object implementing OAuth2 \ Storage \ AccessTokenInterface to use the token server " );
2017-08-08 03:01:11 +08:00
}
$refreshStorage = null ;
if ( isset ( $this -> storages [ 'refresh_token' ])) {
$refreshStorage = $this -> storages [ 'refresh_token' ];
}
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'access_lifetime refresh_token_lifetime' )));
$config [ 'token_type' ] = $this -> tokenType ? $this -> tokenType -> getTokenType () : $this -> getDefaultTokenType () -> getTokenType ();
return new AccessToken ( $this -> storages [ 'access_token' ], $refreshStorage , $config );
}
2020-04-30 21:43:07 +08:00
/**
* @ return IdToken
* @ throws LogicException
*/
2017-08-08 03:01:11 +08:00
protected function createDefaultIdTokenResponseType ()
{
2020-04-30 21:43:07 +08:00
if ( ! isset ( $this -> storages [ 'user_claims' ])) {
throw new LogicException ( " You must supply a storage object implementing OAuth2 \ OpenID \ Storage \ UserClaimsInterface to use openid connect " );
}
2017-08-08 03:01:11 +08:00
if ( ! isset ( $this -> storages [ 'public_key' ])) {
2020-04-30 21:43:07 +08:00
throw new LogicException ( " You must supply a storage object implementing OAuth2 \ Storage \ PublicKeyInterface to use openid connect " );
2017-08-08 03:01:11 +08:00
}
$config = array_intersect_key ( $this -> config , array_flip ( explode ( ' ' , 'issuer id_lifetime' )));
return new IdToken ( $this -> storages [ 'user_claims' ], $this -> storages [ 'public_key' ], $config );
}
2020-04-30 21:43:07 +08:00
/**
* @ return IdTokenToken
*/
2017-08-08 03:01:11 +08:00
protected function createDefaultIdTokenTokenResponseType ()
{
return new IdTokenToken ( $this -> getAccessTokenResponseType (), $this -> getIdTokenResponseType ());
}
2020-04-30 21:43:07 +08:00
/**
* @ throws InvalidArgumentException
*/
protected function validateOpenIdConnect ()
{
$authCodeGrant = $this -> getGrantType ( 'authorization_code' );
if ( ! empty ( $authCodeGrant ) && ! $authCodeGrant instanceof OpenIDAuthorizationCodeGrantType ) {
throw new \InvalidArgumentException ( 'You have enabled OpenID Connect, but supplied a grant type that does not support it.' );
}
}
/**
* @ param string $name
* @ return string
*/
2017-08-08 03:01:11 +08:00
protected function normalizeResponseType ( $name )
{
// for multiple-valued response types - make them alphabetical
if ( ! empty ( $name ) && false !== strpos ( $name , ' ' )) {
$types = explode ( ' ' , $name );
sort ( $types );
$name = implode ( ' ' , $types );
}
return $name ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return mixed
*/
2017-08-08 03:01:11 +08:00
public function getResponse ()
{
return $this -> response ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return array
*/
2017-08-08 03:01:11 +08:00
public function getStorages ()
{
return $this -> storages ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param string $name
* @ return object | null
*/
2017-08-08 03:01:11 +08:00
public function getStorage ( $name )
{
return isset ( $this -> storages [ $name ]) ? $this -> storages [ $name ] : null ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return array
*/
2017-08-08 03:01:11 +08:00
public function getGrantTypes ()
{
return $this -> grantTypes ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param string $name
* @ return object | null
*/
2017-08-08 03:01:11 +08:00
public function getGrantType ( $name )
{
return isset ( $this -> grantTypes [ $name ]) ? $this -> grantTypes [ $name ] : null ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return array
*/
2017-08-08 03:01:11 +08:00
public function getResponseTypes ()
{
return $this -> responseTypes ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param string $name
* @ return object | null
*/
2017-08-08 03:01:11 +08:00
public function getResponseType ( $name )
{
// for multiple-valued response types - make them alphabetical
$name = $this -> normalizeResponseType ( $name );
return isset ( $this -> responseTypes [ $name ]) ? $this -> responseTypes [ $name ] : null ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return TokenTypeInterface
*/
2017-08-08 03:01:11 +08:00
public function getTokenType ()
{
return $this -> tokenType ;
}
2020-04-30 21:43:07 +08:00
/**
* @ return ClientAssertionTypeInterface
*/
2017-08-08 03:01:11 +08:00
public function getClientAssertionType ()
{
return $this -> clientAssertionType ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param string $name
* @ param mixed $value
*/
2017-08-08 03:01:11 +08:00
public function setConfig ( $name , $value )
{
$this -> config [ $name ] = $value ;
}
2020-04-30 21:43:07 +08:00
/**
* @ param string $name
* @ param mixed $default
* @ return mixed
*/
2017-08-08 03:01:11 +08:00
public function getConfig ( $name , $default = null )
{
return isset ( $this -> config [ $name ]) ? $this -> config [ $name ] : $default ;
}
2020-04-30 21:43:07 +08:00
/**
* Check if user has already connected to oauth server previously and got an associated ID .
*
* @ param string $username
* Username of an LDAP user ( often uid )
*
* @ return bool
* True if user has already an ID , false else .
*/
public function userExists ( $username )
{
if ( isset ( $this -> storages [ 'authorization_code' ])) {
// Sanitize and format username
$user = strtolower ( strip_tags ( htmlspecialchars ( $username )));
// Use getUsersID() method to retrieve associated ID, if an ID is returned so user already exists.
if ( $this -> storages [ 'authorization_code' ] -> getUsersID ( $user )) {
return true ;
}
else {
return false ;
}
}
else {
// Storage error
throw new Exception ( 'Fatal Error : Storage is undifined' );
}
}
2017-08-08 03:01:11 +08:00
}