Skip to content

Commit

Permalink
Fix token generation
Browse files Browse the repository at this point in the history
  • Loading branch information
angel-dimitrov committed Feb 16, 2024
1 parent dc21739 commit 49c2a13
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 37 deletions.
6 changes: 3 additions & 3 deletions src/Controller/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/DataType/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ public function __construct(
*/
public function refreshToken(): string
{
return $this->refreshToken();
return $this->refreshToken;
}

/**
* @Field()
*/
public function accessToken(): string
{
return $this->accessToken();
return $this->accessToken;
}
}
36 changes: 32 additions & 4 deletions src/Infrastructure/RefreshToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(),
Expand All @@ -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
Expand Down Expand Up @@ -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();
Expand Down
47 changes: 20 additions & 27 deletions src/Service/AccessToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -51,23 +52,28 @@ class AccessToken
/** @var ModuleConfiguration */
private $moduleConfiguration;

/** @var TokenInfrastructure */
private $tokenInfrastructure;
/** @var AccessTokenInfra */
private $accessTokenInfra;

/** @var RefreshTokenInfra */
private $refreshTokenInfra;

public function __construct(
?UnencryptedToken $token,
JwtConfigurationBuilder $jwtConfigurationBuilder,
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;
}

/**
Expand All @@ -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);

Expand Down Expand Up @@ -149,17 +142,17 @@ 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();
}
}

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();
}
Expand All @@ -172,15 +165,15 @@ private function registerToken(
DateTimeImmutable $expire
): void {
if (!$user->isAnonymous()) {
$this->tokenInfrastructure->registerToken($token, $time, $expire);
$this->accessTokenInfra->registerToken($token, $time, $expire);
}
}

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();
}
Expand All @@ -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);
}
}
}
2 changes: 1 addition & 1 deletion src/Service/RefreshToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 49c2a13

Please sign in to comment.