Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
098c996
added UI validation for existing email during user registration
Anxhul10 Feb 20, 2025
2401105
Add changeset for email validation fix
Anxhul10 Feb 20, 2025
4760feb
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 21, 2025
94aa790
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 21, 2025
0ca1a3e
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 24, 2025
0322f63
fixed lint errors
Anxhul10 Feb 24, 2025
5a7eda0
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 24, 2025
97bceb5
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 25, 2025
0173b0e
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 25, 2025
5155fe6
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 25, 2025
1766d36
Update users.ts
Anxhul10 Feb 25, 2025
4d92b92
Update RegisterForm.tsx
Anxhul10 Feb 25, 2025
29b97c3
Update RegisterForm.tsx
Anxhul10 Feb 25, 2025
1cf131e
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 25, 2025
69e19d4
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 26, 2025
15a3982
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 26, 2025
bfbc470
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 27, 2025
f2bf979
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Feb 28, 2025
cf4f398
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Mar 7, 2025
e169807
Merge branch 'RocketChat:develop' into fix/email-already-registered
Anxhul10 Mar 7, 2025
06b34a8
test: added e2e test for duplicate email validation in user registration
Anxhul10 Mar 13, 2025
12a8283
Merge branch 'RocketChat:develop' into fix/email-already-registered
Anxhul10 Mar 13, 2025
5748771
test:added api for signup, specific locator and separate test block
Anxhul10 Mar 13, 2025
e76f770
fixed lint errors
Anxhul10 Mar 13, 2025
22da7a3
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Mar 13, 2025
2b068b5
Merge branch 'develop' into fix/email-already-registered
Mar 18, 2025
87a987e
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Apr 9, 2025
dbb5436
Merge branch 'develop' into fix/email-already-registered
Anxhul10 Apr 9, 2025
f181ce4
Merge branch 'develop' into fix/email-already-registered
MartinSchoeler Apr 15, 2025
738cb9c
Merge branch 'develop' into fix/email-already-registered
dougfabris Jun 18, 2025
4274758
fix: error locators
dougfabris Jun 18, 2025
cb832c4
Merge branch 'develop' into fix/email-already-registered
kodiakhq[bot] Jun 18, 2025
398a7a9
Merge branch 'develop' into fix/email-already-registered
kodiakhq[bot] Jun 18, 2025
b13e3a8
Merge branch 'develop' into fix/email-already-registered
dougfabris Jun 20, 2025
dc912dd
Merge branch 'develop' into fix/email-already-registered
dougfabris Jun 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/open-toes-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/meteor': patch
'@rocket.chat/web-ui-registration': patch
---

