Skip to content

Commit

Permalink
WIP add refresh token functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
angel-dimitrov committed Jan 31, 2024
1 parent 4e95ff1 commit dc21739
Show file tree
Hide file tree
Showing 33 changed files with 587 additions and 124 deletions.
2 changes: 1 addition & 1 deletion metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
'name' => 'sJsonWebTokenLifetime',
'type' => 'select',
'constraints' => '15min|1hrs|3hrs|8hrs|24hrs',
'value' => '8hrs'
'value' => '15min'
],
[
'group' => 'graphql_base',
Expand Down
39 changes: 39 additions & 0 deletions migration/data/Version20240115132144.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/**
* Copyright © OXID eSales AG. All rights reserved.
* See LICENSE file for license details.
*/

declare(strict_types=1);

namespace OxidEsales\GraphQL\Base\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20240115132144 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->connection->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');

if (!$schema->hasTable('oegraphqlrefreshtoken')) {
$this->addSql("CREATE TABLE `oegraphqlrefreshtoken` (
`OXID` char(32) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL COMMENT 'Primary oxid',
`OXSHOPID` int(11) NOT NULL DEFAULT '0' COMMENT 'Shop id (oxshops), value 0 in case no shop was specified',
`OXUSERID` char(32) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL COMMENT 'Userid for this order',
`TOKEN` char(255) NOT NULL default '' COMMENT 'token string',
`ISSUED_AT` datetime NOT NULL COMMENT 'creation date',
`EXPIRES_AT` datetime NOT NULL COMMENT 'expiration date',
`OXTIMESTAMP` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Timestamp',
PRIMARY KEY (`OXID`),
KEY `OXUSERID` (`OXUSERID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
}
}

public function down(Schema $schema): void
{
}
}
23 changes: 15 additions & 8 deletions services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,28 @@ services:
class: OxidEsales\GraphQL\Base\Service\Authentication
public: true

OxidEsales\GraphQL\Base\Service\Token:
class: OxidEsales\GraphQL\Base\Service\Token
OxidEsales\GraphQL\Base\Service\AccessToken:
class: OxidEsales\GraphQL\Base\Service\AccessToken
public: true

OxidEsales\GraphQL\Base\Service\TokenValidator:
class: OxidEsales\GraphQL\Base\Service\TokenValidator
OxidEsales\GraphQL\Base\Service\RefreshToken:
class: OxidEsales\GraphQL\Base\Service\RefreshToken
public: true

OxidEsales\GraphQL\Base\Service\AccessTokenValidator:
class: OxidEsales\GraphQL\Base\Service\AccessTokenValidator

OxidEsales\GraphQL\Base\Service\TokenAdministration:
class: OxidEsales\GraphQL\Base\Service\TokenAdministration
OxidEsales\GraphQL\Base\Service\AccessTokenAdministration:
class: OxidEsales\GraphQL\Base\Service\AccessTokenAdministration

OxidEsales\GraphQL\Base\Infrastructure\Legacy:
class: OxidEsales\GraphQL\Base\Infrastructure\Legacy

OxidEsales\GraphQL\Base\Infrastructure\Token:
class: OxidEsales\GraphQL\Base\Infrastructure\Token
OxidEsales\GraphQL\Base\Infrastructure\AccessToken:
class: OxidEsales\GraphQL\Base\Infrastructure\AccessToken

OxidEsales\GraphQL\Base\Infrastructure\RefreshToken:
class: OxidEsales\GraphQL\Base\Infrastructure\RefreshToken

OxidEsales\GraphQL\Base\Infrastructure\Repository:
class: OxidEsales\GraphQL\Base\Infrastructure\Repository
Expand Down
28 changes: 19 additions & 9 deletions src/Controller/Token.php → src/Controller/AccessToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@
use OxidEsales\GraphQL\Base\DataType\Filter\IDFilter;
use OxidEsales\GraphQL\Base\DataType\Pagination\Pagination;
use OxidEsales\GraphQL\Base\DataType\Sorting\TokenSorting;
use OxidEsales\GraphQL\Base\DataType\Token as TokenDataType;
use OxidEsales\GraphQL\Base\DataType\TokenFilterList;
use OxidEsales\GraphQL\Base\DataType\AccessToken as TokenDataType;
use OxidEsales\GraphQL\Base\DataType\AccessTokenFilterList;
use OxidEsales\GraphQL\Base\Service\Authentication;
use OxidEsales\GraphQL\Base\Service\Authorization;
use OxidEsales\GraphQL\Base\Service\Token as TokenService;
use OxidEsales\GraphQL\Base\Service\TokenAdministration;
use OxidEsales\GraphQL\Base\Service\AccessToken as TokenService;
use OxidEsales\GraphQL\Base\Service\AccessTokenAdministration;
use TheCodingMachine\GraphQLite\Annotations\Logged;
use TheCodingMachine\GraphQLite\Annotations\Mutation;
use TheCodingMachine\GraphQLite\Annotations\Query;
use TheCodingMachine\GraphQLite\Annotations\Right;
use TheCodingMachine\GraphQLite\Types\ID;

class Token
class AccessToken
{
/** @var TokenAdministration */
/** @var AccessTokenAdministration */
private $tokenAdministration;

/** @var Authentication */
Expand All @@ -39,7 +39,7 @@ class Token
private $tokenService;

public function __construct(
TokenAdministration $tokenAdministration,
AccessTokenAdministration $tokenAdministration,
Authentication $authentication,
Authorization $authorization,
TokenService $tokenService
Expand All @@ -60,12 +60,12 @@ public function __construct(
* @return TokenDataType[]
*/
public function tokens(
?TokenFilterList $filter = null,
?AccessTokenFilterList $filter = null,
?Pagination $pagination = null,
?TokenSorting $sort = null
): array {
return $this->tokenAdministration->tokens(
$filter ?? TokenFilterList::fromUserInput(
$filter ?? AccessTokenFilterList::fromUserInput(
new IDFilter(
$this->authentication->getUser()->id()
)
Expand All @@ -75,6 +75,16 @@ public function tokens(
);
}

/**
* retrieve a JWT for authentication of further requests
*
* @Query
*/
public function accessToken(string $refreshToken): string
{
return $this->tokenService->createToken($refreshToken)->toString();
}

/**
* Invalidate all tokens per customer.
* - Customer with right INVALIDATE_ANY_TOKEN can invalidate tokens for any customer Id.
Expand Down
20 changes: 7 additions & 13 deletions src/Controller/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,24 @@

namespace OxidEsales\GraphQL\Base\Controller;

use OxidEsales\GraphQL\Base\Service\Token;
use OxidEsales\GraphQL\Base\DataType\Login as DataTypeLogin;
use OxidEsales\GraphQL\Base\Service\AccessToken as AccessTokenService;
use TheCodingMachine\GraphQLite\Annotations\Query;

class Login
{
/** @var Token */
protected $tokenService;

public function __construct(
Token $tokenService
protected AccessTokenService $accessTokenService
) {
$this->tokenService = $tokenService;
}

/**
* retrieve a JWT for authentication of further requests
*
* Retrieve a refresh token and access token
*
* @Query
*/
public function token(?string $username = null, ?string $password = null): string
public function login(?string $username = null, ?string $password = null): DataTypeLogin
{
return $this->tokenService->createToken(
$username,
$password
)->toString();
return $this->tokenService->login($username, $password);
}
}
4 changes: 2 additions & 2 deletions src/DataType/Token.php → src/DataType/AccessToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
namespace OxidEsales\GraphQL\Base\DataType;

use DateTimeInterface;
use OxidEsales\GraphQL\Base\Infrastructure\Model\Token as GraphQLTokenModel;
use OxidEsales\GraphQL\Base\Infrastructure\Model\RefreshToken as GraphQLTokenModel;
use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\Type;
use TheCodingMachine\GraphQLite\Types\ID;

/**
* @Type()
*/
final class Token implements ShopModelAwareInterface
final class AccessToken implements ShopModelAwareInterface
{
/** @var GraphQLTokenModel */
private $tokenModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use OxidEsales\GraphQL\Base\DataType\Filter\IDFilter;
use TheCodingMachine\GraphQLite\Annotations\Factory;

final class TokenFilterList implements FilterListInterface
final class AccessTokenFilterList implements FilterListInterface
{
/** @var ?IDFilter */
private $customerId;
Expand Down
41 changes: 41 additions & 0 deletions src/DataType/Login.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/**
* Copyright © OXID eSales AG. All rights reserved.
* See LICENSE file for license details.
*/

declare(strict_types=1);

namespace OxidEsales\GraphQL\Base\DataType;

use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\Type;

/**
* @Type()
*/
final class Login
{
public function __construct(
private string $refreshToken,
private string $accessToken
) {
}

/**
* @Field()
*/
public function refreshToken(): string
{
return $this->refreshToken();
}

/**
* @Field()
*/
public function accessToken(): string
{
return $this->accessToken();
}
}
92 changes: 92 additions & 0 deletions src/DataType/RefreshToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

/**
* Copyright © OXID eSales AG. All rights reserved.
* See LICENSE file for license details.
*/

declare(strict_types=1);

namespace OxidEsales\GraphQL\Base\DataType;

use DateTimeInterface;
use OxidEsales\GraphQL\Base\Infrastructure\Model\RefreshToken as GraphQLTokenModel;
use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\Type;
use TheCodingMachine\GraphQLite\Types\ID;

/**
* @Type()
*/
final class RefreshToken implements ShopModelAwareInterface
{
/** @var GraphQLTokenModel */
private $tokenModel;

public function __construct(GraphQLTokenModel $tokenModel)
{
$this->tokenModel = $tokenModel;
}

public function getEshopModel(): GraphQLTokenModel
{
return $this->tokenModel;
}

/**
* @Field()
*/
public function id(): ID
{
return new ID((string)$this->tokenModel->getId());
}

/**
* @Field()
*/
public function token(): string
{
return (string)$this->tokenModel->getRawFieldData('token');
}

/**
* @Field()
*/
public function createdAt(): ?DateTimeInterface
{
return DateTimeImmutableFactory::fromString(
(string)$this->tokenModel->getRawFieldData('issued_at')
);
}

/**
* @Field()
*/
public function expiresAt(): ?DateTimeInterface
{
return DateTimeImmutableFactory::fromString(
(string)$this->tokenModel->getRawFieldData('expires_at')
);
}

/**
* @Field()
*/
public function customerId(): ID
{
return new ID((string)$this->tokenModel->getRawFieldData('oxuserid'));
}

/**
* @Field()
*/
public function shopId(): ID
{
return new ID((string)$this->tokenModel->getRawFieldData('oxshopid'));
}

public static function getModelClass(): string
{
return GraphQLTokenModel::class;
}
}
2 changes: 1 addition & 1 deletion src/Framework/Constraint/BelongsToShop.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
use OxidEsales\GraphQL\Base\Service\Token as TokenService;
use OxidEsales\GraphQL\Base\Service\AccessToken as TokenService;

final class BelongsToShop implements Constraint
{
Expand Down
6 changes: 3 additions & 3 deletions src/Framework/RequestReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use Lcobucci\JWT\UnencryptedToken;
use OxidEsales\GraphQL\Base\Exception\InvalidToken;
use OxidEsales\GraphQL\Base\Service\JwtConfigurationBuilder;
use OxidEsales\GraphQL\Base\Service\TokenValidator;
use OxidEsales\GraphQL\Base\Service\AccessTokenValidator;

use function apache_request_headers;
use function array_change_key_case;
Expand All @@ -28,14 +28,14 @@

class RequestReader
{
/** @var TokenValidator */
/** @var AccessTokenValidator */
private $tokenValidatorService;

/** @var JwtConfigurationBuilder */
private $jwtConfigurationBuilder;

public function __construct(
TokenValidator $tokenValidatorService,
AccessTokenValidator $tokenValidatorService,
JwtConfigurationBuilder $jwtConfigurationBuilder
) {
$this->tokenValidatorService = $tokenValidatorService;
Expand Down
Loading

0 comments on commit dc21739

Please sign in to comment.