. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ namespace Espo\Tools\OAuthServer\Revoke; use Espo\Core\Exceptions\Unauthorized; use Espo\ORM\EntityManager; use Espo\Tools\OAuthServer\ClientValidator; use Espo\Tools\OAuthServer\Entities\Client; use Espo\Tools\OAuthServer\Repository\AccessTokenRepository; use Espo\Tools\OAuthServer\Repository\ClientRepository; use Espo\Tools\OAuthServer\Repository\RefreshTokenRepository; use Espo\Tools\OAuthServer\Utils\RefreshTokenDataExtractor; use Exception; use SensitiveParameter; class TokenRevokeService { public const string TYPE_ACCESS_TOKEN = 'access_token'; public const string TYPE_REFRESH_TOKEN = 'refresh_token'; public function __construct( private AccessTokenRepository $accessTokenRepository, private RefreshTokenRepository $refreshTokenRepository, private ClientRepository $clientRepository, private ClientValidator $clientValidator, private EntityManager $entityManager, private RefreshTokenDataExtractor $refreshTokenDataExtractor, ) {} /** * @throws Unauthorized */ public function revoke(#[SensitiveParameter] Data $data): void { $tokenTypeHint = $data->tokenTypeHint; $token = $data->token; $client = $this->clientRepository->getActiveByIdentifier($data->clientId); if (!$client || !$this->clientValidator->validate($client, $data->clientSecret)) { throw new Unauthorized(); } if (!$tokenTypeHint || $tokenTypeHint === self::TYPE_ACCESS_TOKEN) { $this->revokeAccessToken($token, $client); } if (!$tokenTypeHint || $tokenTypeHint === self::TYPE_REFRESH_TOKEN) { $this->revokeRefreshToken($token, $client); } } private function revokeAccessToken(#[SensitiveParameter] string $token, Client $client): void { $accessToken = $this->accessTokenRepository->getActiveByIdentifier($token); if ( !$accessToken || $accessToken->getClient()->getId() !== $client->getId() ) { return; } $accessToken->setRevoked(); $this->entityManager->saveEntity($accessToken); } private function revokeRefreshToken(#[SensitiveParameter] string $token, Client $client): void { try { $identifier = $this->refreshTokenDataExtractor->extractIdentifier($token); } catch (Exception) { return; } $refreshToken = $this->refreshTokenRepository->getActiveByIdentifier($identifier); if ( !$refreshToken || $refreshToken->getClient()->getId() !== $client->getId() ) { return; } $accessToken = $refreshToken->getAccessToken(); $refreshToken->setRevoked(); $this->entityManager->saveEntity($refreshToken); if ($accessToken) { $accessToken->setRevoked(); $this->entityManager->saveEntity($accessToken); } } }