diff --git a/api/src/identity-access-management/application/password/password.controller.js b/api/src/identity-access-management/application/password/password.controller.js
index 2d588f3bc4a..de7b11c3798 100644
--- a/api/src/identity-access-management/application/password/password.controller.js
+++ b/api/src/identity-access-management/application/password/password.controller.js
@@ -9,7 +9,7 @@ const checkResetDemand = async function (request, h, dependencies = { userSerial
return dependencies.userSerializer.serialize(user);
};
const createResetPasswordDemand = async function (request, h, dependencies = { resetPasswordSerializer }) {
- const { email } = request.payload.data.attributes;
+ const email = request.payload.email;
const locale = extractLocaleFromRequest(request);
const resetPasswordDemand = await usecases.createResetPasswordDemand({
diff --git a/api/src/identity-access-management/application/password/password.route.js b/api/src/identity-access-management/application/password/password.route.js
index c25664d94cd..031f98e6401 100644
--- a/api/src/identity-access-management/application/password/password.route.js
+++ b/api/src/identity-access-management/application/password/password.route.js
@@ -11,6 +11,10 @@ export const passwordRoutes = [
handler: (request, h) => passwordController.createResetPasswordDemand(request, h),
validate: {
payload: Joi.object({
+ email: Joi.when('data.attributes.email', {
+ then: Joi.string().email().default(Joi.ref('data.attributes.email')),
+ otherwise: Joi.string().email().required(),
+ }),
data: {
attributes: {
email: Joi.string().email().required(),
diff --git a/api/tests/identity-access-management/acceptance/application/password/password.route.test.js b/api/tests/identity-access-management/acceptance/application/password/password.route.test.js
index 700b61b7a94..438d7bbcf03 100644
--- a/api/tests/identity-access-management/acceptance/application/password/password.route.test.js
+++ b/api/tests/identity-access-management/acceptance/application/password/password.route.test.js
@@ -13,44 +13,85 @@ describe('Acceptance | Identity Access Management | Application | Route | passwo
describe('POST /api/password-reset-demands', function () {
let options;
- beforeEach(async function () {
- options = {
- method: 'POST',
- url: '/api/password-reset-demands',
- payload: {
- data: {
- attributes: { email },
- },
- },
- };
+ context('with simple payload', function () {
+ beforeEach(async function () {
+ options = {
+ method: 'POST',
+ url: '/api/password-reset-demands',
+ payload: { email },
+ };
- config.mailing.enabled = false;
+ config.mailing.enabled = false;
- const userId = databaseBuilder.factory.buildUser({ email }).id;
- databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ userId });
- await databaseBuilder.commit();
- });
+ const userId = databaseBuilder.factory.buildUser({ email }).id;
+ databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ userId });
+ await databaseBuilder.commit();
+ });
+
+ context('when given email doesn’t exist', function () {
+ it('replies with 404', async function () {
+ // given
+ options.payload.email = 'unknown@example.net';
- context('when email provided is unknown', function () {
- it('replies with 404', async function () {
- // given
- options.payload.data.attributes.email = 'unknown@example.net';
+ // when
+ const response = await server.inject(options);
+
+ // then
+ expect(response.statusCode).to.equal(404);
+ });
+ });
- // when
- const response = await server.inject(options);
+ context('when given email exists', function () {
+ it('replies with 201', async function () {
+ // when
+ const response = await server.inject(options);
- // then
- expect(response.statusCode).to.equal(404);
+ // then
+ expect(response.statusCode).to.equal(201);
+ });
});
});
- context('when existing email is provided', function () {
- it('replies with 201', async function () {
- // when
- const response = await server.inject(options);
+ context('with deprecated ember-data-centric payload', function () {
+ beforeEach(async function () {
+ options = {
+ method: 'POST',
+ url: '/api/password-reset-demands',
+ payload: {
+ data: {
+ attributes: { email },
+ },
+ },
+ };
+
+ config.mailing.enabled = false;
+
+ const userId = databaseBuilder.factory.buildUser({ email }).id;
+ databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ userId });
+ await databaseBuilder.commit();
+ });
+
+ context('when given email doesn’t exist', function () {
+ it('replies with 404', async function () {
+ // given
+ options.payload.data.attributes.email = 'unknown@example.net';
+
+ // when
+ const response = await server.inject(options);
+
+ // then
+ expect(response.statusCode).to.equal(404);
+ });
+ });
+
+ context('when given email exists', function () {
+ it('replies with 201', async function () {
+ // when
+ const response = await server.inject(options);
- // then
- expect(response.statusCode).to.equal(201);
+ // then
+ expect(response.statusCode).to.equal(201);
+ });
});
});
});
diff --git a/api/tests/identity-access-management/integration/application/password/password.controller.test.js b/api/tests/identity-access-management/integration/application/password/password.controller.test.js
index 0ec0058d9cd..f79a3931ae2 100644
--- a/api/tests/identity-access-management/integration/application/password/password.controller.test.js
+++ b/api/tests/identity-access-management/integration/application/password/password.controller.test.js
@@ -20,12 +20,7 @@ describe('Integration | Identity Access Management | Application | Controller |
const headers = {
'accept-language': 'fr',
};
- const payload = {
- data: {
- type: 'password-reset-demands',
- attributes: { email },
- },
- };
+ const payload = { email };
it('returns a 201 HTTP status code with a response', async function () {
// given
diff --git a/api/tests/identity-access-management/unit/application/password/password.controller.test.js b/api/tests/identity-access-management/unit/application/password/password.controller.test.js
index 5f0b637ece2..211b4b15e39 100644
--- a/api/tests/identity-access-management/unit/application/password/password.controller.test.js
+++ b/api/tests/identity-access-management/unit/application/password/password.controller.test.js
@@ -41,11 +41,7 @@ describe('Unit | Identity Access Management | Application | Controller | passwor
headers: {
'accept-language': locale,
},
- payload: {
- data: {
- attributes: { email },
- },
- },
+ payload: { email },
};
const resetPasswordDemand = {
id: 1,
diff --git a/mon-pix/app/components/authentication/password-reset-demand-form.gjs b/mon-pix/app/components/authentication/password-reset-demand-form.gjs
new file mode 100644
index 00000000000..ffb61b74e33
--- /dev/null
+++ b/mon-pix/app/components/authentication/password-reset-demand-form.gjs
@@ -0,0 +1,122 @@
+import PixButton from '@1024pix/pix-ui/components/pix-button';
+import PixButtonLink from '@1024pix/pix-ui/components/pix-button-link';
+import PixInput from '@1024pix/pix-ui/components/pix-input';
+import PixMessage from '@1024pix/pix-ui/components/pix-message';
+import { on } from '@ember/modifier';
+import { action } from '@ember/object';
+import { service } from '@ember/service';
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { t } from 'ember-intl';
+import ENV from 'mon-pix/config/environment';
+
+import isEmailValid from '../../utils/email-validator.js';
+
+export default class PasswordResetDemandForm extends Component {
+ @service intl;
+
+ @tracked isLoading = false;
+ @tracked errorMessage;
+ @tracked emailInputPlaceholder = this.intl.t(
+ 'components.authentication.password-reset-demand-form.fields.email.placeholder',
+ );
+ @tracked emailInputvalidationStatus;
+ @tracked emailInputvalidationErrorMessage;
+
+ email;
+
+ @action
+ handleEmailChange(event) {
+ this.email = event.target.value;
+ this.emailInputvalidationStatus = isEmailValid(this.email) ? 'success' : 'error';
+ this.emailInputvalidationErrorMessage = this.intl.t(
+ 'components.authentication.password-reset-demand-form.fields.email.error-message-invalid',
+ );
+ }
+
+ @action
+ async handlePasswordResetDemand(event) {
+ if (event) event.preventDefault();
+
+ this.errorMessage = null;
+
+ const email = this.email.trim();
+ if (!email || this.emailInputvalidationStatus === 'error') {
+ return;
+ }
+
+ try {
+ this.isLoading = true;
+ const response = await window.fetch(`${ENV.APP.API_HOST}/api/password-reset-demands`, {
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
+ method: 'POST',
+ body: JSON.stringify({ email }),
+ });
+ if (response.status == 404) {
+ this.errorMessage = this.intl.t('components.authentication.password-reset-demand-form.404-message');
+ } else if (!response.ok) {
+ throw new Error(`Response status: ${response.status}`);
+ }
+ } catch (error) {
+ this.errorMessage = this.intl.t('common.api-error-messages.internal-server-error');
+ } finally {
+ this.isLoading = false;
+ }
+ }
+
+
+
+
+}
diff --git a/mon-pix/app/components/authentication/password-reset-demand.scss b/mon-pix/app/components/authentication/password-reset-demand.scss
new file mode 100644
index 00000000000..581c7d5de1a
--- /dev/null
+++ b/mon-pix/app/components/authentication/password-reset-demand.scss
@@ -0,0 +1,41 @@
+.authentication-password-reset-demand-form {
+ input {
+ padding: var(--pix-spacing-3x);
+ }
+
+ &__rule {
+ @extend %pix-body-xs;
+
+ color: var(--pix-neutral-500);
+ }
+
+ &__error {
+ margin-top: var(--pix-spacing-4x);
+ }
+
+ &__input-block {
+ margin-top: var(--pix-spacing-4x);
+
+ .pix-input {
+ width: 100%;
+ }
+ }
+
+ &__button {
+ width: 100%;
+ margin-top: var(--pix-spacing-4x);
+ }
+
+ &__help {
+ @extend %pix-body-s;
+
+ margin-top: var(--pix-spacing-4x);
+ color: var(--pix-neutral-70);
+ text-align: center;
+ }
+
+ &__help-contact-us-link {
+ display: inline;
+ padding: 0;
+ }
+}
\ No newline at end of file
diff --git a/mon-pix/app/components/form-textfield.hbs b/mon-pix/app/components/form-textfield.hbs
index 299dbd29f90..fa8f2ef20bd 100644
--- a/mon-pix/app/components/form-textfield.hbs
+++ b/mon-pix/app/components/form-textfield.hbs
@@ -1,3 +1,5 @@
+{{! DEPRECATED COMPONENT: DON’T USE IT! USE COMPONENTS OF THE DESIGN SYSTEM INSTEAD!! }}
+
{{! template-lint-disable require-input-label no-unknown-arguments-for-builtin-components }}