2013-01-05 01:14:02 +05:30
< ? php
2013-02-13 02:03:23 +05:30
/**
2013-02-20 18:10:42 +05:30
* OAuth 2.0 Authorization Server
2013-02-13 02:03:23 +05:30
*
* @ package lncd / oauth2
* @ author Alex Bilbie < hello @ alexbilbie . com >
* @ copyright Copyright ( c ) 2013 University of Lincoln
* @ license http :// mit - license . org /
* @ link http :// github . com / lncd / oauth2
*/
2013-01-05 01:14:02 +05:30
namespace OAuth2 ;
2013-02-05 21:25:38 +05:30
use OAuth2\Util\Request ;
2013-01-29 22:23:39 +05:30
use OAuth2\Util\SecureKey ;
2013-01-29 19:46:47 +05:30
use OAuth2\Storage\SessionInterface ;
use OAuth2\Storage\ClientInterface ;
use OAuth2\Storage\ScopeInterface ;
2013-02-01 20:11:10 +05:30
use OAuth2\Grant\GrantTypeInterface ;
2013-01-29 19:46:47 +05:30
2013-02-13 02:03:23 +05:30
/**
2013-02-20 18:10:42 +05:30
* OAuth 2.0 authorization server class
2013-02-13 02:03:23 +05:30
*/
2013-01-22 22:03:09 +05:30
class AuthServer
2013-01-05 01:14:02 +05:30
{
2013-01-29 19:48:13 +05:30
/**
* The delimeter between scopes specified in the scope query string parameter
*
* The OAuth 2 specification states it should be a space but that is stupid
* and everyone excepted Google use a comma instead .
*
* @ var string
*/
2013-01-05 01:14:02 +05:30
protected $scopeDelimeter = ',' ;
2013-02-13 02:03:23 +05:30
/**
* The TTL ( time to live ) of an access token in seconds ( default : 3600 )
* @ var integer
*/
2013-03-06 22:29:18 +05:30
protected $expiresIn = 3600 ;
2013-01-05 01:14:02 +05:30
2013-02-13 02:03:23 +05:30
/**
* The registered grant response types
* @ var array
*/
2013-01-29 19:47:56 +05:30
protected $responseTypes = array ();
2013-01-05 01:14:02 +05:30
2013-02-13 02:03:23 +05:30
/**
* The client , scope and session storage classes
* @ var array
*/
2013-03-06 22:29:18 +05:30
protected $storages = array ();
2013-01-05 01:14:02 +05:30
2013-02-13 02:03:23 +05:30
/**
* The registered grant types
* @ var array
*/
2013-03-06 22:29:18 +05:30
protected $grantTypes = array ();
2013-01-05 01:14:02 +05:30
2013-03-04 18:40:00 +05:30
/**
* Require the " scope " parameter to be in checkAuthoriseParams ()
* @ var boolean
*/
2013-03-04 18:45:12 +05:30
protected $requireScopeParam = true ;
2013-03-04 21:16:02 +05:30
/**
* Default scope to be used if none is provided and requireScopeParam is false
* @ var string
*/
protected $defaultScope = null ;
2013-03-04 18:45:12 +05:30
/**
* Require the " state " parameter to be in checkAuthoriseParams ()
* @ var boolean
*/
protected $requireStateParam = false ;
2013-03-04 18:40:00 +05:30
2013-02-13 02:03:23 +05:30
/**
* The request object
* @ var Util\RequestInterface
*/
2013-03-06 22:29:18 +05:30
protected $request = null ;
2013-01-22 21:55:51 +05:30
2013-01-29 19:49:23 +05:30
/**
* Exception error codes
* @ var array
*/
2013-02-15 21:59:00 +05:30
protected static $exceptionCodes = array (
2013-01-29 19:49:23 +05:30
0 => 'invalid_request' ,
1 => 'unauthorized_client' ,
2 => 'access_denied' ,
3 => 'unsupported_response_type' ,
4 => 'invalid_scope' ,
5 => 'server_error' ,
6 => 'temporarily_unavailable' ,
7 => 'unsupported_grant_type' ,
8 => 'invalid_client' ,
9 => 'invalid_grant'
);
/**
* Exception error messages
* @ var array
*/
2013-03-06 22:29:18 +05:30
protected static $exceptionMessages = array (
2013-01-29 19:49:23 +05:30
'invalid_request' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.' ,
'unauthorized_client' => 'The client is not authorized to request an access token using this method.' ,
'access_denied' => 'The resource owner or authorization server denied the request.' ,
'unsupported_response_type' => 'The authorization server does not support obtaining an access token using this method.' ,
'invalid_scope' => 'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.' ,
'server_error' => 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' ,
'temporarily_unavailable' => 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.' ,
'unsupported_grant_type' => 'The authorization grant type "%s" is not supported by the authorization server' ,
'invalid_client' => 'Client authentication failed' ,
'invalid_grant' => 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Check the "%s" parameter.' ,
'invalid_credentials' => 'The user credentials were incorrect.' ,
'invalid_refresh' => 'The refresh token is invalid.' ,
);
2013-01-05 01:14:02 +05:30
2013-02-13 02:03:23 +05:30
/**
* Get an exception message
*
* @ param string $error The error message key
* @ return string The error message
*/
2013-02-01 20:11:10 +05:30
public static function getExceptionMessage ( $error = '' )
{
return self :: $exceptionMessages [ $error ];
}
2013-02-15 21:59:00 +05:30
/**
* Get an exception code
*
* @ param integer $code The exception code
* @ return string The exception code type
*/
public static function getExceptionType ( $code = 0 )
{
return self :: $exceptionCodes [ $code ];
}
2013-02-13 02:03:23 +05:30
/**
2013-02-20 18:10:42 +05:30
* Create a new OAuth2 authorization server
2013-02-13 02:03:23 +05:30
*
* @ param ClientInterface $client A class which inherits from Storage / ClientInterface
* @ param SessionInterface $session A class which inherits from Storage / SessionInterface
* @ param ScopeInterface $scope A class which inherits from Storage / ScopeInterface
*/
2013-01-29 19:46:47 +05:30
public function __construct ( ClientInterface $client , SessionInterface $session , ScopeInterface $scope )
{
2013-03-06 22:29:18 +05:30
$this -> storages = array (
2013-01-29 19:46:47 +05:30
'client' => $client ,
'session' => $session ,
'scope' => $scope
);
2013-01-05 01:14:02 +05:30
}
2013-02-13 02:03:23 +05:30
/**
* Enable support for a grant
* @ param GrantTypeInterface $grantType A grant class which conforms to Interface / GrantTypeInterface
* @ param null | string $identifier An identifier for the grant ( autodetected if not passed )
*/
2013-01-29 19:47:56 +05:30
public function addGrantType ( GrantTypeInterface $grantType , $identifier = null )
2013-01-05 01:14:02 +05:30
{
if ( is_null ( $identifier )) {
2013-01-29 19:47:56 +05:30
$identifier = $grantType -> getIdentifier ();
}
2013-03-06 22:29:18 +05:30
$this -> grantTypes [ $identifier ] = $grantType ;
2013-01-29 19:47:56 +05:30
2013-01-29 21:51:53 +05:30
if ( ! is_null ( $grantType -> getResponseType ())) {
2013-01-29 19:47:56 +05:30
$this -> responseTypes [] = $grantType -> getResponseType ();
2013-01-05 01:14:02 +05:30
}
}
2013-02-13 02:03:23 +05:30
/**
* Check if a grant type has been enabled
* @ param string $identifier The grant type identifier
* @ return boolean Returns " true " if enabled , " false " if not
*/
2013-03-06 22:29:18 +05:30
public function hasGrantType ( $identifier )
2013-02-01 20:11:10 +05:30
{
2013-03-06 22:29:18 +05:30
return ( array_key_exists ( $identifier , $this -> grantTypes ));
2013-02-01 20:11:10 +05:30
}
2013-03-04 18:40:00 +05:30
/**
* Require the " scope " paremter in checkAuthoriseParams ()
* @ param boolean $require
* @ return void
*/
2013-03-04 18:45:12 +05:30
public function requireScopeParam ( $require = true )
{
$this -> requireScopeParam = $require ;
}
2013-03-21 21:52:29 +05:30
/**
* Is the scope parameter required ?
* @ return bool
*/
public function scopeParamRequired ()
{
return $this -> requireScopeParam ;
}
2013-03-04 21:16:02 +05:30
/**
* Default scope to be used if none is provided and requireScopeParam is false
* @ var string
*/
2013-03-21 21:52:16 +05:30
public function setDefaultScope ( $default = null )
2013-03-04 21:16:02 +05:30
{
$this -> defaultScope = $default ;
}
2013-03-21 21:52:44 +05:30
/**
* Default scope to be used if none is provided and requireScopeParam is false
* @ return string | null
*/
public function getDefaultScope ()
{
return $this -> defaultScope ;
}
2013-03-04 18:45:12 +05:30
/**
* Require the " state " paremter in checkAuthoriseParams ()
* @ param boolean $require
* @ return void
*/
public function requireStateParam ( $require = false )
2013-03-04 18:40:00 +05:30
{
2013-03-04 18:45:12 +05:30
$this -> requireStateParam = $require ;
2013-03-04 18:40:00 +05:30
}
2013-02-13 02:03:23 +05:30
/**
* Get the scope delimeter
*
* @ return string The scope delimiter ( default : " , " )
*/
2013-01-22 21:55:51 +05:30
public function getScopeDelimeter ()
{
return $this -> scopeDelimeter ;
}
2013-02-13 02:03:23 +05:30
/**
* Set the scope delimiter
*
* @ param string $scopeDelimeter
*/
public function setScopeDelimeter ( $scopeDelimeter )
2013-01-05 01:14:02 +05:30
{
2013-02-13 02:03:23 +05:30
$this -> scopeDelimeter = $scopeDelimeter ;
2013-01-05 01:14:02 +05:30
}
2013-02-13 02:03:23 +05:30
/**
* Get the TTL for an access token
* @ return int The TTL
*/
2013-03-06 22:29:18 +05:30
public function getExpiresIn ()
2013-01-22 21:55:51 +05:30
{
2013-03-06 22:29:18 +05:30
return $this -> expiresIn ;
2013-01-22 21:55:51 +05:30
}
2013-02-13 02:03:23 +05:30
/**
* Set the TTL for an access token
* @ param int $expiresIn The new TTL
*/
2013-02-01 20:11:10 +05:30
public function setExpiresIn ( $expiresIn )
2013-01-05 01:14:02 +05:30
{
2013-03-06 22:29:18 +05:30
$this -> expiresIn = $expiresIn ;
2013-01-05 01:14:02 +05:30
}
2013-01-22 21:55:51 +05:30
/**
* Sets the Request Object
*
2013-02-13 02:03:23 +05:30
* @ param Util\RequestInterface The Request Object
2013-01-22 21:55:51 +05:30
*/
2013-02-05 21:25:38 +05:30
public function setRequest ( Util\RequestInterface $request )
2013-01-22 21:55:51 +05:30
{
2013-03-06 22:29:18 +05:30
$this -> request = $request ;
2013-01-22 21:55:51 +05:30
}
/**
* Gets the Request object . It will create one from the globals if one is not set .
*
2013-02-13 02:03:23 +05:30
* @ return Util\RequestInterface
2013-01-22 21:55:51 +05:30
*/
2013-03-06 22:29:18 +05:30
public function getRequest ()
2013-01-22 21:55:51 +05:30
{
2013-03-06 22:29:18 +05:30
if ( $this -> request === null ) {
2013-02-05 01:15:34 +05:30
// @codeCoverageIgnoreStart
2013-03-06 22:29:18 +05:30
$this -> request = Request :: buildFromGlobals ();
2013-02-05 01:15:34 +05:30
2013-01-22 21:55:51 +05:30
}
2013-02-05 01:15:34 +05:30
// @codeCoverageIgnoreEnd
2013-01-22 21:55:51 +05:30
2013-03-06 22:29:18 +05:30
return $this -> request ;
2013-01-22 21:55:51 +05:30
}
2013-02-13 02:03:23 +05:30
/**
* Return a storage class
* @ param string $obj The class required
* @ return Storage\ClientInterface | Storage\ScopeInterface | Storage\SessionInterface
*/
2013-03-06 22:29:18 +05:30
public function getStorage ( $obj )
2013-01-29 19:46:47 +05:30
{
2013-03-06 22:29:18 +05:30
return $this -> storages [ $obj ];
2013-01-29 19:46:47 +05:30
}
2013-01-22 21:55:51 +05:30
/**
2013-01-29 21:55:14 +05:30
* Check authorise parameters
2013-01-22 21:55:51 +05:30
*
2013-02-01 20:11:10 +05:30
* @ param array $inputParams Optional array of parsed $_GET keys
2013-02-13 02:03:23 +05:30
* @ throws \OAuth2\Exception\ClientException
2013-01-22 21:55:51 +05:30
* @ return array Authorise request parameters
*/
2013-02-01 20:11:10 +05:30
public function checkAuthoriseParams ( $inputParams = array ())
2013-01-22 21:55:51 +05:30
{
2013-02-19 06:10:30 +05:30
// Auth params
2013-03-06 22:29:18 +05:30
$authParams = $this -> getParam ( array ( 'client_id' , 'redirect_uri' , 'response_type' , 'scope' , 'state' ), 'get' , $inputParams );
2013-01-29 20:26:17 +05:30
2013-02-01 20:11:10 +05:30
if ( is_null ( $authParams [ 'client_id' ])) {
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'invalid_request' ], 'client_id' ), 0 );
2013-01-29 20:26:17 +05:30
}
2013-02-01 20:11:10 +05:30
if ( is_null ( $authParams [ 'redirect_uri' ])) {
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'invalid_request' ], 'redirect_uri' ), 0 );
2013-01-29 20:26:17 +05:30
}
2013-03-04 21:01:59 +05:30
if ( $this -> requireStateParam === true && is_null ( $authParams [ 'state' ])) {
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'invalid_request' ], 'state' ), 0 );
2013-03-04 18:45:12 +05:30
}
2013-01-29 20:26:17 +05:30
// Validate client ID and redirect URI
2013-03-06 23:31:12 +05:30
$clientDetails = $this -> getStorage ( 'client' ) -> getClient ( $authParams [ 'client_id' ], null , $authParams [ 'redirect_uri' ]);
2013-01-29 20:26:17 +05:30
if ( $clientDetails === false ) {
2013-02-01 20:11:10 +05:30
throw new Exception\ClientException ( self :: $exceptionMessages [ 'invalid_client' ], 8 );
2013-01-29 20:26:17 +05:30
}
2013-02-01 20:11:10 +05:30
$authParams [ 'client_details' ] = $clientDetails ;
2013-01-29 20:26:17 +05:30
2013-02-01 20:11:10 +05:30
if ( is_null ( $authParams [ 'response_type' ])) {
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'invalid_request' ], 'response_type' ), 0 );
2013-01-29 20:26:17 +05:30
}
// Ensure response type is one that is recognised
2013-02-01 20:11:10 +05:30
if ( ! in_array ( $authParams [ 'response_type' ], $this -> responseTypes )) {
throw new Exception\ClientException ( self :: $exceptionMessages [ 'unsupported_response_type' ], 3 );
2013-01-29 20:26:17 +05:30
}
2013-02-19 06:10:30 +05:30
// Validate scopes
$scopes = explode ( $this -> scopeDelimeter , $authParams [ 'scope' ]);
2013-01-29 20:26:17 +05:30
for ( $i = 0 ; $i < count ( $scopes ); $i ++ ) {
$scopes [ $i ] = trim ( $scopes [ $i ]);
if ( $scopes [ $i ] === '' ) unset ( $scopes [ $i ]); // Remove any junk scopes
}
2013-03-04 18:45:12 +05:30
if ( $this -> requireScopeParam === true && count ( $scopes ) === 0 ) {
2013-02-01 20:11:10 +05:30
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'invalid_request' ], 'scope' ), 0 );
2013-03-04 21:16:02 +05:30
} elseif ( count ( $scopes ) === 0 && $this -> defaultScope ) {
$scopes = array ( $this -> defaultScope );
2013-01-29 20:26:17 +05:30
}
2013-02-01 20:11:10 +05:30
$authParams [ 'scopes' ] = array ();
2013-01-29 20:26:17 +05:30
foreach ( $scopes as $scope ) {
2013-03-06 23:31:12 +05:30
$scopeDetails = $this -> getStorage ( 'scope' ) -> getScope ( $scope );
2013-01-29 20:26:17 +05:30
if ( $scopeDetails === false ) {
2013-02-01 20:11:10 +05:30
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'invalid_scope' ], $scope ), 4 );
2013-01-29 20:26:17 +05:30
}
2013-02-01 20:11:10 +05:30
$authParams [ 'scopes' ][] = $scopeDetails ;
2013-01-29 20:26:17 +05:30
}
2013-02-01 20:11:10 +05:30
return $authParams ;
2013-01-22 21:55:51 +05:30
}
/**
* Parse a new authorise request
*
2013-02-13 02:03:23 +05:30
* @ param string $type The session owner ' s type
* @ param string $typeId The session owner ' s ID
* @ param array $authParams The authorise request $_GET parameters
* @ return string An authorisation code
2013-01-22 21:55:51 +05:30
*/
2013-02-01 20:11:10 +05:30
public function newAuthoriseRequest ( $type , $typeId , $authParams = array ())
2013-01-22 21:55:51 +05:30
{
2013-01-29 21:53:41 +05:30
// Generate an auth code
$authCode = SecureKey :: make ();
2013-01-22 21:55:51 +05:30
2013-01-29 21:53:41 +05:30
// Remove any old sessions the user might have
2013-03-06 23:31:12 +05:30
$this -> getStorage ( 'session' ) -> deleteSession ( $authParams [ 'client_id' ], $type , $typeId );
2013-01-29 21:53:41 +05:30
// Create a new session
2013-03-06 23:31:12 +05:30
$sessionId = $this -> getStorage ( 'session' ) -> createSession ( $authParams [ 'client_id' ], $authParams [ 'redirect_uri' ], $type , $typeId , $authCode );
2013-01-29 21:53:41 +05:30
// Associate scopes with the new session
2013-02-01 20:11:10 +05:30
foreach ( $authParams [ 'scopes' ] as $scope )
2013-01-29 21:53:41 +05:30
{
2013-03-06 23:31:12 +05:30
$this -> getStorage ( 'session' ) -> associateScope ( $sessionId , $scope [ 'id' ]);
2013-01-29 21:53:41 +05:30
}
return $authCode ;
2013-01-22 21:55:51 +05:30
}
/**
* Issue an access token
*
2013-02-01 20:11:10 +05:30
* @ param array $inputParams Optional array of parsed $_POST keys
2013-01-22 21:55:51 +05:30
* @ return array Authorise request parameters
*/
2013-02-01 20:11:10 +05:30
public function issueAccessToken ( $inputParams = array ())
2013-01-22 21:55:51 +05:30
{
2013-03-06 22:48:04 +05:30
$grantType = $this -> getParam ( 'grant_type' , 'post' , $inputParams );
2013-01-22 21:55:51 +05:30
2013-02-14 01:06:10 +05:30
if ( is_null ( $grantType )) {
2013-02-01 20:11:10 +05:30
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'invalid_request' ], 'grant_type' ), 0 );
2013-01-29 21:54:28 +05:30
}
2013-01-22 21:55:51 +05:30
2013-01-29 21:54:28 +05:30
// Ensure grant type is one that is recognised and is enabled
2013-03-06 22:29:18 +05:30
if ( ! in_array ( $grantType , array_keys ( $this -> grantTypes ))) {
2013-02-14 01:06:10 +05:30
throw new Exception\ClientException ( sprintf ( self :: $exceptionMessages [ 'unsupported_grant_type' ], $grantType ), 7 );
2013-01-29 21:54:28 +05:30
}
2013-01-22 21:55:51 +05:30
2013-01-29 21:54:48 +05:30
// Complete the flow
2013-02-14 01:06:10 +05:30
return $this -> getGrantType ( $grantType ) -> completeFlow ( $inputParams );
2013-01-29 21:54:48 +05:30
}
2013-01-22 21:55:51 +05:30
2013-02-13 02:03:23 +05:30
/**
* Return a grant type class
* @ param string $grantType The grant type identifer
* @ return class
*/
2013-03-06 23:31:34 +05:30
public function getGrantType ( $grantType )
2013-01-29 21:54:48 +05:30
{
2013-03-06 22:29:18 +05:30
return $this -> grantTypes [ $grantType ];
2013-01-22 21:55:51 +05:30
}
2013-02-13 23:55:10 +05:30
/**
* Get a parameter from passed input parameters or the Request class
2013-03-20 20:25:42 +05:30
* @ param string | array $param Required parameter
2013-02-13 23:55:10 +05:30
* @ param string $method Get / put / post / delete
* @ param array $inputParams Passed input parameters
* @ return mixed 'Null' if parameter is missing
*/
2013-03-06 22:48:13 +05:30
public function getParam ( $param = '' , $method = 'get' , $inputParams = array (), $default = null )
2013-02-13 23:55:10 +05:30
{
if ( is_string ( $param )) {
2013-03-22 17:14:53 +05:30
if ( isset ( $inputParams [ $param ])) {
2013-03-22 16:10:47 +05:30
return $inputParams [ $param ];
2013-03-22 17:14:53 +05:30
} elseif ( $param === 'client_id' && ! is_null ( $client_id = $this -> getRequest () -> server ( 'PHP_AUTH_USER' ))) {
2013-03-22 16:10:47 +05:30
return $client_id ;
2013-03-22 17:14:53 +05:30
} elseif ( $param === 'client_secret' && ! is_null ( $client_secret = $this -> getRequest () -> server ( 'PHP_AUTH_PW' ))) {
2013-03-22 16:10:47 +05:30
return $client_secret ;
2013-03-22 17:14:53 +05:30
} else {
return $this -> getRequest () -> { $method }( $param , $default );
}
2013-02-13 23:55:10 +05:30
} else {
$response = array ();
foreach ( $param as $p ) {
2013-03-06 22:29:18 +05:30
$response [ $p ] = $this -> getParam ( $p , $method , $inputParams );
2013-02-13 23:55:10 +05:30
}
return $response ;
}
}
2013-01-05 01:14:02 +05:30
}