From 642a553b90cfe7cc27056dd709c57c17159e2cc9 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Fri, 3 Jan 2025 17:38:33 +0500 Subject: [PATCH 1/2] fix: use custom Redirect URI validation logic in RefreshTokenGrant Signed-off-by: Octol1ttle --- .../OAuth2/Grants/RefreshTokenGrant.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/common/components/OAuth2/Grants/RefreshTokenGrant.php b/common/components/OAuth2/Grants/RefreshTokenGrant.php index 6b5d3d45..9ce11fee 100644 --- a/common/components/OAuth2/Grants/RefreshTokenGrant.php +++ b/common/components/OAuth2/Grants/RefreshTokenGrant.php @@ -11,12 +11,15 @@ use Lcobucci\JWT\Validation\Constraint\LooseValidAt; use Lcobucci\JWT\Validation\Validator; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\RefreshTokenGrant as BaseRefreshTokenGrant; +use League\OAuth2\Server\RequestEvent; use Psr\Http\Message\ServerRequestInterface; use Throwable; use Yii; +use yii\helpers\StringHelper; final class RefreshTokenGrant extends BaseRefreshTokenGrant { use CryptTrait; @@ -112,4 +115,20 @@ private function validateAccessToken(string $jwt): array { ]; } + protected function validateRedirectUri( + string $redirectUri, + ClientEntityInterface $client, + ServerRequestInterface $request, + ): void { + $allowedRedirectUris = (array)$client->getRedirectUri(); + foreach ($allowedRedirectUris as $allowedRedirectUri) { + if (StringHelper::startsWith($redirectUri, $allowedRedirectUri)) { + return; + } + } + + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + throw OAuthServerException::invalidClient($request); + } + } From 0615f10e3c0e7133061a0bf27cc5ace387eb4051 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Thu, 9 Jan 2025 01:22:24 +0100 Subject: [PATCH 2/2] Extract common implementation into traits --- .../OAuth2/Grants/AuthCodeGrant.php | 32 ++--------------- .../Grants/CheckOfflineAccessScopeTrait.php | 26 ++++++++++++++ .../OAuth2/Grants/DeviceCodeGrant.php | 10 ++---- .../OAuth2/Grants/RefreshTokenGrant.php | 20 +---------- .../Grants/ValidateRedirectUriTrait.php | 34 +++++++++++++++++++ 5 files changed, 66 insertions(+), 56 deletions(-) create mode 100644 common/components/OAuth2/Grants/CheckOfflineAccessScopeTrait.php create mode 100644 common/components/OAuth2/Grants/ValidateRedirectUriTrait.php diff --git a/common/components/OAuth2/Grants/AuthCodeGrant.php b/common/components/OAuth2/Grants/AuthCodeGrant.php index 9dd7031b..0d162922 100644 --- a/common/components/OAuth2/Grants/AuthCodeGrant.php +++ b/common/components/OAuth2/Grants/AuthCodeGrant.php @@ -4,19 +4,15 @@ namespace common\components\OAuth2\Grants; use common\components\OAuth2\CryptTrait; -use common\components\OAuth2\Events\RequestedRefreshToken; -use common\components\OAuth2\Repositories\PublicScopeRepository; use DateInterval; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClientEntityInterface; -use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\AuthCodeGrant as BaseAuthCodeGrant; -use League\OAuth2\Server\RequestEvent; -use Psr\Http\Message\ServerRequestInterface; -use yii\helpers\StringHelper; final class AuthCodeGrant extends BaseAuthCodeGrant { use CryptTrait; + use CheckOfflineAccessScopeTrait; + use ValidateRedirectUriTrait; protected function issueAccessToken( DateInterval $accessTokenTTL, @@ -24,30 +20,8 @@ protected function issueAccessToken( ?string $userIdentifier, array $scopes = [], ): AccessTokenEntityInterface { - foreach ($scopes as $i => $scope) { - if ($scope->getIdentifier() === PublicScopeRepository::OFFLINE_ACCESS) { - unset($scopes[$i]); - $this->getEmitter()->emit(new RequestedRefreshToken('refresh_token_requested')); - } - } - + $this->checkOfflineAccessScope($scopes); return parent::issueAccessToken($accessTokenTTL, $client, $userIdentifier, $scopes); } - protected function validateRedirectUri( - string $redirectUri, - ClientEntityInterface $client, - ServerRequestInterface $request, - ): void { - $allowedRedirectUris = (array)$client->getRedirectUri(); - foreach ($allowedRedirectUris as $allowedRedirectUri) { - if (StringHelper::startsWith($redirectUri, $allowedRedirectUri)) { - return; - } - } - - $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient($request); - } - } diff --git a/common/components/OAuth2/Grants/CheckOfflineAccessScopeTrait.php b/common/components/OAuth2/Grants/CheckOfflineAccessScopeTrait.php new file mode 100644 index 00000000..12a1b182 --- /dev/null +++ b/common/components/OAuth2/Grants/CheckOfflineAccessScopeTrait.php @@ -0,0 +1,26 @@ + $scope) { + if ($scope->getIdentifier() === PublicScopeRepository::OFFLINE_ACCESS) { + unset($scopes[$i]); + $this->getEmitter()->emit(new RequestedRefreshToken('refresh_token_requested')); + } + } + } + +} diff --git a/common/components/OAuth2/Grants/DeviceCodeGrant.php b/common/components/OAuth2/Grants/DeviceCodeGrant.php index b4d154e9..994a6aab 100644 --- a/common/components/OAuth2/Grants/DeviceCodeGrant.php +++ b/common/components/OAuth2/Grants/DeviceCodeGrant.php @@ -3,9 +3,7 @@ namespace common\components\OAuth2\Grants; -use common\components\OAuth2\Events\RequestedRefreshToken; use common\components\OAuth2\Repositories\ExtendedDeviceCodeRepositoryInterface; -use common\components\OAuth2\Repositories\PublicScopeRepository; use common\components\OAuth2\ResponseTypes\EmptyResponse; use DateInterval; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; @@ -22,6 +20,7 @@ * @property ExtendedDeviceCodeRepositoryInterface $deviceCodeRepository */ final class DeviceCodeGrant extends BaseDeviceCodeGrant { + use CheckOfflineAccessScopeTrait; public function __construct( ExtendedDeviceCodeRepositoryInterface $deviceCodeRepository, @@ -95,12 +94,7 @@ protected function issueAccessToken( ?string $userIdentifier, array $scopes = [], ): AccessTokenEntityInterface { - foreach ($scopes as $i => $scope) { - if ($scope->getIdentifier() === PublicScopeRepository::OFFLINE_ACCESS) { - unset($scopes[$i]); - $this->getEmitter()->emit(new RequestedRefreshToken('refresh_token_requested')); - } - } + $this->checkOfflineAccessScope($scopes); return parent::issueAccessToken($accessTokenTTL, $client, $userIdentifier, $scopes); } diff --git a/common/components/OAuth2/Grants/RefreshTokenGrant.php b/common/components/OAuth2/Grants/RefreshTokenGrant.php index 9ce11fee..b60c74d3 100644 --- a/common/components/OAuth2/Grants/RefreshTokenGrant.php +++ b/common/components/OAuth2/Grants/RefreshTokenGrant.php @@ -11,18 +11,16 @@ use Lcobucci\JWT\Validation\Constraint\LooseValidAt; use Lcobucci\JWT\Validation\Validator; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; -use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\RefreshTokenGrant as BaseRefreshTokenGrant; -use League\OAuth2\Server\RequestEvent; use Psr\Http\Message\ServerRequestInterface; use Throwable; use Yii; -use yii\helpers\StringHelper; final class RefreshTokenGrant extends BaseRefreshTokenGrant { use CryptTrait; + use ValidateRedirectUriTrait; /** * Previously, refresh tokens were stored in Redis. @@ -115,20 +113,4 @@ private function validateAccessToken(string $jwt): array { ]; } - protected function validateRedirectUri( - string $redirectUri, - ClientEntityInterface $client, - ServerRequestInterface $request, - ): void { - $allowedRedirectUris = (array)$client->getRedirectUri(); - foreach ($allowedRedirectUris as $allowedRedirectUri) { - if (StringHelper::startsWith($redirectUri, $allowedRedirectUri)) { - return; - } - } - - $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); - throw OAuthServerException::invalidClient($request); - } - } diff --git a/common/components/OAuth2/Grants/ValidateRedirectUriTrait.php b/common/components/OAuth2/Grants/ValidateRedirectUriTrait.php new file mode 100644 index 00000000..d9e7dd37 --- /dev/null +++ b/common/components/OAuth2/Grants/ValidateRedirectUriTrait.php @@ -0,0 +1,34 @@ +getRedirectUri(); + foreach ($allowedRedirectUris as $allowedRedirectUri) { + if (StringHelper::startsWith($redirectUri, $allowedRedirectUri)) { + return; + } + } + + $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request)); + + throw OAuthServerException::invalidClient($request); + } + +}