Merge branch 'feature/refresh-token-rotation' into develop

This commit is contained in:
Alex Bilbie 2013-05-10 16:13:22 -07:00
commit e591fbb25c
2 changed files with 85 additions and 17 deletions

View File

@ -54,6 +54,12 @@ class RefreshToken implements GrantTypeInterface {
*/ */
protected $refreshTokenTTL = 604800; protected $refreshTokenTTL = 604800;
/**
* Rotate refresh tokens
* @var boolean
*/
protected $rotateRefreshTokens = false;
/** /**
* Constructor * Constructor
* @param Authorization $authServer Authorization server instance * @param Authorization $authServer Authorization server instance
@ -111,6 +117,16 @@ class RefreshToken implements GrantTypeInterface {
return $this->refreshTokenTTL; return $this->refreshTokenTTL;
} }
/**
* When a new access is token, expire the refresh token used and issue a new one.
* @param boolean $rotateRefreshTokens Set to true to enable (default = false)
* @return void
*/
public function rotateRefreshTokens($rotateRefreshTokens = false)
{
$this->rotateRefreshTokens = $rotateRefreshTokens;
}
/** /**
* Complete the refresh token grant * Complete the refresh token grant
* @param null|array $inputParams * @param null|array $inputParams
@ -160,24 +176,32 @@ class RefreshToken implements GrantTypeInterface {
$accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL(); $accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL();
$accessTokenExpires = time() + $accessTokenExpiresIn; $accessTokenExpires = time() + $accessTokenExpiresIn;
// Generate a new refresh token
$refreshToken = SecureKey::make();
$refreshTokenExpires = time() + $this->getRefreshTokenTTL();
// Revoke the old refresh token
$this->authServer->getStorage('session')->removeRefreshToken($authParams['refresh_token']);
// Associate the new access token with the session // Associate the new access token with the session
$newAccessTokenId = $this->authServer->getStorage('session')->associateAccessToken($accessTokenDetails['session_id'], $accessToken, $accessTokenExpires); $newAccessTokenId = $this->authServer->getStorage('session')->associateAccessToken($accessTokenDetails['session_id'], $accessToken, $accessTokenExpires);
// There isn't a request for reduced scopes so assign the original ones if ($this->rotateRefreshTokens === true) {
// Generate a new refresh token
$refreshToken = SecureKey::make();
$refreshTokenExpires = time() + $this->getRefreshTokenTTL();
// Revoke the old refresh token
$this->authServer->getStorage('session')->removeRefreshToken($authParams['refresh_token']);
// Associate the new refresh token with the new access token
$this->authServer->getStorage('session')->associateRefreshToken($newAccessTokenId, $refreshToken, $refreshTokenExpires, $authParams['client_id']);
}
// There isn't a request for reduced scopes so assign the original ones (or we're not rotating scopes)
if ( ! isset($authParams['scope'])) { if ( ! isset($authParams['scope'])) {
foreach ($scopes as $scope) { foreach ($scopes as $scope) {
$this->authServer->getStorage('session')->associateScope($newAccessTokenId, $scope['id']); $this->authServer->getStorage('session')->associateScope($newAccessTokenId, $scope['id']);
} }
} else {
// The request is asking for reduced scopes } elseif ( isset($authParams['scope']) && $this->rotateRefreshTokens === true) {
// The request is asking for reduced scopes and rotate tokens is enabled
$reqestedScopes = explode($this->authServer->getScopeDelimeter(), $authParams['scope']); $reqestedScopes = explode($this->authServer->getScopeDelimeter(), $authParams['scope']);
for ($i = 0; $i < count($reqestedScopes); $i++) { for ($i = 0; $i < count($reqestedScopes); $i++) {
@ -202,16 +226,18 @@ class RefreshToken implements GrantTypeInterface {
} }
} }
// Associate the new refresh token with the new access token $response = array(
$this->authServer->getStorage('session')->associateRefreshToken($newAccessTokenId, $refreshToken, $refreshTokenExpires, $authParams['client_id']);
return array(
'access_token' => $accessToken, 'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'token_type' => 'bearer', 'token_type' => 'bearer',
'expires' => $accessTokenExpires, 'expires' => $accessTokenExpires,
'expires_in' => $accessTokenExpiresIn 'expires_in' => $accessTokenExpiresIn
); );
if ($this->rotateRefreshTokens === true) {
$response['refresh_token'] = $refreshToken;
}
return $response;
} }
} }

View File

@ -204,7 +204,6 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase
$this->assertArrayHasKey('token_type', $v); $this->assertArrayHasKey('token_type', $v);
$this->assertArrayHasKey('expires', $v); $this->assertArrayHasKey('expires', $v);
$this->assertArrayHasKey('expires_in', $v); $this->assertArrayHasKey('expires_in', $v);
$this->assertArrayHasKey('refresh_token', $v);
$this->assertEquals($a->getAccessTokenTTL(), $v['expires_in']); $this->assertEquals($a->getAccessTokenTTL(), $v['expires_in']);
$this->assertEquals(time()+$a->getAccessTokenTTL(), $v['expires']); $this->assertEquals(time()+$a->getAccessTokenTTL(), $v['expires']);
@ -240,6 +239,48 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase
'refresh_token' => 'abcdef', 'refresh_token' => 'abcdef',
)); ));
$this->assertArrayHasKey('access_token', $v);
$this->assertArrayHasKey('token_type', $v);
$this->assertArrayHasKey('expires', $v);
$this->assertArrayHasKey('expires_in', $v);
$this->assertEquals($a->getAccessTokenTTL(), $v['expires_in']);
$this->assertEquals(time()+$a->getAccessTokenTTL(), $v['expires']);
}
public function test_issueAccessToken_refreshTokenGrant_rotateTokens()
{
$this->client->shouldReceive('getClient')->andReturn(array(
'client_id' => 1234,
'client_secret' => 5678,
'redirect_uri' => 'http://foo/redirect',
'name' => 'Example Client'
));
$this->session->shouldReceive('validateRefreshToken')->andReturn(1);
$this->session->shouldReceive('validateAuthCode')->andReturn(1);
$this->session->shouldReceive('updateSession')->andReturn(null);
$this->session->shouldReceive('updateRefreshToken')->andReturn(null);
$this->session->shouldReceive('getAccessToken')->andReturn(null);
$this->session->shouldReceive('getScopes')->andReturn(array('id' => 1));
$this->session->shouldReceive('associateAccessToken')->andReturn(1);
$this->session->shouldReceive('associateRefreshToken')->andReturn(1);
$this->session->shouldReceive('removeRefreshToken')->andReturn(1);
$this->session->shouldReceive('associateScope')->andReturn(null);
$a = $this->returnDefault();
$rt = new League\OAuth2\Server\Grant\RefreshToken($a);
$rt->rotateRefreshTokens(true);
$a->addGrantType($rt);
$v = $a->issueAccessToken(array(
'grant_type' => 'refresh_token',
'client_id' => 1234,
'client_secret' => 5678,
'refresh_token' => 'abcdef',
));
$this->assertArrayHasKey('access_token', $v); $this->assertArrayHasKey('access_token', $v);
$this->assertArrayHasKey('token_type', $v); $this->assertArrayHasKey('token_type', $v);
$this->assertArrayHasKey('expires', $v); $this->assertArrayHasKey('expires', $v);
@ -286,7 +327,6 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase
$this->assertArrayHasKey('token_type', $v); $this->assertArrayHasKey('token_type', $v);
$this->assertArrayHasKey('expires', $v); $this->assertArrayHasKey('expires', $v);
$this->assertArrayHasKey('expires_in', $v); $this->assertArrayHasKey('expires_in', $v);
$this->assertArrayHasKey('refresh_token', $v);
$this->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']); $this->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']);
$this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']); $this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']);
@ -318,6 +358,7 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase
$a = $this->returnDefault(); $a = $this->returnDefault();
$grant = new League\OAuth2\Server\Grant\RefreshToken($a); $grant = new League\OAuth2\Server\Grant\RefreshToken($a);
$grant->setAccessTokenTTL(30); $grant->setAccessTokenTTL(30);
$grant->rotateRefreshTokens(true);
$a->addGrantType($grant); $a->addGrantType($grant);
$v = $a->issueAccessToken(array( $v = $a->issueAccessToken(array(
@ -368,6 +409,7 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase
$a = $this->returnDefault(); $a = $this->returnDefault();
$grant = new League\OAuth2\Server\Grant\RefreshToken($a); $grant = new League\OAuth2\Server\Grant\RefreshToken($a);
$grant->setAccessTokenTTL(30); $grant->setAccessTokenTTL(30);
$grant->rotateRefreshTokens(true);
$a->addGrantType($grant); $a->addGrantType($grant);
$a->issueAccessToken(array( $a->issueAccessToken(array(