Skip to content

Commit 67ba6dd

Browse files
eliseacornejoglo74186
authored andcommitted
LYNX-391: Update Error Handling for generateCustomerToken Mutation (#227)
1 parent c75fb3f commit 67ba6dd

File tree

7 files changed

+178
-61
lines changed

7 files changed

+178
-61
lines changed

app/code/Magento/Customer/Model/AccountManagement/Authenticate.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,18 @@ public function execute(string $email, string $password): CustomerInterface
9292
if ($this->authentication->isLocked($customerId)) {
9393
throw new UserLockedException(__('The account is locked.'));
9494
}
95-
try {
96-
$this->authentication->authenticate($customerId, $password);
97-
} catch (InvalidEmailOrPasswordException $exception) {
98-
throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
99-
}
10095

10196
if ($customer->getConfirmation()
10297
&& ($this->isConfirmationRequired($customer) || $this->isEmailChangedConfirmationRequired($customer))) {
10398
throw new EmailNotConfirmedException(__('This account isn\'t confirmed. Verify and try again.'));
10499
}
105100

101+
try {
102+
$this->authentication->authenticate($customerId, $password);
103+
} catch (InvalidEmailOrPasswordException $exception) {
104+
throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
105+
}
106+
106107
$customerModel = $this->customerFactory->create()->updateData($customer);
107108
$this->eventManager->dispatch(
108109
'customer_customer_authenticated',

app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\CustomerGraphQl\Model\Resolver;
99

1010
use Magento\Framework\Exception\AuthenticationException;
11+
use Magento\Framework\Exception\EmailNotConfirmedException;
1112
use Magento\Framework\GraphQl\Config\Element\Field;
1213
use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException;
1314
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
@@ -55,8 +56,8 @@ public function resolve(
5556
try {
5657
$token = $this->customerTokenService->createCustomerAccessToken($args['email'], $args['password']);
5758
return ['token' => $token];
58-
} catch (AuthenticationException $e) {
59-
throw new GraphQlAuthenticationException(__($e->getMessage()), $e);
59+
} catch (EmailNotConfirmedException|AuthenticationException $e) {
60+
throw new GraphQlAuthenticationException(__($e->getRawMessage()), $e);
6061
}
6162
}
6263
}

app/code/Magento/Integration/Api/CustomerTokenServiceInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface CustomerTokenServiceInterface
2121
* @param string $password
2222
* @return string Token created
2323
* @throws \Magento\Framework\Exception\AuthenticationException
24+
* @throws \Magento\Framework\Exception\EmailNotConfirmedException
2425
*/
2526
public function createCustomerAccessToken($username, $password);
2627

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ************************************************************************
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace Magento\Integration\Api;
21+
22+
use Magento\Authorization\Model\UserContextInterface;
23+
use Magento\Framework\App\ObjectManager;
24+
use Magento\Integration\Api\Data\UserTokenParametersInterface;
25+
use Magento\Integration\Model\UserToken\UserTokenParameters;
26+
use Magento\Integration\Model\UserToken\UserTokenParametersFactory;
27+
28+
/**
29+
* Issues tokens used to authenticate users.
30+
*/
31+
class TokenManager
32+
{
33+
/**
34+
* @var UserTokenParametersFactory
35+
*/
36+
private $tokenParametersFactory;
37+
38+
/**
39+
* @var UserTokenIssuerInterface
40+
*/
41+
private $tokenIssuer;
42+
43+
/**
44+
* @var UserTokenRevokerInterface
45+
*/
46+
private $tokenRevoker;
47+
48+
/**
49+
* @param UserTokenParametersFactory|null $tokenParamsFactory
50+
* @param UserTokenIssuerInterface|null $tokenIssuer
51+
* @param UserTokenRevokerInterface|null $tokenRevoker
52+
*/
53+
public function __construct(
54+
?UserTokenParametersFactory $tokenParamsFactory = null,
55+
?UserTokenIssuerInterface $tokenIssuer = null,
56+
?UserTokenRevokerInterface $tokenRevoker = null
57+
) {
58+
$this->tokenParametersFactory = $tokenParamsFactory
59+
?? ObjectManager::getInstance()->get(UserTokenParametersFactory::class);
60+
$this->tokenIssuer = $tokenIssuer ?? ObjectManager::getInstance()->get(UserTokenIssuerInterface::class);
61+
$this->tokenRevoker = $tokenRevoker ?? ObjectManager::getInstance()->get(UserTokenRevokerInterface::class);
62+
}
63+
64+
/**
65+
* Create class instance with specified parameters
66+
*
67+
* @param array $data
68+
* @return UserTokenParameters
69+
*/
70+
public function createUserTokenParameters(array $data = []): UserTokenParameters
71+
{
72+
return $this->tokenParametersFactory->create($data);
73+
}
74+
75+
/**
76+
* Create token for a user.
77+
*
78+
* @param UserContextInterface $userContext
79+
* @param UserTokenParametersInterface $params
80+
* @return string
81+
*/
82+
public function create(UserContextInterface $userContext, UserTokenParametersInterface $params): string
83+
{
84+
return $this->tokenIssuer->create($userContext, $params);
85+
}
86+
87+
/**
88+
* Revoke all previously issued tokens for given user.
89+
*
90+
* @param UserContextInterface $userContext
91+
* @return void
92+
*/
93+
public function revokeFor(UserContextInterface $userContext): void
94+
{
95+
$this->tokenRevoker->revokeFor($userContext);
96+
}
97+
}

app/code/Magento/Integration/Model/CustomerTokenService.php

Lines changed: 16 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@
88

99
use Magento\Customer\Api\AccountManagementInterface;
1010
use Magento\Framework\App\ObjectManager;
11+
use Magento\Framework\Exception\EmailNotConfirmedException;
1112
use Magento\Framework\Exception\LocalizedException;
12-
use Magento\Integration\Model\UserToken\UserTokenParametersFactory;
13+
use Magento\Integration\Api\TokenManager;
1314
use Magento\Integration\Api\Exception\UserTokenException;
14-
use Magento\Integration\Api\UserTokenIssuerInterface;
15-
use Magento\Integration\Api\UserTokenRevokerInterface;
16-
use Magento\Integration\Model\Oauth\TokenFactory as TokenModelFactory;
17-
use Magento\Integration\Model\ResourceModel\Oauth\Token\CollectionFactory as TokenCollectionFactory;
1815
use Magento\Integration\Model\Oauth\Token\RequestThrottler;
1916
use Magento\Framework\Exception\AuthenticationException;
2017
use Magento\Framework\Event\ManagerInterface;
@@ -30,67 +27,26 @@ class CustomerTokenService implements CustomerTokenServiceInterface
3027
*/
3128
private $eventManager;
3229

33-
/**
34-
* Customer Account Service
35-
*
36-
* @var AccountManagementInterface
37-
*/
38-
private $accountManagement;
39-
40-
/**
41-
* @var CredentialsValidator
42-
*/
43-
private $validatorHelper;
44-
4530
/**
4631
* @var RequestThrottler
4732
*/
4833
private $requestThrottler;
4934

50-
/**
51-
* @var UserTokenParametersFactory
52-
*/
53-
private $tokenParametersFactory;
54-
55-
/**
56-
* @var UserTokenIssuerInterface
57-
*/
58-
private $tokenIssuer;
59-
60-
/**
61-
* @var UserTokenRevokerInterface
62-
*/
63-
private $tokenRevoker;
64-
6535
/**
6636
* Initialize service
6737
*
68-
* @param TokenModelFactory $tokenModelFactory
6938
* @param AccountManagementInterface $accountManagement
70-
* @param TokenCollectionFactory $tokenModelCollectionFactory
7139
* @param CredentialsValidator $validatorHelper
40+
* @param TokenManager $tokenManager
7241
* @param ManagerInterface|null $eventManager
73-
* @param UserTokenParametersFactory|null $tokenParamsFactory
74-
* @param UserTokenIssuerInterface|null $tokenIssuer
75-
* @param UserTokenRevokerInterface|null $tokenRevoker
7642
*/
7743
public function __construct(
78-
TokenModelFactory $tokenModelFactory,
79-
AccountManagementInterface $accountManagement,
80-
TokenCollectionFactory $tokenModelCollectionFactory,
81-
CredentialsValidator $validatorHelper,
82-
ManagerInterface $eventManager = null,
83-
?UserTokenParametersFactory $tokenParamsFactory = null,
84-
?UserTokenIssuerInterface $tokenIssuer = null,
85-
?UserTokenRevokerInterface $tokenRevoker = null
44+
private readonly AccountManagementInterface $accountManagement,
45+
private readonly CredentialsValidator $validatorHelper,
46+
private readonly TokenManager $tokenManager,
47+
ManagerInterface $eventManager = null
8648
) {
87-
$this->accountManagement = $accountManagement;
88-
$this->validatorHelper = $validatorHelper;
8949
$this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(ManagerInterface::class);
90-
$this->tokenParametersFactory = $tokenParamsFactory
91-
?? ObjectManager::getInstance()->get(UserTokenParametersFactory::class);
92-
$this->tokenIssuer = $tokenIssuer ?? ObjectManager::getInstance()->get(UserTokenIssuerInterface::class);
93-
$this->tokenRevoker = $tokenRevoker ?? ObjectManager::getInstance()->get(UserTokenRevokerInterface::class);
9450
}
9551

9652
/**
@@ -102,6 +58,9 @@ public function createCustomerAccessToken($username, $password)
10258
$this->getRequestThrottler()->throttle($username, RequestThrottler::USER_TYPE_CUSTOMER);
10359
try {
10460
$customerDataObject = $this->accountManagement->authenticate($username, $password);
61+
} catch (EmailNotConfirmedException $exception) {
62+
$this->getRequestThrottler()->logAuthenticationFailure($username, RequestThrottler::USER_TYPE_CUSTOMER);
63+
throw $exception;
10564
} catch (\Exception $e) {
10665
$this->getRequestThrottler()->logAuthenticationFailure($username, RequestThrottler::USER_TYPE_CUSTOMER);
10766
throw new AuthenticationException(
@@ -117,9 +76,9 @@ public function createCustomerAccessToken($username, $password)
11776
(int)$customerDataObject->getId(),
11877
CustomUserContext::USER_TYPE_CUSTOMER
11978
);
120-
$params = $this->tokenParametersFactory->create();
79+
$params = $this->tokenManager->createUserTokenParameters();
12180

122-
return $this->tokenIssuer->create($context, $params);
81+
return $this->tokenManager->create($context, $params);
12382
}
12483

12584
/**
@@ -132,7 +91,9 @@ public function createCustomerAccessToken($username, $password)
13291
public function revokeCustomerAccessToken($customerId)
13392
{
13493
try {
135-
$this->tokenRevoker->revokeFor(new CustomUserContext((int)$customerId, CustomUserContext::USER_TYPE_CUSTOMER));
94+
$this->tokenManager->revokeFor(
95+
new CustomUserContext((int)$customerId, CustomUserContext::USER_TYPE_CUSTOMER)
96+
);
13697
} catch (UserTokenException $exception) {
13798
throw new LocalizedException(__('Failed to revoke customer\'s access tokens'), $exception);
13899
}
@@ -144,6 +105,7 @@ public function revokeCustomerAccessToken($customerId)
144105
*
145106
* @return RequestThrottler
146107
* @deprecated 100.0.4
108+
* @see no alternatives
147109
*/
148110
private function getRequestThrottler()
149111
{

dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GenerateCustomerTokenTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77

88
namespace Magento\GraphQl\Customer;
99

10+
use Magento\Customer\Api\AccountManagementInterface;
1011
use Magento\Customer\Model\Log;
1112
use Magento\Customer\Model\Logger;
13+
use Magento\Customer\Test\Fixture\Customer;
1214
use Magento\Framework\App\ResourceConnection;
1315
use Magento\Framework\DB\Adapter\AdapterInterface;
16+
use Magento\Framework\Exception\EmailNotConfirmedException;
17+
use Magento\TestFramework\Fixture\Config;
18+
use Magento\TestFramework\Fixture\DataFixture;
19+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
1420
use Magento\TestFramework\Helper\Bootstrap;
1521
use Magento\TestFramework\TestCase\GraphQlAbstract;
1622

@@ -66,6 +72,27 @@ public function testGenerateCustomerTokenInvalidData(string $email, string $pass
6672
$this->graphQlMutation($mutation);
6773
}
6874

75+
#[
76+
Config('customer/create_account/confirm', 1),
77+
DataFixture(
78+
Customer::class,
79+
[
80+
'email' => 'another@example.com',
81+
'confirmation' => 'account_not_confirmed'
82+
],
83+
'customer'
84+
)
85+
]
86+
public function testGenerateCustomerEmailNotConfirmed()
87+
{
88+
$this->expectException(\Exception::class);
89+
$customer = DataFixtureStorageManager::getStorage()->get('customer');
90+
91+
$mutation = $this->getQuery($customer->getEmail());
92+
$this->expectExceptionMessage("This account isn't confirmed. Verify and try again.");
93+
$this->graphQlMutation($mutation);
94+
}
95+
6996
/**
7097
* Test customer token regeneration.
7198
*

dev/tests/integration/testsuite/Magento/Integration/Model/CustomerTokenServiceTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@
77
namespace Magento\Integration\Model;
88

99
use Magento\Customer\Api\AccountManagementInterface;
10+
use Magento\Customer\Test\Fixture\Customer;
11+
use Magento\Framework\Exception\EmailNotConfirmedException;
1012
use Magento\Framework\Exception\InputException;
1113
use Magento\Integration\Model\Oauth\Token as TokenModel;
14+
use Magento\TestFramework\Fixture\Config;
15+
use Magento\TestFramework\Fixture\DataFixture;
16+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
1217
use Magento\TestFramework\Helper\Bootstrap;
1318

1419
/**
@@ -84,6 +89,29 @@ public function testCreateCustomerAccessTokenInvalidCustomer()
8489
);
8590
}
8691

92+
#[
93+
Config('customer/create_account/confirm', 1, 'website'),
94+
DataFixture(
95+
Customer::class,
96+
[
97+
'email' => 'another@example.com',
98+
'confirmation' => 'account_not_confirmed'
99+
],
100+
'customer'
101+
)
102+
]
103+
public function testCreateCustomerAccessTokenEmailNotConfirmed()
104+
{
105+
$customer = DataFixtureStorageManager::getStorage()->get('customer');
106+
$this->expectException(EmailNotConfirmedException::class);
107+
108+
$this->tokenService->createCustomerAccessToken($customer->getEmail(), 'password');
109+
110+
$this->expectExceptionMessage(
111+
"This account isn't confirmed. Verify and try again."
112+
);
113+
}
114+
87115
/**
88116
* Provider to test input validation
89117
*

0 commit comments

Comments
 (0)