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-05-08 23:05:13 +05:30
protected $accessTokenTTL = 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-03-26 21:47:01 +05:30
/**
* Exception error HTTP status codes
* @ var array
2013-03-27 19:56:46 +05:30
*
2013-03-26 21:47:01 +05:30
* RFC 6749 , section 4.1 . 2.1 .:
2013-03-27 19:56:46 +05:30
* No 503 status code for 'temporarily_unavailable' , because
2013-03-26 21:47:01 +05:30
* " a 503 Service Unavailable HTTP status code cannot be
* returned to the client via an HTTP redirect "
*/
protected static $exceptionHttpStatusCodes = array (
'invalid_request' => 400 ,
'unauthorized_client' => 400 ,
'access_denied' => 401 ,
'unsupported_response_type' => 400 ,
'invalid_scope' => 400 ,
'server_error' => 500 ,
'temporarily_unavailable' => 400 ,
2013-03-27 19:56:46 +05:30
'unsupported_grant_type' => 501 ,
2013-03-26 21:47:01 +05:30
'invalid_client' => 401 ,
'invalid_grant' => 400 ,
'invalid_credentials' => 400 ,
'invalid_refresh' => 400 ,
);
2013-03-27 19:56:46 +05:30
2013-03-26 21:47:01 +05:30
/**
* Get all headers that have to be send with the error response
*
* @ param string $error The error message key
* @ return array Array with header values
*/
public static function getExceptionHttpHeaders ( $error )
{
$headers = array ();
switch ( self :: $exceptionHttpStatusCodes [ $error ]) {
case 401 :
$headers [] = 'HTTP/1.1 401 Unauthorized' ;
break ;
case 500 :
$headers [] = 'HTTP/1.1 500 Internal Server Error' ;
break ;
case 501 :
$headers [] = 'HTTP/1.1 501 Not Implemented' ;
break ;
case 400 :
default :
$headers [] = 'HTTP/1.1 400 Bad Request' ;
}
2013-03-27 19:56:46 +05:30
2013-03-26 21:47:01 +05:30
// Add "WWW-Authenticate" header
//
2013-03-27 19:56:46 +05:30
// RFC 6749, section 5.2.:
2013-03-26 21:47:01 +05:30
// "If the client attempted to authenticate via the 'Authorization'
// request header field, the authorization server MUST
// respond with an HTTP 401 (Unauthorized) status code and
// include the "WWW-Authenticate" response header field
// matching the authentication scheme used by the client.
2013-03-27 19:56:46 +05:30
// @codeCoverageIgnoreStart
2013-03-26 21:47:01 +05:30
if ( $error === 'invalid_client' ) {
2013-03-27 19:56:46 +05:30
$authScheme = null ;
2013-03-26 21:47:01 +05:30
$request = new Request ();
if ( $request -> server ( 'PHP_AUTH_USER' ) !== null ) {
2013-03-27 19:56:46 +05:30
$authScheme = 'Basic' ;
2013-03-26 21:47:01 +05:30
} else {
2013-03-27 19:56:46 +05:30
$authHeader = $request -> header ( 'Authorization' );
if ( $authHeader !== null ) {
if ( strpos ( $authHeader , 'Bearer' ) === 0 ) {
$authScheme = 'Bearer' ;
} elseif ( strpos ( $authHeader , 'Basic' ) === 0 ) {
$authScheme = 'Basic' ;
2013-03-26 21:47:01 +05:30
}
}
}
2013-03-27 19:56:46 +05:30
if ( $authScheme !== null ) {
$headers [] = 'WWW-Authenticate: ' . $authScheme . ' realm=""' ;
2013-03-26 21:47:01 +05:30
}
}
2013-03-27 19:56:46 +05:30
// @codeCoverageIgnoreEnd
2013-03-26 21:47:01 +05:30
return $headers ;
}
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-31 18:07:12 +05:30
public function getResponseTypes ()
{
return $this -> responseTypes ;
}
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-31 18:27:24 +05:30
/**
* Require the " state " paremter in checkAuthoriseParams ()
* @ param boolean $require
* @ return void
*/
public function stateParamRequired ()
{
return $this -> requireStateParam ;
}
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-05-08 23:05:13 +05:30
public function getAccessTokenTTL ()
2013-01-22 21:55:51 +05:30
{
2013-05-08 23:05:13 +05:30
return $this -> accessTokenTTL ;
2013-01-22 21:55:51 +05:30
}
2013-02-13 02:03:23 +05:30
/**
* Set the TTL for an access token
2013-05-08 03:46:30 +05:30
* @ param int $accessTokenTTL The new TTL
2013-02-13 02:03:23 +05:30
*/
2013-05-08 23:05:13 +05:30
public function setAccessTokenTTL ( $accessTokenTTL )
2013-01-05 01:14:02 +05:30
{
2013-05-08 03:46:30 +05:30
$this -> accessTokenTTL = $accessTokenTTL ;
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
/**
* 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-05-07 02:27:46 +05:30
if ( isset ( $this -> grantTypes [ $grantType ])) {
return $this -> grantTypes [ $grantType ];
}
throw new Exception\InvalidGrantTypeException ( sprintf ( self :: $exceptionMessages [ 'unsupported_grant_type' ], $grantType ), 9 );
}
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
}