diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65c872d..b7e29fa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v5 - name: Install PHP uses: shivammathur/setup-php@v2 with: @@ -26,7 +26,7 @@ jobs: id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" - name: Cache composer dependencies - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c187a24..a8e3212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ Yii Framework 2 authclient extension Change Log 2.2.18 under development ------------------------ -- Bug #393: Fix type for `BaseOAuth` property - `accessToken` (max-s-lab) +- Bug #396: Fix `BaseOAuth::refreshAccessToken()` when no refresh token exists (kalmer) +- Bug #393: Fix type for `BaseOAuth::$accessToken` (max-s-lab) 2.2.17 February 13, 2025 diff --git a/src/BaseOAuth.php b/src/BaseOAuth.php index 45a2908..7c22c48 100644 --- a/src/BaseOAuth.php +++ b/src/BaseOAuth.php @@ -7,9 +7,9 @@ namespace yii\authclient; +use Yii; use yii\base\Exception; use yii\base\InvalidArgumentException; -use Yii; use yii\helpers\Inflector; use yii\httpclient\Request; @@ -291,7 +291,7 @@ protected function restoreAccessToken() $token = $this->getState('token'); if (is_object($token)) { /** @var OAuthToken $token */ - if ($token->getIsExpired() && $this->autoRefreshAccessToken) { + if ($token->getIsExpired() && $this->autoRefreshAccessToken && $token->hasRefreshToken()) { $token = $this->refreshAccessToken($token); } } diff --git a/src/OAuthToken.php b/src/OAuthToken.php index 2d4b34e..6514569 100644 --- a/src/OAuthToken.php +++ b/src/OAuthToken.php @@ -35,6 +35,10 @@ class OAuthToken extends BaseObject * @var string key in [[params]] array, which stores token secret key. */ public $tokenSecretParamKey = 'oauth_token_secret'; + /** + * @var string key in [[params]] array, which stores refresh token key. + */ + public $refreshTokenParamKey = 'refresh_token'; /** * @var int object creation timestamp. */ @@ -59,6 +63,9 @@ public function __construct(array $config = []) if (array_key_exists('tokenSecretParamKey', $config)) { $this->tokenSecretParamKey = ArrayHelper::remove($config, 'tokenSecretParamKey'); } + if (array_key_exists('refreshTokenParamKey', $config)) { + $this->refreshTokenParamKey = ArrayHelper::remove($config, 'refreshTokenParamKey'); + } parent::__construct($config); } @@ -224,4 +231,23 @@ public function getIsValid() return (!empty($token) && !$this->getIsExpired()); } + + public function getRefreshToken() + { + return $this->getParam($this->refreshTokenParamKey); + } + + /** + * Sets refresh token. + * @param string $refreshToken + */ + public function setRefreshToken($refreshToken) + { + $this->setParam($this->refreshTokenParamKey, $refreshToken); + } + + public function hasRefreshToken() + { + return !!$this->getRefreshToken(); + } } diff --git a/tests/BaseOAuthTest.php b/tests/BaseOAuthTest.php index add36dd..e34d7a5 100644 --- a/tests/BaseOAuthTest.php +++ b/tests/BaseOAuthTest.php @@ -2,9 +2,9 @@ namespace yiiunit\extensions\authclient; -use yii\authclient\signature\PlainText; -use yii\authclient\OAuthToken; use yii\authclient\BaseOAuth; +use yii\authclient\OAuthToken; +use yii\authclient\signature\PlainText; use yii\httpclient\Client; use yii\httpclient\Request; use yii\httpclient\Response; @@ -267,6 +267,54 @@ public function sendRequestDataProvider() 'Client error' => [400, 'yii\\authclient\\ClientErrorResponseException'], 'Server error' => [500, 'yii\\authclient\\InvalidResponseException'], ]; + } + + public function testDoNotRestoreAccessTokenWithNoRefreshToken() + { + /** + * @var BaseOAuth|\PHPUnit_Framework_MockObject_MockObject + */ + $oauthClient = $this->getMockBuilder(BaseOAuth::className()) + ->setMethods(['composeRequestCurlOptions', 'refreshAccessToken', 'applyAccessTokenToRequest', 'initUserAttributes', 'getState']) + ->getMock(); + + $oauthClient->expects($this->never()) + ->method('refreshAccessToken'); + + $accessToken = new OAuthToken(); + $accessToken->setExpireDuration(-100); + + $oauthClient->expects($this->once()) + ->method('getState') + ->willReturn($accessToken); + + $this->assertSame($accessToken, $oauthClient->getAccessToken()); + } + + public function testRestoreAccessTokenWithRefreshToken() + { + /** + * @var BaseOAuth|\PHPUnit_Framework_MockObject_MockObject + */ + $oauthClient = $this->getMockBuilder(BaseOAuth::className()) + ->setMethods(['composeRequestCurlOptions', 'refreshAccessToken', 'applyAccessTokenToRequest', 'initUserAttributes', 'getState']) + ->getMock(); + + $oauthClient->expects($this->once()) + ->method('refreshAccessToken') + ->willReturn(new OAuthToken()); + + $accessToken = new OAuthToken([ + 'params' => [ + 'refresh_token' => 'test_refresh_token', + ], + ]); + $accessToken->setExpireDuration(-100); + + $oauthClient->expects($this->once()) + ->method('getState') + ->willReturn($accessToken); + $this->assertNotSame($accessToken, $oauthClient->getAccessToken()); } } diff --git a/tests/OAuth1Test.php b/tests/OAuth1Test.php index 594d017..9ded758 100644 --- a/tests/OAuth1Test.php +++ b/tests/OAuth1Test.php @@ -3,8 +3,8 @@ namespace yiiunit\extensions\authclient; use yii\authclient\OAuth1; -use yii\authclient\signature\BaseMethod; use yii\authclient\OAuthToken; +use yii\authclient\signature\BaseMethod; class OAuth1Test extends TestCase { @@ -30,6 +30,8 @@ protected function createClient() $oauthClient = $this->getMockBuilder(OAuth1::className()) ->setMethods(['initUserAttributes']) ->getMock(); + $oauthClient->apiBaseUrl = 'https://www.google.com'; + return $oauthClient; } diff --git a/tests/TokenTest.php b/tests/TokenTest.php index 949c693..326bbd9 100644 --- a/tests/TokenTest.php +++ b/tests/TokenTest.php @@ -2,6 +2,7 @@ namespace yiiunit\extensions\authclient; +use Yii; use yii\authclient\OAuthToken; class TokenTest extends TestCase @@ -11,6 +12,7 @@ public function testCreate() $config = [ 'tokenParamKey' => 'test_token_param_key', 'tokenSecretParamKey' => 'test_token_secret_param_key', + 'refreshTokenParamKey' => 'test_refresh_token_param_key', ]; $oauthToken = new OAuthToken($config); $this->assertTrue(is_object($oauthToken), 'Unable to create access token!'); @@ -27,6 +29,7 @@ public function testCreateWithIncorrectConfigOrder() 'tokenSecret' => 'tokenSecret', 'tokenParamKey' => 'test_token_param_key', 'tokenSecretParamKey' => 'test_token_secret_param_key', + 'refreshTokenParamKey' => 'test_refresh_token_param_key', ]; $oauthToken = new OAuthToken($config); $this->assertInternalType('object', $oauthToken, 'Unable to create access token!'); @@ -144,4 +147,15 @@ public function testGetIsValid() $oauthToken->createTimestamp = $oauthToken->createTimestamp - ($expireDuration + 1); $this->assertFalse($oauthToken->getIsValid(), 'Expired token is valid!'); } + + public function testHasRefreshToken() + { + $oauthToken = new OAuthToken([ + 'params' => [ + 'refresh_token' => 'test_refresh_token', + ], + ]); + + $this->assertTrue($oauthToken->hasRefreshToken()); + } }