diff --git a/src/Controller/Login.php b/src/Controller/Login.php index 8316529e..8d50b681 100644 --- a/src/Controller/Login.php +++ b/src/Controller/Login.php @@ -10,19 +10,19 @@ namespace OxidEsales\GraphQL\Base\Controller; use OxidEsales\GraphQL\Base\DataType\Login as DataTypeLogin; -use OxidEsales\GraphQL\Base\Service\AccessToken as AccessTokenService; +use OxidEsales\GraphQL\Base\Service\RefreshToken as RefreshTokenService; use TheCodingMachine\GraphQLite\Annotations\Query; class Login { public function __construct( - protected AccessTokenService $accessTokenService + protected RefreshTokenService $tokenService ) { } /** * Retrieve a refresh token and access token - * + * * @Query */ public function login(?string $username = null, ?string $password = null): DataTypeLogin diff --git a/src/DataType/Login.php b/src/DataType/Login.php index 568b2c21..a6debd65 100644 --- a/src/DataType/Login.php +++ b/src/DataType/Login.php @@ -28,7 +28,7 @@ public function __construct( */ public function refreshToken(): string { - return $this->refreshToken(); + return $this->refreshToken; } /** @@ -36,6 +36,6 @@ public function refreshToken(): string */ public function accessToken(): string { - return $this->accessToken(); + return $this->accessToken; } } diff --git a/src/Infrastructure/RefreshToken.php b/src/Infrastructure/RefreshToken.php index 5567d6a8..3f54fca3 100644 --- a/src/Infrastructure/RefreshToken.php +++ b/src/Infrastructure/RefreshToken.php @@ -13,11 +13,14 @@ use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface; use OxidEsales\GraphQL\Base\DataType\RefreshToken as RefreshTokenDataType; use OxidEsales\GraphQL\Base\DataType\User as UserDataType; +use OxidEsales\GraphQL\Base\Exception\InvalidToken; use OxidEsales\GraphQL\Base\Infrastructure\Model\RefreshToken as RefreshTokenModel; use PDO; class RefreshToken { + private ?RefreshTokenDataType $token = null; + public function __construct( private QueryBuilderFactoryInterface $queryBuilderFactory, private Legacy $legacyInfrastructure @@ -29,9 +32,8 @@ public function registerToken( DateTimeImmutable $time, DateTimeImmutable $expire, UserDataType $user - ): RefreshTokenDataType - { - $model = new RefreshTokenModel; + ): RefreshTokenDataType { + $model = new RefreshTokenModel(); $model->assign( [ 'OXID' => Legacy::createUniqueIdentifier(), @@ -45,7 +47,10 @@ public function registerToken( ); $model->save(); - return new RefreshTokenDataType($model); + $token = new RefreshTokenDataType($model); + $this->token = $token; + + return $token; } public function isTokenRegistered(string $tokenId): bool @@ -120,6 +125,29 @@ public function tokenDelete(?UserDataType $user = null, ?string $tokenId = null, return is_object($result) ? $result->columnCount() : (int)$result; } + public function getTokenUser(string $token): UserDataType + { + if ($this->token) { + $userId = $this->token->customerId()->val(); + } else { + $queryBuilder = $this->queryBuilderFactory->create() + ->select('OXUSERID') + ->from('oegraphqlrefreshtoken') + ->where('TOKEN = :token') + ->andWhere('EXPIRES_AT > NOW()') + ->setParameter('token', $token); + $userId = $queryBuilder->execute()->fetchOne(); + + if ($userId === false) { + throw new InvalidToken('Invalid refresh token'); + } + } + + $userModel = $this->legacyInfrastructure->getUserModel($userId); + + return new UserDataType($userModel); + } + public function userHasToken(UserDataType $user, string $tokenId): bool { $queryBuilder = $this->queryBuilderFactory->create(); diff --git a/src/Service/AccessToken.php b/src/Service/AccessToken.php index f4dccfca..7776749d 100644 --- a/src/Service/AccessToken.php +++ b/src/Service/AccessToken.php @@ -17,7 +17,8 @@ use OxidEsales\GraphQL\Base\Exception\InvalidToken; use OxidEsales\GraphQL\Base\Exception\TokenQuota; use OxidEsales\GraphQL\Base\Infrastructure\Legacy; -use OxidEsales\GraphQL\Base\Infrastructure\AccessToken as TokenInfrastructure; +use OxidEsales\GraphQL\Base\Infrastructure\AccessToken as AccessTokenInfrastructure; +use OxidEsales\GraphQL\Base\Infrastructure\RefreshToken as RefreshTokenInfrastructure; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use TheCodingMachine\GraphQLite\Types\ID; @@ -51,8 +52,11 @@ class AccessToken /** @var ModuleConfiguration */ private $moduleConfiguration; - /** @var TokenInfrastructure */ - private $tokenInfrastructure; + /** @var AccessTokenInfra */ + private $accessTokenInfra; + + /** @var RefreshTokenInfra */ + private $refreshTokenInfra; public function __construct( ?UnencryptedToken $token, @@ -60,14 +64,16 @@ public function __construct( Legacy $legacyInfrastructure, EventDispatcherInterface $eventDispatcher, ModuleConfiguration $moduleConfiguration, - TokenInfrastructure $tokenInfrastructure + AccessTokenInfrastructure $accessTokenInfra, + RefreshTokenInfrastructure $refreshTokenInfra ) { $this->token = $token; $this->jwtConfigurationBuilder = $jwtConfigurationBuilder; $this->legacyInfrastructure = $legacyInfrastructure; $this->eventDispatcher = $eventDispatcher; $this->moduleConfiguration = $moduleConfiguration; - $this->tokenInfrastructure = $tokenInfrastructure; + $this->accessTokenInfra = $accessTokenInfra; + $this->refreshTokenInfra = $refreshTokenInfra; } /** @@ -89,27 +95,14 @@ public function getToken(): ?UnencryptedToken return $this->token; } - public function createRefreshToken(?string $username = null, ?string $password = null): string - { - /** @var UserDataType $user */ - $user = $this->legacyInfrastructure->login($username, $password); - $this->removeExpiredTokens($user); - - $token = 'bla'; - - $this->tokenInfrastructure->saveRefreshToken($token); - - return $token; - } - /** * @throws InvalidLogin * @throws TokenQuota */ - public function createToken(?string $username = null, ?string $password = null): UnencryptedToken + public function createToken(string $refreshToken): UnencryptedToken { /** @var UserDataType $user */ - $user = $this->legacyInfrastructure->login($username, $password); + $user = $this->refreshTokenInfra->getTokenUser($refreshToken); $this->removeExpiredTokens($user); $this->canIssueToken($user); @@ -149,8 +142,8 @@ public function deleteToken(ID $tokenId): void { $tokenId = (string)$tokenId; - if ($this->tokenInfrastructure->isTokenRegistered($tokenId)) { - $this->tokenInfrastructure->tokenDelete(null, $tokenId); + if ($this->accessTokenInfra->isTokenRegistered($tokenId)) { + $this->accessTokenInfra->tokenDelete(null, $tokenId); } else { throw InvalidToken::unknownToken(); } @@ -158,8 +151,8 @@ public function deleteToken(ID $tokenId): void public function deleteUserToken(UserDataType $user, ID $tokenId): void { - if ($this->tokenInfrastructure->userHasToken($user, (string)$tokenId)) { - $this->tokenInfrastructure->tokenDelete($user, (string)$tokenId); + if ($this->accessTokenInfra->userHasToken($user, (string)$tokenId)) { + $this->accessTokenInfra->tokenDelete($user, (string)$tokenId); } else { throw InvalidToken::unknownToken(); } @@ -172,7 +165,7 @@ private function registerToken( DateTimeImmutable $expire ): void { if (!$user->isAnonymous()) { - $this->tokenInfrastructure->registerToken($token, $time, $expire); + $this->accessTokenInfra->registerToken($token, $time, $expire); } } @@ -180,7 +173,7 @@ private function canIssueToken(UserDataType $user): void { if ( !$user->isAnonymous() && - !$this->tokenInfrastructure->canIssueToken($user, $this->moduleConfiguration->getUserTokenQuota()) + !$this->accessTokenInfra->canIssueToken($user, $this->moduleConfiguration->getUserTokenQuota()) ) { throw TokenQuota::quotaExceeded(); } @@ -189,7 +182,7 @@ private function canIssueToken(UserDataType $user): void private function removeExpiredTokens(UserDataType $user): void { if (!$user->isAnonymous()) { - $this->tokenInfrastructure->removeExpiredTokens($user); + $this->accessTokenInfra->removeExpiredTokens($user); } } } diff --git a/src/Service/RefreshToken.php b/src/Service/RefreshToken.php index 4bab46d7..3e13f0fe 100644 --- a/src/Service/RefreshToken.php +++ b/src/Service/RefreshToken.php @@ -46,7 +46,7 @@ public function createToken(?string $username = null, ?string $password = null): $time = new DateTimeImmutable('now'); $expire = new DateTimeImmutable('1month'); // TODO: should be configurable - $token = bin2hex(random_bytes(255)); + $token = substr(bin2hex(random_bytes(128)), 0, 255); $this->tokenInfrastructure->registerToken($token, $time, $expire, $user);