Added the user_credentials and refresh_token grants. Fixed expires_in so it is inline with the spec, but added expires for the old usage of expires_in. Made redirect_uri in oauth_sessions ALLOW NULL since user_credential grants don't have a redirect

This commit is contained in:
Daniel Horrigan 2012-12-19 16:12:48 -05:00
parent 41d8a1efa3
commit 0f6f5e2939
3 changed files with 214 additions and 15 deletions

View File

@ -21,11 +21,12 @@ CREATE TABLE `client_endpoints` (
CREATE TABLE `oauth_sessions` ( CREATE TABLE `oauth_sessions` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT, `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`client_id` varchar(40) NOT NULL DEFAULT '', `client_id` varchar(40) NOT NULL DEFAULT '',
`redirect_uri` varchar(250) NOT NULL DEFAULT '', `redirect_uri` varchar(250) DEFAULT '',
`owner_type` enum('user','client') NOT NULL DEFAULT 'user', `owner_type` enum('user','client') NOT NULL DEFAULT 'user',
`owner_id` varchar(255) DEFAULT NULL, `owner_id` varchar(255) DEFAULT NULL,
`auth_code` varchar(40) DEFAULT '', `auth_code` varchar(40) DEFAULT '',
`access_token` varchar(40) DEFAULT '', `access_token` varchar(40) DEFAULT '',
`refresh_token` varchar(40) NOT NULL,
`access_token_expires` int(10) DEFAULT NULL, `access_token_expires` int(10) DEFAULT NULL,
`stage` enum('requested','granted') NOT NULL DEFAULT 'requested', `stage` enum('requested','granted') NOT NULL DEFAULT 'requested',
`first_requested` int(10) unsigned NOT NULL, `first_requested` int(10) unsigned NOT NULL,
@ -56,4 +57,4 @@ CREATE TABLE `oauth_session_scopes` (
KEY `scope` (`scope`), KEY `scope` (`scope`),
CONSTRAINT `oauth_session_scopes_ibfk_3` FOREIGN KEY (`scope`) REFERENCES `scopes` (`scope`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `oauth_session_scopes_ibfk_3` FOREIGN KEY (`scope`) REFERENCES `scopes` (`scope`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `oauth_session_scopes_ibfk_4` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE CONSTRAINT `oauth_session_scopes_ibfk_4` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@ -77,6 +77,7 @@ interface Database
$typeId = null, $typeId = null,
$authCode = null, $authCode = null,
$accessToken = null, $accessToken = null,
$refreshToken = null,
$accessTokenExpire = null, $accessTokenExpire = null,
$stage = 'requested' $stage = 'requested'
); );
@ -102,6 +103,7 @@ interface Database
$sessionId, $sessionId,
$authCode = null, $authCode = null,
$accessToken = null, $accessToken = null,
$refreshToken = null,
$accessTokenExpire = null, $accessTokenExpire = null,
$stage = 'requested' $stage = 'requested'
); );
@ -125,6 +127,26 @@ interface Database
$typeId $typeId
); );
/**
* Update the refresh token
*
* Database query:
*
* <code>
* UPDATE oauth_sessions SET access_token = $newAccessToken, refresh_token =
* $newRefreshToken, access_toke_expires = $accessTokenExpires, last_updated = UNIX_TIMESTAMP(NOW()) WHERE
* refresh_token = $currentRefreshToken
* </code>
*
* @param string $currentRefreshToken The session's current refresh token
* @param string $newAccessToken The new access token for this session
* @param string $newRefreshToken The new refresh token for the session
* @param int $accessTokenExpires The UNIX timestamp of when the new token expires
* @return bool Whether the $currentRefreshToken was valid or not.
*/
public function refreshToken($currentRefreshToken, $newAccessToken, $newRefreshToken, $accessTokenExpires);
/** /**
* Validate that an authorisation code is valid * Validate that an authorisation code is valid
* *

View File

@ -30,8 +30,8 @@ class Server
* @var array * @var array
*/ */
private $_config = array( private $_config = array(
'scope_delimeter' => ',', 'scope_delimeter' => ',',
'access_token_ttl' => null 'access_token_ttl' => 3600
); );
/** /**
@ -47,7 +47,9 @@ class Server
* @var array * @var array
*/ */
private $_grantTypes = array( private $_grantTypes = array(
'authorization_code' 'authorization_code',
'user_credentials',
'refresh_token',
); );
/** /**
@ -75,16 +77,18 @@ class Server
* @var array * @var array
*/ */
public $errors = array( public $errors = array(
'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.', '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.', '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.', '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.', '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.', '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.', '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.', '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 is not supported by the authorization server', 'unsupported_grant_type' => 'The authorization grant type is not supported by the authorization server',
'invalid_client' => 'Client authentication failed', '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_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' => 'Invalid Credentials',
'invalid_refresh' => 'Invalid Refresh Token',
); );
/** /**
@ -284,7 +288,7 @@ class Server
* @param string $accessToken The access token (default = null) * @param string $accessToken The access token (default = null)
* @return string An authorisation code * @return string An authorisation code
*/ */
private function newAuthCode($clientId, $type, $typeId, $redirectUri, $scopes = array(), $accessToken = null) private function newAuthCode($clientId, $type, $typeId, $redirectUri, $scopes = array(), $accessToken = null, $refreshToken = null)
{ {
$authCode = $this->generateCode(); $authCode = $this->generateCode();
@ -299,6 +303,7 @@ class Server
$typeId, $typeId,
$authCode, $authCode,
$accessToken, $accessToken,
$refreshToken,
'requested' 'requested'
); );
@ -374,9 +379,17 @@ class Server
return $this->completeAuthCodeGrant($authParams, $params); return $this->completeAuthCodeGrant($authParams, $params);
break; break;
case 'user_credentials':
return $this->completeUserCredentialsGrant($authParams, $params);
break;
case 'refresh_token': // Refresh token case 'refresh_token': // Refresh token
return $this->completeRefreshTokenGrant($authParams, $params);
break;
case 'password': // Resource owner password credentials grant case 'password': // Resource owner password credentials grant
case 'client_credentials': // Client credentials grant case 'client_credentials': // Client credentials grant
default: // Unsupported default: // Unsupported
throw new ServerException($this->errors['server_error'] . 'Tried to process an unsuppported grant type.', 5); throw new ServerException($this->errors['server_error'] . 'Tried to process an unsuppported grant type.', 5);
break; break;
@ -479,16 +492,19 @@ class Server
// remove the authorisation code, change the stage to 'granted' // remove the authorisation code, change the stage to 'granted'
$accessToken = $this->generateCode(); $accessToken = $this->generateCode();
$refreshToken = $this->generateCode();
$accessTokenExpires = ($this->_config['access_token_ttl'] === null) ? $accessTokenExpires = ($this->_config['access_token_ttl'] === null) ?
null : null :
time() + $this->_config['access_token_ttl']; time() + $this->_config['access_token_ttl'];
$accessTokenExpiresIn = ($this->_config['access_token_ttl'] === null) ? 0 : $this->_config['access_token_ttl'];
$this->_dbCall( $this->_dbCall(
'updateSession', 'updateSession',
$session['id'], $session['id'],
null, null,
$accessToken, $accessToken,
$refreshToken,
$accessTokenExpires, $accessTokenExpires,
'granted' 'granted'
); );
@ -497,17 +513,177 @@ class Server
$this->_dbCall( $this->_dbCall(
'updateSessionScopeAccessToken', 'updateSessionScopeAccessToken',
$session['id'], $session['id'],
$accessToken $accessToken,
$refreshToken
); );
return array( return array(
'access_token' => $accessToken, 'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'token_type' => 'bearer', 'token_type' => 'bearer',
'expires_in' => $this->_config['access_token_ttl'] 'expires' => $accessTokenExpires,
'expires_in' => $accessTokenExpiresIn
); );
} }
} }
/**
* Complete the user credentials grant
*
* @access private
*
* @param array $authParams Array of parsed $_POST keys
* @param array $params Generated parameters from issueAccessToken()
*
* @return array Authorise request parameters
*/
private function completeUserCredentialsGrant($authParams = array(), $params = array())
{
$params = array();
if ( ! isset($authParams['user_auth_callback'])) {
throw new \InvalidArgumentException('You must set a user_auth_callback when using the user_credentials grant type.');
}
// Client ID
if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) {
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
} else {
$params['client_id'] = (isset($authParams['client_id'])) ?
$authParams['client_id'] :
$_POST['client_id'];
}
// Client secret
if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) {
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0);
} else {
$params['client_secret'] = (isset($authParams['client_secret'])) ?
$authParams['client_secret'] :
$_POST['client_secret'];
}
// Validate client ID and redirect URI
$clientDetails = $this->_dbCall(
'validateClient',
$params['client_id'],
$params['client_secret'],
null
);
if ($clientDetails === false) {
throw new \Oauth2\Authentication\ClientException($this->errors['invalid_client'], 8);
}
// Check for grant
if ( ! isset($_POST['grant_type'])) {
throw new \Oauth2\Authentication\ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
} else {
$params['grant_type'] = $_POST['grant_type'];
}
if ($params['grant_type'] == 'user_credentials')
{
// Check if user's u+p are correct
$userId = call_user_func($authParams['user_auth_callback']);
if ($userId === false)
{
throw new \Oauth2\Authentication\ClientException($this->errors['invalid_credentials'], 0);
}
else
{
// Generate an access token
$accessToken = $this->generateCode();
$refreshToken = $this->generateCode();
$accessTokenExpires = ($this->_config['access_token_ttl'] === null) ?
null :
time() + $this->_config['access_token_ttl'];
$accessTokenExpiresIn = ($this->_config['access_token_ttl'] === null) ? 0 : $this->_config['access_token_ttl'];
// Delete any existing sessions just to be sure
$this->_dbCall('deleteSession', $params['client_id'], 'user', $userId);
// Create a new session
$this->_dbCall('newSession', $params['client_id'], null, 'user', $userId, null, $accessToken, $refreshToken, $accessTokenExpires, 'granted');
return array(
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'token_type' => 'bearer',
'expires' => $accessTokenExpires,
'expires_in' => $accessTokenExpiresIn
);
}
} else {
throw new \Oauth2\Authentication\ClientException($this->errors['unsupported_grant_type'], 7);
}
}
/**
* Complete the refresh token grant
*
* @access private
*
* @param array $authParams Array of parsed $_POST keys
* @param array $params Generated parameters from issueAccessToken()
*
* @return array Authorise request parameters
*/
private function completeRefreshTokenGrant($authParams = array(), $params = array())
{
$params = array();
// Check for grant
if ( ! isset($_POST['grant_type'])) {
throw new \Oauth2\Authentication\ClientException(sprintf($this->errors['invalid_request'], 'grant_type'), 0);
} else {
$params['grant_type'] = $_POST['grant_type'];
}
if ( ! isset($authParams['refresh_token']) && ! isset($_POST['refresh_token'])) {
throw new ClientException(sprintf($this->errors['invalid_request'], 'refresh_token'), 0);
} else {
$params['refresh_token'] = (isset($authParams['refresh_token'])) ?
$authParams['refresh_token'] :
$_POST['refresh_token'];
}
if ($params['grant_type'] == 'refresh_token')
{
// Generate an access token
$accessToken = $this->generateCode();
$refreshToken = $this->generateCode();
$accessTokenExpires = ($this->_config['access_token_ttl'] === null) ?
null :
time() + $this->_config['access_token_ttl'];
$accessTokenExpiresIn = ($this->_config['access_token_ttl'] === null) ? 0 : $this->_config['access_token_ttl'];
// Delete any existing sessions just to be sure
$result = $this->_dbCall('refreshToken', $params['refresh_token'], $accessToken, $refreshToken, $accessTokenExpires);
if ( ! $result) {
throw new \Oauth2\Authentication\ClientException($this->errors['invalid_refresh'], 0);
}
return array(
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'token_type' => 'bearer',
'expires' => $accessTokenExpires,
'expires_in' => $accessTokenExpiresIn
);
} else {
throw new \Oauth2\Authentication\ClientException($this->errors['unsupported_grant_type'], 7);
}
}
/** /**
* Generates the redirect uri with appended params * Generates the redirect uri with appended params
* *