diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/RequestPasswordResetEmail.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/RequestPasswordResetEmail.php
new file mode 100644
index 0000000000000..d0985d6acaf19
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/RequestPasswordResetEmail.php
@@ -0,0 +1,116 @@
+authentication = $authentication;
+ $this->customerRepository = $customerRepository;
+ $this->customerAccountManagement = $customerAccountManagement;
+ $this->emailValidator = $emailValidator;
+ }
+
+ /**
+ * Send password email request
+ *
+ * @param Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ *
+ * @return bool|Value|mixed
+ *
+ * @throws GraphQlInputException
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (empty($args['email'])) {
+ throw new GraphQlInputException(__('You must specify an email address.'));
+ }
+
+ if (!$this->emailValidator->isValid($args['email'])) {
+ throw new GraphQlInputException(__('The email address has an invalid format.'));
+ }
+
+ try {
+ $customer = $this->customerRepository->get($args['email']);
+ } catch (LocalizedException $e) {
+ throw new GraphQlInputException(__('Cannot reset the customer\'s password'), $e);
+ }
+
+ if (true === $this->authentication->isLocked($customer->getId())) {
+ throw new GraphQlInputException(__('The account is locked'));
+ }
+
+ try {
+ return $this->customerAccountManagement->initiatePasswordReset(
+ $args['email'],
+ AccountManagement::EMAIL_RESET
+ );
+ } catch (LocalizedException $e) {
+ throw new GraphQlInputException(__('Cannot reset the customer\'s password'), $e);
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ResetPassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ResetPassword.php
new file mode 100644
index 0000000000000..fa2ae669cc89d
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ResetPassword.php
@@ -0,0 +1,124 @@
+authentication = $authentication;
+ $this->customerRepository = $customerRepository;
+ $this->customerAccountManagement = $customerAccountManagement;
+ $this->emailValidator = $emailValidator;
+ }
+
+ /**
+ * Reset old password and set new
+ *
+ * @param Field $field
+ * @param ContextInterface $context
+ * @param ResolveInfo $info
+ * @param array|null $value
+ * @param array|null $args
+ *
+ * @return bool|Value|mixed
+ *
+ * @throws GraphQlInputException
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (empty($args['email'])) {
+ throw new GraphQlInputException(__('You must specify an email address.'));
+ }
+
+ if (!$this->emailValidator->isValid($args['email'])) {
+ throw new GraphQlInputException(__('The email address has an invalid format.'));
+ }
+
+ if (empty($args['resetPasswordToken'])) {
+ throw new GraphQlInputException(__('resetPasswordToken must be specified'));
+ }
+
+ if (empty($args['newPassword'])) {
+ throw new GraphQlInputException(__('newPassword must be specified'));
+ }
+
+ try {
+ $customer = $this->customerRepository->get($args['email']);
+ } catch (LocalizedException $e) {
+ throw new GraphQlInputException(__('Cannot set the customer\'s password'), $e);
+ }
+
+ if (true === $this->authentication->isLocked($customer->getId())) {
+ throw new GraphQlInputException(__('The account is locked'));
+ }
+
+ try {
+ return $this->customerAccountManagement->resetPassword(
+ $args['email'],
+ $args['resetPasswordToken'],
+ $args['newPassword']
+ );
+ } catch (LocalizedException $e) {
+ throw new GraphQlInputException(__('Cannot set the customer\'s password'), $e);
+ }
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
index cb4ab0b7b27f4..1ba0e457430e0 100644
--- a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
+++ b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
@@ -20,4 +20,13 @@
+
+
+
+ - customer/password/required_character_classes_number
+ - customer/password/minimum_password_length
+ - customer/password/autocomplete_on_storefront
+
+
+
diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
index 9aa1fdaa841e4..3bf36d11e71fe 100644
--- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
@@ -1,6 +1,12 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.
+type StoreConfig {
+ required_character_classes_number : String @doc(description: "The number of different character classes required in a password (lowercase, uppercase, digits, special characters).")
+ minimum_password_length : String @doc(description: "The minimum number of characters required for a valid password.")
+ autocomplete_on_storefront : Boolean @doc(description: "Enable autocomplete on login and forgot password forms")
+}
+
type Query {
customer: Customer @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Customer") @doc(description: "The customer query returns information about a customer account") @cache(cacheable: false)
isEmailAvailable (
@@ -17,6 +23,8 @@ type Mutation {
createCustomerAddress(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\CreateCustomerAddress") @doc(description: "Create customer address")
updateCustomerAddress(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddress") @doc(description: "Update customer address")
deleteCustomerAddress(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddress") @doc(description: "Delete customer address")
+ requestPasswordResetEmail(email: String!): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RequestPasswordResetEmail") @doc(description: "Request an email with a reset password token for the registered customer identified by the specified email.")
+ resetPassword(email: String!, resetPasswordToken: String!, newPassword: String!): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ResetPassword") @doc(description: "Reset a customer's password using the reset password token that the customer received in an email after requesting it using requestPasswordResetEmail.")
}
input CustomerAddressInput {
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RequestPasswordResetEmailTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RequestPasswordResetEmailTest.php
new file mode 100644
index 0000000000000..9f30124fbb54a
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RequestPasswordResetEmailTest.php
@@ -0,0 +1,112 @@
+lockCustomer = Bootstrap::getObjectManager()->get(LockCustomer::class);
+ }
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testCustomerAccountWithEmailAvailable()
+ {
+ $query =
+ <<graphQlMutation($query);
+
+ self::assertArrayHasKey('requestPasswordResetEmail', $response);
+ self::assertTrue($response['requestPasswordResetEmail']);
+ }
+
+ /**
+ * Check if customer account is not available
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Cannot reset the customer's password
+ */
+ public function testCustomerAccountWithEmailNotAvailable()
+ {
+ $query =
+ <<graphQlMutation($query);
+ }
+
+ /**
+ * Check if email value empty
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage You must specify an email address.
+ */
+ public function testEmailAvailableEmptyValue()
+ {
+ $query = <<graphQlMutation($query);
+ }
+
+ /**
+ * Check if email is invalid
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage The email address has an invalid format.
+ */
+ public function testEmailAvailableInvalidValue()
+ {
+ $query = <<graphQlMutation($query);
+ }
+
+ /**
+ * Check if email was sent for lock customer
+ *
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage The account is locked
+ */
+ public function testRequestPasswordResetEmailForLockCustomer()
+ {
+ $this->lockCustomer->execute(1);
+ $query =
+ <<graphQlMutation($query);
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ResetPasswordTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ResetPasswordTest.php
new file mode 100644
index 0000000000000..ff9831abb590d
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ResetPasswordTest.php
@@ -0,0 +1,265 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class);
+ $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class);
+ $this->lockCustomer = Bootstrap::getObjectManager()->get(LockCustomer::class);
+ parent::setUp();
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ * @throws NoSuchEntityException
+ * @throws Exception
+ *
+ * @throws LocalizedException
+ */
+ public function testResetCustomerAccountPasswordSuccessfully(): void
+ {
+ $query = <<getCustomerEmail()}"
+ resetPasswordToken: "{$this->getResetPasswordToken()}"
+ newPassword: "{$this->getNewPassword()}"
+ )
+}
+QUERY;
+ $response = $this->graphQlMutation($query);
+ self::assertArrayHasKey('resetPassword', $response);
+ self::assertTrue($response['resetPassword']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage You must specify an email address.
+ *
+ * @throws NoSuchEntityException
+ * @throws Exception
+ * @throws LocalizedException
+ */
+ public function testEmailAvailableEmptyValue()
+ {
+ $query = <<getResetPasswordToken()}"
+ newPassword: "{$this->getNewPassword()}"
+ )
+}
+QUERY;
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage The email address has an invalid format.
+ *
+ * @throws NoSuchEntityException
+ * @throws Exception
+ * @throws LocalizedException
+ */
+ public function testEmailInvalidValue()
+ {
+ $query = <<getResetPasswordToken()}"
+ newPassword: "{$this->getNewPassword()}"
+ )
+}
+QUERY;
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage resetPasswordToken must be specified
+ *
+ * @throws NoSuchEntityException
+ * @throws Exception
+ * @throws LocalizedException
+ */
+ public function testResetPasswordTokenEmptyValue()
+ {
+ $query = <<getCustomerEmail()}"
+ resetPasswordToken: ""
+ newPassword: "{$this->getNewPassword()}"
+ )
+}
+QUERY;
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Cannot set the customer's password
+ *
+ * @throws NoSuchEntityException
+ * @throws Exception
+ * @throws LocalizedException
+ */
+ public function testResetPasswordTokenMismatched()
+ {
+ $query = <<getCustomerEmail()}"
+ resetPasswordToken: "1234567890XYZ"
+ newPassword: "{$this->getNewPassword()}"
+ )
+}
+QUERY;
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage newPassword must be specified
+ *
+ * @throws NoSuchEntityException
+ * @throws Exception
+ * @throws LocalizedException
+ */
+ public function testNewPasswordEmptyValue()
+ {
+ $query = <<getCustomerEmail()}"
+ resetPasswordToken: "{$this->getResetPasswordToken()}"
+ newPassword: ""
+ )
+}
+QUERY;
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * Check password reset for lock customer
+ *
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage The account is locked
+ *
+ * @throws LocalizedException
+ * @throws NoSuchEntityException
+ */
+ public function testPasswordResetForLockCustomer()
+ {
+ $this->lockCustomer->execute(1);
+ $query = <<getCustomerEmail()}"
+ resetPasswordToken: "{$this->getResetPasswordToken()}"
+ newPassword: "{$this->getNewPassword()}"
+ )
+}
+QUERY;
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * Get reset password token
+ *
+ * @return string
+ *
+ * @throws LocalizedException
+ * @throws NoSuchEntityException
+ */
+ private function getResetPasswordToken()
+ {
+ $this->accountManagement->initiatePasswordReset(
+ $this->getCustomerEmail(),
+ AccountManagement::EMAIL_RESET,
+ 1
+ );
+
+ $customerSecure = $this->customerRegistry->retrieveSecureData(1);
+ return $customerSecure->getRpToken();
+ }
+
+ /**
+ * Get customer email
+ *
+ * @return string
+ */
+ private function getCustomerEmail()
+ {
+ return self::CUSTOMER_EMAIL;
+ }
+
+ /**
+ * Get new password for customer account
+ *
+ * @return string
+ */
+ private function getNewPassword()
+ {
+ return self::CUSTOMER_NEW_PASSWORD;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/StoreConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/StoreConfigTest.php
new file mode 100644
index 0000000000000..473105c2c46be
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/StoreConfigTest.php
@@ -0,0 +1,38 @@
+graphQlQuery($query);
+ self::assertArrayHasKey('autocomplete_on_storefront', $response['storeConfig']);
+ self::assertTrue($response['storeConfig']['autocomplete_on_storefront']);
+ }
+}