diff --git a/Controller/Login/Redirect.php b/Controller/Login/Redirect.php index 2e31da1..5f2bd8c 100644 --- a/Controller/Login/Redirect.php +++ b/Controller/Login/Redirect.php @@ -41,6 +41,8 @@ */ class Redirect implements ActionInterface { + private const RETRY_OBTAIN_TOKEN_COUNTER = 3; + /** * @var SessionManagerInterface|Session */ @@ -122,6 +124,9 @@ public function execute() { $code = $this->request->getParam('code'); $state = $this->request->getParam('state'); + + $this->customerSession->setData(StateKey::DATA_KEY_STATE, $state); + $resultRedirect = $this->resultRedirectFactory->create(); try { @@ -141,6 +146,14 @@ public function execute() // get token $token = $this->tokenCommand->execute($code); + if (!$this->checkAttempts($token)) { + throw new LocalizedException(__('Can not obtain access token.')); + } + + if (!$token) { + return $resultRedirect->setPath('vipps/login/redirect', ['code' => $code, 'state' => $state]); + } + // remember token $this->storeToken($token); @@ -159,6 +172,27 @@ public function execute() return $resultRedirect->setPath('vipps/login/error'); } + /** + * @param string $token + * + * @return bool + */ + private function checkAttempts($token) + { + if ($token) { + $this->customerSession->setData('retry_obtain_token_counter', null); + + return true; + } + + $value = (int) ($this->customerSession->getData('retry_obtain_token_counter') ?? 0); + $value = $value > self::RETRY_OBTAIN_TOKEN_COUNTER ? 0 : $value; + + $this->customerSession->setData('retry_obtain_token_counter', ++$value); + + return !($value > self::RETRY_OBTAIN_TOKEN_COUNTER); + } + /** * Save access token and vipps ID token in customer session data. * diff --git a/Gateway/Command/TokenCommand.php b/Gateway/Command/TokenCommand.php index c3b9536..4c1c4eb 100644 --- a/Gateway/Command/TokenCommand.php +++ b/Gateway/Command/TokenCommand.php @@ -18,6 +18,7 @@ namespace Vipps\Login\Gateway\Command; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\HTTP\ClientFactory; @@ -70,14 +71,18 @@ class TokenCommand private $logger; /** - * TokenCommand constructor. - * + * @var ResourceConnection + */ + private $resourceConnection; + + /** * @param ConfigInterface $config * @param SerializerInterface $serializer * @param ApiEndpointsInterface $apiEndpoints * @param ClientFactory $httpClientFactory * @param UrlInterface $url * @param LoggerInterface $logger + * @param ResourceConnection $resourceConnection */ public function __construct( ConfigInterface $config, @@ -85,7 +90,8 @@ public function __construct( ApiEndpointsInterface $apiEndpoints, ClientFactory $httpClientFactory, UrlInterface $url, - LoggerInterface $logger + LoggerInterface $logger, + ResourceConnection $resourceConnection ) { $this->config = $config; $this->httpClientFactory = $httpClientFactory; @@ -93,6 +99,7 @@ public function __construct( $this->serializer = $serializer; $this->url = $url; $this->logger = $logger; + $this->resourceConnection = $resourceConnection; } /** @@ -105,6 +112,15 @@ public function __construct( */ public function execute($code) { + $authRecord = $this->fetchAuthorizationRecord($code); + if (!$authRecord) { + return null; + } + + if (isset($authRecord['payload'])) { + return $this->prepareResponse($authRecord['payload']); + } + $clientId = $this->config->getLoginClientId(); $clientSecret = $this->config->getLoginClientSecret(); @@ -119,17 +135,70 @@ public function execute($code) 'redirect_uri' => trim($this->url->getUrl('vipps/login/redirect'), '/') ]); - $token = $this->serializer->unserialize($httpClient->getBody()); - $payload = $this->getPayload($token); + $this->storeAuthorizationRecord($code, $httpClient->getBody()); - $token['id_token_payload'] = $payload; - return $token; + return $this->prepareResponse($httpClient->getBody()); } catch (\Exception $e) { $this->logger->critical($e); throw new LocalizedException(__('An error occurred trying to get token'), $e); } } + private function fetchAuthorizationRecord($code): ?array + { + $connection = $this->resourceConnection->getConnection('write'); + $connection->delete( + $connection->getTableName('vipps_login_authorization'), + \sprintf('created_at < %s', $connection->quote((new \DateTime())->modify('-5 min')->format('Y-m-d H:i-s'))) + ); + + $select = $connection->select() + ->from($connection->getTableName('vipps_login_authorization')) + ->where('code = ?', $code); + + $row = $connection->fetchRow($select); + if (!$row) { + try { + $connection->insert( + $connection->getTableName('vipps_login_authorization'), + [ + 'code' => $code, + 'created_at' => (new \DateTime())->format('Y-m-d H:i:s'), + ] + ); + + $row = $connection->fetchRow($select); + } catch (\Throwable $t) { + return null; + } + } + + return $row; + } + + private function storeAuthorizationRecord($code, $payload) + { + $connection = $this->resourceConnection->getConnection('write'); + + return $connection->update( + $connection->getTableName('vipps_login_authorization'), + [ + 'payload' => $payload + ], + 'code = ' . $connection->quote($code) + ); + } + + private function prepareResponse($body): array + { + $token = $this->serializer->unserialize($body); + $payload = $this->getPayload($token); + + $token['id_token_payload'] = $payload; + + return $token; + } + /** * @param $token * diff --git a/composer.json b/composer.json index f3b2b90..4fa8126 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "psr/log": "~1.0" }, "type": "magento2-module", - "version": "2.4.12", + "version": "2.4.13", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/etc/db_schema.xml b/etc/db_schema.xml index 67f2e3f..0d66f99 100644 --- a/etc/db_schema.xml +++ b/etc/db_schema.xml @@ -91,4 +91,20 @@ table="vipps_quote_addresses_relation" column="vipps_customer_address_id" referenceTable="vipps_customer_address" referenceColumn="entity_id" onDelete="CASCADE"/> + + + + + + + + + + + + + + +
diff --git a/etc/db_schema_whitelist.json b/etc/db_schema_whitelist.json index 77a46e9..95bb0db 100644 --- a/etc/db_schema_whitelist.json +++ b/etc/db_schema_whitelist.json @@ -51,5 +51,17 @@ "VIPPS_QUOTE_ADDRES_RELATION_QUOTE_ADDR_ID_QUOTE_ADDR_ADDR_ID": true, "FK_46E162119F9C6A5527FB629B614079C7": true } + }, + "vipps_login_authorization": { + "column": { + "entity_id": true, + "code": true, + "payload": true, + "created_at": true + }, + "constraint": { + "PRIMARY": true, + "VIPPS_LOGIN_AUTHORIZATION_CODE": true + } } } \ No newline at end of file diff --git a/etc/module.xml b/etc/module.xml index 6e2750e..ced1504 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -15,7 +15,7 @@ --> - +