From f4bcfee687df85bd4e0b68b1126d99b98abea042 Mon Sep 17 00:00:00 2001 From: Alex Bilbie Date: Thu, 9 May 2013 11:41:55 -0700 Subject: [PATCH] Update associated scopes if requested in refresh access token. Fixes #47 --- .../OAuth2/Server/Grant/RefreshToken.php | 41 ++++++++- tests/authorization/RefreshTokenTest.php | 88 +++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/League/OAuth2/Server/Grant/RefreshToken.php b/src/League/OAuth2/Server/Grant/RefreshToken.php index cf5dfe3b..6142f3f2 100644 --- a/src/League/OAuth2/Server/Grant/RefreshToken.php +++ b/src/League/OAuth2/Server/Grant/RefreshToken.php @@ -119,7 +119,7 @@ class RefreshToken implements GrantTypeInterface { public function completeFlow($inputParams = null) { // Get the required params - $authParams = $this->authServer->getParam(array('client_id', 'client_secret', 'refresh_token'), 'post', $inputParams); + $authParams = $this->authServer->getParam(array('client_id', 'client_secret', 'refresh_token', 'scope'), 'post', $inputParams); if (is_null($authParams['client_id'])) { throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'client_id'), 0); @@ -159,15 +159,50 @@ class RefreshToken implements GrantTypeInterface { $accessToken = SecureKey::make(); $accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL(); $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 $newAccessTokenId = $this->authServer->getStorage('session')->associateAccessToken($accessTokenDetails['session_id'], $accessToken, $accessTokenExpires); - foreach ($scopes as $scope) { - $this->authServer->getStorage('session')->associateScope($newAccessTokenId, $scope['id']); + // There isn't a request for reduced scopes so assign the original ones + if ( ! isset($authParams['scope'])) { + foreach ($scopes as $scope) { + $this->authServer->getStorage('session')->associateScope($newAccessTokenId, $scope['id']); + } + } else { + + // The request is asking for reduced scopes + $reqestedScopes = explode($this->authServer->getScopeDelimeter(), $authParams['scope']); + + for ($i = 0; $i < count($reqestedScopes); $i++) { + $reqestedScopes[$i] = trim($reqestedScopes[$i]); + if ($reqestedScopes[$i] === '') unset($reqestedScopes[$i]); // Remove any junk scopes + } + + // Check that there aren't any new scopes being included + $existingScopes = []; + foreach ($scopes as $s) { + $existingScopes[] = $s['scope']; + } + + foreach ($reqestedScopes as $reqScope) { + if ( ! in_array($reqScope, $existingScopes)) { + throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_request'), 'scope'), 0); + } + + // Associate with the new access token + $scopeDetails = $this->authServer->getStorage('scope')->getScope($reqScope, $authParams['client_id'], $this->identifier); + $this->authServer->getStorage('session')->associateScope($newAccessTokenId, $scopeDetails['id']); + } } + // Associate the new refresh token with the new access token $this->authServer->getStorage('session')->associateRefreshToken($newAccessTokenId, $refreshToken, $refreshTokenExpires, $authParams['client_id']); return array( diff --git a/tests/authorization/RefreshTokenTest.php b/tests/authorization/RefreshTokenTest.php index ae05dfba..3f32acae 100644 --- a/tests/authorization/RefreshTokenTest.php +++ b/tests/authorization/RefreshTokenTest.php @@ -183,6 +183,7 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase $this->session->shouldReceive('updateRefreshToken')->andReturn(null); $this->session->shouldReceive('associateAccessToken')->andReturn(1); $this->session->shouldReceive('associateRefreshToken')->andReturn(1); + $this->session->shouldReceive('removeRefreshToken')->andReturn(1); $this->session->shouldReceive('getAccessToken')->andReturn(null); $this->session->shouldReceive('getScopes')->andReturn(array()); @@ -226,6 +227,7 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase $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(); @@ -265,6 +267,7 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase $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(); @@ -290,4 +293,89 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase $this->assertEquals(30, $v['expires_in']); $this->assertEquals(time()+30, $v['expires']); } + + public function test_issueAccessToken_refreshTokenGrant_newScopes() + { + $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(array('id' => 1, 'scope' => 'foo'), array('id' => 2, 'scope' => 'bar'))); + $this->session->shouldReceive('associateAccessToken')->andReturn(1); + $this->session->shouldReceive('associateRefreshToken')->andReturn(1); + $this->session->shouldReceive('removeRefreshToken')->andReturn(1); + $this->session->shouldReceive('associateScope')->andReturn(null); + $this->scope->shouldReceive('getScope')->andReturn(array('id' => 1, 'scope' => 'foo')); + + $a = $this->returnDefault(); + $grant = new League\OAuth2\Server\Grant\RefreshToken($a); + $grant->setAccessTokenTTL(30); + $a->addGrantType($grant); + + $v = $a->issueAccessToken(array( + 'grant_type' => 'refresh_token', + 'client_id' => 1234, + 'client_secret' => 5678, + 'refresh_token' => 'abcdef', + 'scope' => 'foo' + )); + + $this->assertArrayHasKey('access_token', $v); + $this->assertArrayHasKey('token_type', $v); + $this->assertArrayHasKey('expires', $v); + $this->assertArrayHasKey('expires_in', $v); + $this->assertArrayHasKey('refresh_token', $v); + + $this->assertNotEquals($a->getAccessTokenTTL(), $v['expires_in']); + $this->assertNotEquals(time()+$a->getAccessTokenTTL(), $v['expires']); + $this->assertEquals(30, $v['expires_in']); + $this->assertEquals(time()+30, $v['expires']); + } + + /** + * @expectedException League\OAuth2\Server\Exception\ClientException + * @expectedExceptionCode 0 + */ + public function test_issueAccessToken_refreshTokenGrant_badNewScopes() + { + $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(array('id' => 1, 'scope' => 'foo'), array('id' => 2, 'scope' => 'bar'))); + $this->session->shouldReceive('associateAccessToken')->andReturn(1); + $this->session->shouldReceive('associateRefreshToken')->andReturn(1); + $this->session->shouldReceive('removeRefreshToken')->andReturn(1); + $this->session->shouldReceive('associateScope')->andReturn(null); + $this->scope->shouldReceive('getScope')->andReturn(array('id' => 1, 'scope' => 'foo')); + + $a = $this->returnDefault(); + $grant = new League\OAuth2\Server\Grant\RefreshToken($a); + $grant->setAccessTokenTTL(30); + $a->addGrantType($grant); + + $a->issueAccessToken(array( + 'grant_type' => 'refresh_token', + 'client_id' => 1234, + 'client_secret' => 5678, + 'refresh_token' => 'abcdef', + 'scope' => 'foobar' + )); + } } \ No newline at end of file