+
);
};
diff --git a/src/features/identity-setup/hooks/emailAddressRules.test.ts b/src/features/identity-setup/hooks/emailAddressRules.test.ts
new file mode 100644
index 0000000..9a8cbfd
--- /dev/null
+++ b/src/features/identity-setup/hooks/emailAddressRules.test.ts
@@ -0,0 +1,258 @@
+import { describe, expect, test } from 'vitest';
+import {
+ isEmailAddressValid,
+ isRuleStatusValid,
+ validateEmailAddress,
+ type EmailAddressRuleId,
+ type EmailAddressRuleStatus,
+} from './emailAddressRules';
+
+const ruleStatus = (username: string, id: EmailAddressRuleId): EmailAddressRuleStatus =>
+ validateEmailAddress(username).find((rule) => rule.id === id)!.status;
+
+describe('emailAddressRules', () => {
+ describe('length rule', () => {
+ test('When username has fewer than 3 characters, then it is invalid', () => {
+ const username = 'ab';
+
+ const status = ruleStatus(username, 'length');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username has exactly 3 characters, then it is valid', () => {
+ const username = 'abc';
+
+ const status = ruleStatus(username, 'length');
+
+ expect(status).toBe('valid');
+ });
+
+ test('When username has exactly 30 characters, then it is valid', () => {
+ const username = 'a'.repeat(30);
+
+ const status = ruleStatus(username, 'length');
+
+ expect(status).toBe('valid');
+ });
+
+ test('When username has more than 30 characters, then it is invalid', () => {
+ const username = 'a'.repeat(31);
+
+ const status = ruleStatus(username, 'length');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username is empty, then it is idle', () => {
+ const username = '';
+
+ const status = ruleStatus(username, 'length');
+
+ expect(status).toBe('idle');
+ });
+ });
+
+ describe('charset rule', () => {
+ test('When username only has lowercase letters, numbers, periods, hyphens and underscores, then it is valid', () => {
+ const username = 'jane.doe-99_x';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('valid');
+ });
+
+ test('When username contains an uppercase letter, then it is invalid', () => {
+ const username = 'Jane';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username contains a space, then it is invalid', () => {
+ const username = 'jane doe';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username contains an unsupported symbol, then it is invalid', () => {
+ const username = 'jane@doe';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username has two consecutive periods, then it is invalid', () => {
+ const username = 'jane..doe';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username has two consecutive underscores, then it is invalid', () => {
+ const username = 'jane__doe';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username mixes two different consecutive special characters, then it is invalid', () => {
+ const username = 'jane.-doe';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username has single, non-adjacent special characters, then it is valid', () => {
+ const username = 'jane.doe-99_x';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('valid');
+ });
+
+ test('When username is empty, then it is idle', () => {
+ const username = '';
+
+ const status = ruleStatus(username, 'charset');
+
+ expect(status).toBe('idle');
+ });
+ });
+
+ describe('edges rule', () => {
+ test('When username starts with a period, then it is invalid', () => {
+ const username = '.jane';
+
+ const status = ruleStatus(username, 'edges');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username ends with a hyphen, then it is invalid', () => {
+ const username = 'jane-';
+
+ const status = ruleStatus(username, 'edges');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username starts with an underscore, then it is invalid', () => {
+ const username = '_jane';
+
+ const status = ruleStatus(username, 'edges');
+
+ expect(status).toBe('invalid');
+ });
+
+ test('When username starts and ends with alphanumeric characters, then it is valid', () => {
+ const username = 'jane.doe';
+
+ const status = ruleStatus(username, 'edges');
+
+ expect(status).toBe('valid');
+ });
+
+ test('When username is empty, then it is idle', () => {
+ const username = '';
+
+ const status = ruleStatus(username, 'edges');
+
+ expect(status).toBe('idle');
+ });
+ });
+
+ describe('available rule', () => {
+ test.each(['admin', 'support', 'postmaster', 'noreply'])(
+ 'When username is the reserved name "%s", then it is invalid',
+ (reservedName) => {
+ const status = ruleStatus(reservedName, 'available');
+
+ expect(status).toBe('invalid');
+ },
+ );
+
+ test('When username is not a reserved name, then it is valid', () => {
+ const username = 'jane.doe';
+
+ const status = ruleStatus(username, 'available');
+
+ expect(status).toBe('valid');
+ });
+
+ test('When username is empty, then it is idle', () => {
+ const username = '';
+
+ const status = ruleStatus(username, 'available');
+
+ expect(status).toBe('idle');
+ });
+
+ test('When a formatting rule fails, then it stays idle instead of being checked', () => {
+ const username = 'Jane';
+
+ const status = ruleStatus(username, 'available');
+
+ expect(status).toBe('idle');
+ });
+ });
+
+ describe('isRuleStatusValid', () => {
+ test('When status is valid, then it returns true', () => {
+ const status: EmailAddressRuleStatus = 'valid';
+
+ const result = isRuleStatusValid(status);
+
+ expect(result).toBe(true);
+ });
+
+ test('When status is invalid, then it returns false', () => {
+ const status: EmailAddressRuleStatus = 'invalid';
+
+ const result = isRuleStatusValid(status);
+
+ expect(result).toBe(false);
+ });
+
+ test('When status is idle, then it returns false', () => {
+ const status: EmailAddressRuleStatus = 'idle';
+
+ const result = isRuleStatusValid(status);
+
+ expect(result).toBe(false);
+ });
+ });
+
+ describe('isEmailAddressValid', () => {
+ test('When username satisfies every rule, then it is valid', () => {
+ const username = 'jane.doe-99_x';
+
+ const result = isEmailAddressValid(username);
+
+ expect(result).toBe(true);
+ });
+
+ test('When username fails at least one rule, then it is invalid', () => {
+ const username = 'admin';
+
+ const result = isEmailAddressValid(username);
+
+ expect(result).toBe(false);
+ });
+
+ test('When username is empty, then it is invalid', () => {
+ const username = '';
+
+ const result = isEmailAddressValid(username);
+
+ expect(result).toBe(false);
+ });
+ });
+});
diff --git a/src/features/identity-setup/hooks/emailAddressRules.ts b/src/features/identity-setup/hooks/emailAddressRules.ts
new file mode 100644
index 0000000..460b512
--- /dev/null
+++ b/src/features/identity-setup/hooks/emailAddressRules.ts
@@ -0,0 +1,88 @@
+import type { TranslationKey } from '@/i18n/types';
+
+export const EMAIL_ADDRESS_MIN_LENGTH = 3;
+export const EMAIL_ADDRESS_MAX_LENGTH = 30;
+
+const ALLOWED_CHARS_REGEX = /^[a-z0-9._-]+$/;
+const EDGE_SPECIAL_CHARS_REGEX = /^[._-]|[._-]$/;
+const CONSECUTIVE_SPECIAL_CHARS_REGEX = /[._-]{2,}/;
+
+const RESERVED_USERNAMES = new Set([
+ 'admin',
+ 'administrator',
+ 'root',
+ 'support',
+ 'postmaster',
+ 'noreply',
+ 'no-reply',
+ 'webmaster',
+ 'hostmaster',
+ 'abuse',
+ 'security',
+ 'info',
+ 'help',
+ 'contact',
+ 'billing',
+ 'sales',
+ 'mailer-daemon',
+ 'daemon',
+ 'ftp',
+ 'www',
+ 'system',
+ 'test',
+]);
+
+export type EmailAddressRuleId = 'length' | 'charset' | 'edges' | 'available';
+export type EmailAddressRuleStatus = 'idle' | 'valid' | 'invalid';
+
+export interface EmailAddressRule {
+ id: EmailAddressRuleId;
+ labelKey: TranslationKey;
+ status: EmailAddressRuleStatus;
+}
+
+const isLengthValid = (username: string): boolean =>
+ username.length >= EMAIL_ADDRESS_MIN_LENGTH && username.length <= EMAIL_ADDRESS_MAX_LENGTH;
+
+const isCharsetValid = (username: string): boolean =>
+ ALLOWED_CHARS_REGEX.test(username) && !CONSECUTIVE_SPECIAL_CHARS_REGEX.test(username);
+
+const isEdgesValid = (username: string): boolean => !EDGE_SPECIAL_CHARS_REGEX.test(username);
+
+const isAvailable = (username: string): boolean => !RESERVED_USERNAMES.has(username);
+
+export const validateEmailAddress = (username: string): EmailAddressRule[] => {
+ const hasContent = username.length > 0;
+ const lengthValid = isLengthValid(username);
+ const charsetValid = isCharsetValid(username);
+ const edgesValid = hasContent && isEdgesValid(username);
+ const formatValid = lengthValid && charsetValid && edgesValid;
+
+ return [
+ {
+ id: 'length',
+ labelKey: 'identitySetup.updateEmail.rules.length',
+ status: hasContent ? (lengthValid ? 'valid' : 'invalid') : 'idle',
+ },
+ {
+ id: 'charset',
+ labelKey: 'identitySetup.updateEmail.rules.charset',
+ status: hasContent ? (charsetValid ? 'valid' : 'invalid') : 'idle',
+ },
+ {
+ id: 'edges',
+ labelKey: 'identitySetup.updateEmail.rules.edges',
+ status: hasContent ? (edgesValid ? 'valid' : 'invalid') : 'idle',
+ },
+ {
+ id: 'available',
+ labelKey: 'identitySetup.updateEmail.rules.available',
+ status: formatValid ? (isAvailable(username) ? 'valid' : 'invalid') : 'idle',
+ },
+ ];
+};
+
+export const isRuleStatusValid = (status: EmailAddressRuleStatus): boolean => status === 'valid';
+
+export const isEmailAddressValid = (username: string): boolean =>
+ validateEmailAddress(username).every((rule) => isRuleStatusValid(rule.status));
diff --git a/src/features/identity-setup/hooks/useEmailAddressValidation.test.ts b/src/features/identity-setup/hooks/useEmailAddressValidation.test.ts
new file mode 100644
index 0000000..6c21dc9
--- /dev/null
+++ b/src/features/identity-setup/hooks/useEmailAddressValidation.test.ts
@@ -0,0 +1,83 @@
+import { act, renderHook } from '@testing-library/react';
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
+import { useEmailAddressValidation } from './useEmailAddressValidation';
+
+describe('useEmailAddressValidation', () => {
+ beforeEach(() => {
+ vi.useFakeTimers();
+ });
+
+ afterEach(() => {
+ vi.useRealTimers();
+ });
+
+ test('When the hook is initialized, then username is empty and the user has not interacted yet', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ expect(result.current.username).toBe('');
+ expect(result.current.hasInteracted).toBe(false);
+ expect(result.current.isValid).toBe(false);
+ });
+
+ test('When validateAddress is called, then username updates immediately without waiting for the debounce', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ act(() => result.current.validateAddress('jane.doe'));
+
+ expect(result.current.username).toBe('jane.doe');
+ });
+
+ test('When validateAddress is called with uppercase characters, then the value is lowercased', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ act(() => result.current.validateAddress('Jane.Doe'));
+
+ expect(result.current.username).toBe('jane.doe');
+ });
+
+ test('When validateAddress is called, then hasInteracted becomes true immediately', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ act(() => result.current.validateAddress('j'));
+
+ expect(result.current.hasInteracted).toBe(true);
+ });
+
+ test('When validateAddress is called, then rules are not recomputed before the debounce delay elapses', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ act(() => result.current.validateAddress('jane.doe'));
+ act(() => vi.advanceTimersByTime(299));
+
+ expect(result.current.rules.find((rule) => rule.id === 'length')!.status).toBe('idle');
+ });
+
+ test('When the debounce delay elapses, then rules reflect the latest typed value', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ act(() => result.current.validateAddress('admin'));
+ act(() => vi.advanceTimersByTime(300));
+
+ expect(result.current.rules.find((rule) => rule.id === 'available')!.status).toBe('invalid');
+ });
+
+ test('When the typed value satisfies every rule and the debounce elapses, then isValid is true', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ act(() => result.current.validateAddress('jane.doe-99_x'));
+ act(() => vi.advanceTimersByTime(300));
+
+ expect(result.current.isValid).toBe(true);
+ });
+
+ test('When the value changes rapidly, then only the last value is validated after the delay', () => {
+ const { result } = renderHook(() => useEmailAddressValidation(300));
+
+ act(() => result.current.validateAddress('admin'));
+ act(() => vi.advanceTimersByTime(100));
+ act(() => result.current.validateAddress('jane.doe'));
+ act(() => vi.advanceTimersByTime(300));
+
+ expect(result.current.isValid).toBe(true);
+ });
+});
diff --git a/src/features/identity-setup/hooks/useEmailAddressValidation.ts b/src/features/identity-setup/hooks/useEmailAddressValidation.ts
new file mode 100644
index 0000000..402bb13
--- /dev/null
+++ b/src/features/identity-setup/hooks/useEmailAddressValidation.ts
@@ -0,0 +1,29 @@
+import { useCallback, useMemo, useState } from 'react';
+import { useDebounce } from '@/hooks/useDebounce';
+import { isRuleStatusValid, validateEmailAddress, type EmailAddressRule } from './emailAddressRules';
+
+const DEFAULT_DEBOUNCE_MS = 300;
+
+export interface UseEmailAddressValidationResult {
+ username: string;
+ rules: EmailAddressRule[];
+ isValid: boolean;
+ hasInteracted: boolean;
+ validateAddress: (value: string) => void;
+}
+
+export const useEmailAddressValidation = (debounceMs = DEFAULT_DEBOUNCE_MS): UseEmailAddressValidationResult => {
+ const [username, setUsername] = useState('');
+ const [hasInteracted, setHasInteracted] = useState(false);
+ const debouncedUsername = useDebounce(username, debounceMs);
+
+ const validateAddress = useCallback((value: string) => {
+ setHasInteracted(true);
+ setUsername(value.toLowerCase());
+ }, []);
+
+ const rules = useMemo(() => validateEmailAddress(debouncedUsername), [debouncedUsername]);
+ const isValid = useMemo(() => rules.every((rule) => isRuleStatusValid(rule.status)), [rules]);
+
+ return { username, rules, isValid, hasInteracted, validateAddress };
+};
diff --git a/src/features/identity-setup/hooks/useUpdateEmail.ts b/src/features/identity-setup/hooks/useUpdateEmail.ts
new file mode 100644
index 0000000..6d7608d
--- /dev/null
+++ b/src/features/identity-setup/hooks/useUpdateEmail.ts
@@ -0,0 +1,41 @@
+import type { EmailDomainsResponse } from '@internxt/sdk/dist/mail/types';
+import { useState } from 'react';
+import { useEmailAddressValidation } from './useEmailAddressValidation';
+
+interface UseUpdateEmailParams {
+ activeDomains: EmailDomainsResponse;
+ onNext: (params: { address: string; domain: string }) => void;
+}
+
+export const useUpdateEmail = ({ activeDomains, onNext }: UseUpdateEmailParams) => {
+ const { username, rules, isValid, validateAddress } = useEmailAddressValidation();
+ const [domain, setDomain] = useState
(activeDomains[0]?.domain ?? '');
+ const [isUsernameFocused, setIsUsernameFocused] = useState(false);
+
+ const submit = () => {
+ if (!isValid) return;
+ onNext({ address: username, domain });
+ };
+
+ const handleSubmit = (e: React.SubmitEvent) => {
+ e.preventDefault();
+ submit();
+ };
+
+ const hasStartedTyping = rules.some((rule) => rule.status !== 'idle');
+ const isPanelVisible = isUsernameFocused || hasStartedTyping;
+
+ return {
+ username,
+ rules,
+ isValid,
+ validateAddress,
+ domain,
+ setDomain,
+ isUsernameFocused,
+ setIsUsernameFocused,
+ isPanelVisible,
+ handleSubmit,
+ submit,
+ };
+};
diff --git a/src/features/identity-setup/index.tsx b/src/features/identity-setup/index.tsx
index 5734556..41565da 100644
--- a/src/features/identity-setup/index.tsx
+++ b/src/features/identity-setup/index.tsx
@@ -1,22 +1,22 @@
-import { Header } from './components/Header';
-import { Footer } from './components/Footer';
-import { use, useState, type ReactNode } from 'react';
-import { UpdateEmail } from './components/UpdateEmail';
-import { ConfirmPassword } from './components/ConfirmPassword';
-import { useAppSelector, useAppDispatch } from '@/store/hooks';
-import { NavigationService } from '@/services/navigation';
+import { DEFAULT_USER_NAME } from '@/constants';
+import { useTranslationContext } from '@/i18n';
import { AppView } from '@/routes/paths';
-import { ConfirmChange } from './components/ConfirmChange';
-import { MailService } from '@/services/sdk/mail';
-import { AuthService } from '@/services/sdk/auth';
+import { CryptoService } from '@/services/crypto';
import { ErrorService } from '@/services/error';
+import { LocalStorageService } from '@/services/local-storage';
+import { NavigationService } from '@/services/navigation';
+import { AuthService } from '@/services/sdk/auth';
+import { MailService } from '@/services/sdk/mail';
+import { mailApi } from '@/store/api/mail';
+import { useAppDispatch, useAppSelector } from '@/store/hooks';
import type { SetupMailAccountPayload } from '@internxt/sdk/dist/mail/types';
-import { CryptoService } from '@/services/crypto';
-import { useTranslationContext } from '@/i18n';
-import { DEFAULT_USER_NAME } from '@/constants';
import { createEncryptionAndRecoveryKeystores } from 'internxt-crypto';
-import { mailApi } from '@/store/api/mail';
-import { LocalStorageService } from '@/services/local-storage';
+import { use, useState, type ReactNode } from 'react';
+import { ConfirmChange } from './components/ConfirmChange';
+import { ConfirmPassword } from './components/ConfirmPassword';
+import { Footer } from './components/Footer';
+import { Header } from './components/Header';
+import { UpdateEmail } from './components/UpdateEmail';
type Step = 'updateEmail' | 'confirmPassword' | 'confirmChange';
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index e43973f..444386f 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -53,8 +53,14 @@
"Your previous email {{current_email}} will be saved for account recovery."
],
"mail": "Mail",
- "mailType": "You can use letters, numbers and periods",
- "action": "Save new email"
+ "mailType": "You can use lowercase letters, numbers, periods, hyphens and underscores",
+ "action": "Save new email",
+ "rules": {
+ "length": "3 to 30 characters",
+ "charset": "Lowercase letters, numbers, ., - and _",
+ "edges": "Start and end with a letter or number",
+ "available": "Username must be available"
+ }
},
"confirmPassword": {
"goBack": "Choose email",
@@ -152,7 +158,6 @@
"authTimeout": "Authentication timeout. Please try again."
},
"identitySetup": {
- "usernameRequired": "The username is required",
"passwordWrong": "The password is incorrect. Please try again.",
"emailNotSelected": "Please choose an email address first",
"passwordCheckFailed": "Something went wrong while verifying your password",
diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json
index 0e1b939..97d6fee 100644
--- a/src/i18n/locales/es.json
+++ b/src/i18n/locales/es.json
@@ -55,8 +55,14 @@
"Tu correo anterior {{current_email}} se guardará para la recuperación de cuenta."
],
"mail": "Correo",
- "mailType": "Puedes usar letras, números y puntos",
- "action": "Guardar nuevo correo"
+ "mailType": "Puedes usar minúsculas, números, puntos, guiones y guiones bajos",
+ "action": "Guardar nuevo correo",
+ "rules": {
+ "length": "Entre 3 y 30 caracteres",
+ "charset": "Minúsculas, números, ., - y _",
+ "edges": "Debe empezar y terminar con una letra o número",
+ "available": "El nombre de usuario debe estar disponible"
+ }
},
"confirmPassword": {
"goBack": "Elegir correo",
@@ -154,7 +160,6 @@
"authTimeout": "Tiempo de autenticación agotado. Por favor, inténtalo de nuevo."
},
"identitySetup": {
- "usernameRequired": "El nombre de usuario es obligatorio",
"passwordWrong": "La contraseña es incorrecta. Por favor, inténtalo de nuevo.",
"emailNotSelected": "Por favor, elige una dirección de correo primero",
"passwordCheckFailed": "Algo ha ido mal al verificar tu contraseña",
diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json
index 3f84fff..a219d82 100644
--- a/src/i18n/locales/fr.json
+++ b/src/i18n/locales/fr.json
@@ -55,8 +55,14 @@
"Votre e-mail précédent {{current_email}} sera conservé pour la récupération du compte."
],
"mail": "E-mail",
- "mailType": "Vous pouvez utiliser des lettres, des chiffres et des points",
- "action": "Enregistrer le nouvel e-mail"
+ "mailType": "Vous pouvez utiliser des minuscules, des chiffres, des points, des tirets et des underscores",
+ "action": "Enregistrer le nouvel e-mail",
+ "rules": {
+ "length": "Entre 3 et 30 caractères",
+ "charset": "Minuscules, chiffres, ., - et _",
+ "edges": "Doit commencer et se terminer par une lettre ou un chiffre",
+ "available": "Le nom d'utilisateur doit être disponible"
+ }
},
"confirmPassword": {
"goBack": "Choisir un e-mail",
@@ -154,7 +160,6 @@
"authTimeout": "Délai d'authentification dépassé. Veuillez réessayer."
},
"identitySetup": {
- "usernameRequired": "Le nom d'utilisateur est obligatoire",
"passwordWrong": "Le mot de passe est incorrect. Veuillez réessayer.",
"emailNotSelected": "Veuillez d'abord choisir une adresse e-mail",
"passwordCheckFailed": "Une erreur s'est produite lors de la vérification de votre mot de passe",
diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json
index e6ffd4a..984dbb5 100644
--- a/src/i18n/locales/it.json
+++ b/src/i18n/locales/it.json
@@ -55,8 +55,14 @@
"La tua email precedente {{current_email}} verrà salvata per il recupero dell'account."
],
"mail": "Email",
- "mailType": "Puoi usare lettere, numeri e punti",
- "action": "Salva nuova email"
+ "mailType": "Puoi usare minuscole, numeri, punti, trattini e underscore",
+ "action": "Salva nuova email",
+ "rules": {
+ "length": "Da 3 a 30 caratteri",
+ "charset": "Minuscole, numeri, ., - e _",
+ "edges": "Deve iniziare e finire con una lettera o un numero",
+ "available": "Il nome utente deve essere disponibile"
+ }
},
"confirmPassword": {
"goBack": "Scegli email",
@@ -154,7 +160,6 @@
"authTimeout": "Timeout di autenticazione. Riprova."
},
"identitySetup": {
- "usernameRequired": "Il nome utente è obbligatorio",
"passwordWrong": "La password non è corretta. Riprova.",
"emailNotSelected": "Per favore, scegli prima un indirizzo email",
"passwordCheckFailed": "Si è verificato un errore durante la verifica della password",