mirror of
				https://github.com/elyby/oauth2-server.git
				synced 2025-05-31 14:12:07 +05:30 
			
		
		
		
	Merge branch 'feature/refresh-token-rotation' into develop
This commit is contained in:
		| @@ -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; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user