Open
Description
As a customer I want to confirm my account registration so that I can login to my account when confirmation is required.
Not having this essentially breaks GraphQL customer registration flow when the "Require Emails Confirmation" option is turned on. https://docs.magento.com/user-guide/customers/account-options-new.html
Seems pretty urgent since nobody wants anybody to be able to create an account email with somebody else's email.
AC
- GraphQL mutation is added for confirming user account registration from the email link.
- generateCustomerToken mutation is adjusted so that it throws an appropriate error when the user tries to login, but has not confirmed his account/
Proposed Schema
type Mutation {
customerConfirmRegistration(id: Int!, key: String!): ConfirmOutput @doc(description: "Confirm customer account registration and get access token") @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Confirm")
customerConfirmResend(email: String!): Boolean @doc(description: "Resend customer confirmation email") @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\ConfirmResend")
}
type ConfirmOutput {
token: String
}
To enable additional information when the customer tries to login but his account is not confirmed yet, something like this should be done:
--- magento/module-integration/Model/CustomerTokenService.php
+++ magento/module-integration/Model/CustomerTokenService.php
@@ -7,6 +7,7 @@
namespace Magento\Integration\Model;
use Magento\Customer\Api\AccountManagementInterface;
+use Magento\Framework\Exception\EmailNotConfirmedException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Integration\Model\CredentialsValidator;
use Magento\Integration\Model\Oauth\Token as Token;
@@ -90,6 +91,9 @@
$this->getRequestThrottler()->throttle($username, RequestThrottler::USER_TYPE_CUSTOMER);
try {
$customerDataObject = $this->accountManagement->authenticate($username, $password);
+ } catch (EmailNotConfirmedException $e) {
+ $this->getRequestThrottler()->logAuthenticationFailure($username, RequestThrottler::USER_TYPE_CUSTOMER);
+ throw $e;
} catch (\Exception $e) {
$this->getRequestThrottler()->logAuthenticationFailure($username, RequestThrottler::USER_TYPE_CUSTOMER);
throw new AuthenticationException(
--- /dev/null
+++ magento/module-customer-graph-ql/Exception/GraphQlAuthenticationEmailNotConfirmedException.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright © Vaimo Group. All rights reserved.
+ * See LICENSE_VAIMO.txt for license details.
+ */
+declare(strict_types=1);
+
+namespace Magento\CustomerGraphQl\Exception;
+
+use GraphQL\Error\ClientAware;
+use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException;
+use Magento\Framework\Phrase;
+
+class GraphQlAuthenticationEmailNotConfirmedException extends GraphQlAuthenticationException implements ClientAware
+{
+ public const EXCEPTION_CATEGORY = 'graphql-authentication-email-not-confirmed';
+
+ public function __construct(Phrase $phrase, \Exception $cause = null, int $code = 0)
+ {
+ parent::__construct($phrase, $cause, $code);
+ }
+
+ public function isClientSafe(): bool
+ {
+ return true;
+ }
+
+ public function getCategory(): string
+ {
+ return self::EXCEPTION_CATEGORY;
+ }
+}
--- magento/module-customer-graph-ql/Model/Resolver/GenerateCustomerToken.php
+++ magento/module-customer-graph-ql/Model/Resolver/GenerateCustomerToken.php
@@ -8,7 +8,9 @@
namespace Magento\CustomerGraphQl\Model\Resolver;
use Magento\Framework\Exception\AuthenticationException;
+use Magento\Framework\Exception\EmailNotConfirmedException;
use Magento\Framework\GraphQl\Config\Element\Field;
+use Magento\CustomerGraphQl\Exception\GraphQlAuthenticationEmailNotConfirmedException;
use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
@@ -55,7 +57,10 @@
try {
$token = $this->customerTokenService->createCustomerAccessToken($args['email'], $args['password']);
return ['token' => $token];
- } catch (AuthenticationException $e) {
+ } catch (EmailNotConfirmedException $e) {
+ throw new GraphQlAuthenticationEmailNotConfirmedException(__($e->getMessage()), $e);
+ }
+ catch (AuthenticationException $e) {
throw new GraphQlAuthenticationException(__($e->getMessage()), $e);
}
}