Fixes email validation in the registration form to prevent users from registering with an already existing email address.
5 changes: 4 additions & 1 deletion apps/meteor/app/api/server/v1/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { executeSetUserActiveStatus } from '../../../../server/methods/setUserAc
import { getUserForCheck, emailCheck } from '../../../2fa/server/code';
import { resetTOTP } from '../../../2fa/server/functions/resetTOTP';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { checkEmailAvailability } from '../../../lib/server/functions/checkEmailAvailability';
import {
checkUsernameAvailability,
checkUsernameAvailabilityWithValidation,
Expand Down Expand Up @@ -663,7 +664,9 @@ API.v1.addRoute(
if (!(await checkUsernameAvailability(this.bodyParams.username))) {
return API.v1.failure('Username is already in use');
}

if (!(await checkEmailAvailability(this.bodyParams.email))) {
return API.v1.failure('Email already exists');
}
if (this.bodyParams.customFields) {
try {
await validateCustomFields(this.bodyParams.customFields);
Expand Down
57 changes: 47 additions & 10 deletions apps/meteor/tests/e2e/register.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { faker } from '@faker-js/faker';

import { Utils, Registration } from './page-objects';
import { request } from '../data/api-data';
import { test, expect } from './utils/test';

test.describe.parallel('register', () => {
Expand All @@ -12,7 +13,8 @@ test.describe.parallel('register', () => {
poRegistration = new Registration(page);
poUtils = new Utils(page);
});
test('Successfully Registration flow', async ({ page }) => {

test('should complete the registration flow', async ({ page }) => {
await test.step('expect trigger a validation error if no data is provided on register', async () => {
await page.goto('/home');
await poRegistration.goToRegister.click();
Expand Down Expand Up @@ -43,24 +45,27 @@ test.describe.parallel('register', () => {
});
});

test.describe('Registration without Account confirmation password set', async () => {
test.describe('should complete registration without account confirmation password set', async () => {
test.beforeEach(async ({ api }) => {
const result = await api.post('/settings/Accounts_RequirePasswordConfirmation', { value: false });

await expect(result.ok()).toBeTruthy();
});

test.beforeEach(async ({ page }) => {
await page.goto('/home');
await poRegistration.goToRegister.click();
});

test.afterEach(async ({ api }) => {
const result = await api.post('/settings/Accounts_RequirePasswordConfirmation', {
value: true,
});

await expect(result.ok()).toBeTruthy();
});

test('expect to register a user without password confirmation', async () => {
test('should register a user without password confirmation', async () => {
await test.step('expect to not have password confirmation field', async () => {
await expect(poRegistration.inputPasswordConfirm).toBeHidden();
});
Expand All @@ -77,12 +82,13 @@ test.describe.parallel('register', () => {
});
});

test.describe('Registration with manually confirmation enabled', async () => {
test.describe('should complete registration with manually confirmation enabled', async () => {
test.beforeEach(async ({ api }) => {
const result = await api.post('/settings/Accounts_ManuallyApproveNewUsers', { value: true });

await expect(result.ok()).toBeTruthy();
});

test.beforeEach(async ({ page }) => {
poRegistration = new Registration(page);

Expand All @@ -97,7 +103,7 @@ test.describe.parallel('register', () => {
await expect(result.ok()).toBeTruthy();
});

test('it should expect to have a textbox asking the reason for the registration', async () => {
test('should expect to have a textbox asking the reason for the registration', async () => {
await test.step('expect to have a textbox asking the reason for the registration', async () => {
await expect(poRegistration.inputReason).toBeVisible();
});
Expand All @@ -119,7 +125,7 @@ test.describe.parallel('register', () => {
await api.post('/settings/Accounts_RegistrationForm', { value: 'Public' });
});

test('It should expect a message warning that registration is disabled', async ({ page }) => {
test('should expect a message warning that registration is disabled', async ({ page }) => {
await page.goto('/home');
await poRegistration.goToRegister.click();
await expect(poRegistration.registrationDisabledCallout).toBeVisible();
Expand All @@ -136,6 +142,36 @@ test.describe.parallel('register', () => {
});
});

test.describe('Registration form validation', async () => {
test.beforeEach(async ({ page }) => {
poRegistration = new Registration(page);
poUtils = new Utils(page);
});

test('should not allow registration with an already registered email', async ({ page }) => {
const email = faker.internet.email();
await request.post('/api/v1/users.register').set('Content-Type', 'application/json').send({
name: faker.person.firstName(),
email,
username: faker.internet.userName(),
pass: 'any_password',
});

await test.step('Attempt registration with the same email', async () => {
await page.goto('/home');
await poRegistration.goToRegister.click();
await poRegistration.inputName.fill(faker.person.firstName());
await poRegistration.inputEmail.fill(email);
await poRegistration.username.fill(faker.internet.userName());
await poRegistration.inputPassword.fill('any_password');
await poRegistration.inputPasswordConfirm.fill('any_password');
await poRegistration.btnRegister.click();

await expect(page.getByRole('alert').filter({ hasText: 'Email already exists' })).toBeVisible();
});
});
});

test.describe('Registration for secret password', async () => {
test.beforeEach(async ({ api, page }) => {
poRegistration = new Registration(page);
Expand All @@ -150,7 +186,7 @@ test.describe.parallel('register', () => {
await expect(result.ok()).toBeTruthy();
});

test('It should expect a message warning that registration is disabled', async ({ page }) => {
test('should expect a message warning that registration is disabled', async ({ page }) => {
await page.goto('/home');
await poRegistration.goToRegister.click();
await expect(poRegistration.registrationDisabledCallout).toBeVisible();
Expand All @@ -160,14 +196,15 @@ test.describe.parallel('register', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/register/invalid_secret');
});
test('It should expect a invalid page informing that the secret password is invalid', async ({ page }) => {

test('should expect a invalid page informing that the secret password is invalid', async ({ page }) => {
await expect(page.locator('role=heading[level=2][name="The URL provided is invalid."]')).toBeVisible({
timeout: 10000,
});
});
});

test('It should register a user if the right secret password is provided', async ({ page }) => {
test('should register a user if the right secret password is provided', async ({ page }) => {
await page.goto('/register/secret');
await page.waitForSelector('role=form');
await poRegistration.inputName.fill(faker.person.firstName());
Expand All @@ -187,7 +224,7 @@ test.describe.parallel('register', () => {
await expect(result.ok()).toBeTruthy();
});

test('It should show an invalid page informing that the url is not valid', async ({ page }) => {
test('should show an invalid page informing that the url is not valid', async ({ page }) => {
await page.goto('/register/secret');
await page.waitForSelector('role=heading[level=2]');
await expect(page.locator('role=heading[level=2][name="The URL provided is invalid."]')).toBeVisible();
Expand Down
12 changes: 6 additions & 6 deletions packages/web-ui-registration/src/RegisterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export const RegisterForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRo
/>
</FieldRow>
{errors.name && (
<FieldError aria-live='assertive' id={`${nameId}-error`}>
<FieldError role='alert' id={`${nameId}-error`}>
{errors.name.message}
</FieldError>
)}
Expand All @@ -185,7 +185,7 @@ export const RegisterForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRo
/>
</FieldRow>
{errors.email && (
<FieldError aria-live='assertive' id={`${emailId}-error`}>
<FieldError role='alert' id={`${emailId}-error`}>
{errors.email.message}
</FieldError>
)}
Expand All @@ -208,7 +208,7 @@ export const RegisterForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRo
/>
</FieldRow>
{errors.username && (
<FieldError aria-live='assertive' id={`${usernameId}-error`}>
<FieldError role='alert' id={`${usernameId}-error`}>
{errors.username.message}
</FieldError>
)}
Expand All @@ -232,7 +232,7 @@ export const RegisterForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRo
/>
</FieldRow>
{errors?.password && (
<FieldError aria-live='assertive' id={`${passwordId}-error`}>
<FieldError role='alert' id={`${passwordId}-error`}>
{errors.password.message}
</FieldError>
)}
Expand Down Expand Up @@ -260,7 +260,7 @@ export const RegisterForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRo
/>
</FieldRow>
{errors.passwordConfirmation && (
<FieldError aria-live='assertive' id={`${passwordConfirmationId}-error`}>
<FieldError role='alert' id={`${passwordConfirmationId}-error`}>
{errors.passwordConfirmation.message}
</FieldError>
)}
Expand All @@ -284,7 +284,7 @@ export const RegisterForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRo
/>
</FieldRow>
{errors.reason && (
<FieldError aria-live='assertive' id={`${reasonId}-error`}>
<FieldError role='alert' id={`${reasonId}-error`}>
{errors.reason.message}
</FieldError>
)}
Expand Down
Loading