diff --git a/apps/meteor/lib/utils/addMinutesToADate.ts b/apps/meteor/lib/utils/addMinutesToADate.ts
index 6277b84ddca5f5db3f12bc65794bac9e3129bb86..f3b99c3e5b8b58a000bceb3be74e0813e372fbfb 100644
--- a/apps/meteor/lib/utils/addMinutesToADate.ts
+++ b/apps/meteor/lib/utils/addMinutesToADate.ts
@@ -1,5 +1,4 @@
 export const addMinutesToADate = (date: Date, minutes: number): Date => {
-	const copy = new Date(date);
-	copy.setMinutes(copy.getMinutes() + minutes);
-	return copy;
+	const minutesInMs = minutes * 60 * 1000;
+	return new Date(date.getTime() + minutesInMs);
 };
diff --git a/apps/meteor/tests/end-to-end/api/31-failed-login-attempts.ts b/apps/meteor/tests/end-to-end/api/31-failed-login-attempts.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3e6c664c8d8fc08c44ad423a9e2e3ad0e8074f3e
--- /dev/null
+++ b/apps/meteor/tests/end-to-end/api/31-failed-login-attempts.ts
@@ -0,0 +1,215 @@
+import { expect } from 'chai';
+import { after, before, beforeEach, afterEach, describe, it } from 'mocha';
+
+import { sleep } from '../../../lib/utils/sleep';
+import { getCredentials, api, request, credentials } from '../../data/api-data.js';
+import { updateSetting, updatePermission } from '../../data/permissions.helper';
+import { password } from '../../data/user';
+import { createUser, deleteUser } from '../../data/users.helper';
+
+describe('[Failed Login Attempts]', function () {
+	this.retries(0);
+
+	const maxAttemptsByUser = 3;
+	const maxAttemptsByIp = 6;
+	const userBlockSeconds = 3;
+	const ipBlockSeconds = 8;
+
+	before((done) => getCredentials(done));
+
+	before(async () => {
+		await updateSetting('Block_Multiple_Failed_Logins_Enabled', true);
+		await updateSetting('Block_Multiple_Failed_Logins_By_Ip', true);
+		await updateSetting('Block_Multiple_Failed_Logins_By_User', true);
+		await updateSetting('Block_Multiple_Failed_Logins_Attempts_Until_Block_by_User', maxAttemptsByUser);
+		await updateSetting('Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes', userBlockSeconds / 60);
+		await updateSetting('Block_Multiple_Failed_Logins_Attempts_Until_Block_By_Ip', maxAttemptsByIp);
+		await updateSetting('Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes', ipBlockSeconds / 60);
+
+		await updatePermission('logout-other-user', ['admin']);
+	});
+
+	after(async () => {
+		await updateSetting('Block_Multiple_Failed_Logins_Attempts_Until_Block_by_User', 10);
+		await updateSetting('Block_Multiple_Failed_Logins_Time_To_Unblock_By_User_In_Minutes', 5);
+		await updateSetting('Block_Multiple_Failed_Logins_Attempts_Until_Block_By_Ip', 50);
+		await updateSetting('Block_Multiple_Failed_Logins_Time_To_Unblock_By_Ip_In_Minutes', 5);
+		await updateSetting('Block_Multiple_Failed_Logins_Enabled', false);
+	});
+
+	async function shouldFailLoginWithUser(username: string, password: string) {
+		await request
+			.post(api('login'))
+			.send({
+				user: username,
+				password,
+			})
+			.expect('Content-Type', 'application/json')
+			.expect(401)
+			.expect((res) => {
+				expect(res.body).to.have.property('status', 'error');
+				expect(res.body).to.have.property('message', 'Incorrect password');
+			});
+	}
+
+	async function shouldSuccesfullyLoginWithUser(username: string, password: string) {
+		await request
+			.post(api('login'))
+			.send({
+				user: username,
+				password,
+			})
+			.expect('Content-Type', 'application/json')
+			.expect(200)
+			.expect((res) => {
+				expect(res.body).to.have.property('status', 'success');
+				expect(res.body).to.have.property('data').and.to.be.an('object');
+				expect(res.body.data).to.have.property('userId');
+				expect(res.body.data).to.have.property('authToken');
+			});
+	}
+
+	async function shouldLogoutUser(uid: string) {
+		await request.post(api('users.logout')).set(credentials).send({ userId: uid }).expect('Content-Type', 'application/json').expect(200);
+	}
+
+	async function shouldBlockLogin(username: string, password: string, reason: 'user' | 'ip') {
+		await request
+			.post(api('login'))
+			.send({
+				user: username,
+				password,
+			})
+			.expect('Content-Type', 'application/json')
+			.expect(401)
+			.expect((res) => {
+				expect(res.body).to.have.property('status', 'error');
+				expect(res.body).to.have.property('error', `error-login-blocked-for-${reason}`);
+			});
+	}
+
+	async function failMaxAttempts(username: string, password: string) {
+		const promises = [];
+		for (let i = 0; i < maxAttemptsByUser; i++) {
+			promises.push(shouldFailLoginWithUser(username, password));
+		}
+		await Promise.all(promises);
+	}
+
+	describe('[Block by User]', () => {
+		let user: Awaited<ReturnType<typeof createUser>> | undefined;
+
+		before(async () => {
+			await updateSetting('Block_Multiple_Failed_Logins_By_Ip', false);
+		});
+
+		after(async () => {
+			await updateSetting('Block_Multiple_Failed_Logins_By_Ip', true);
+		});
+
+		beforeEach(async () => {
+			user = await createUser();
+		});
+
+		afterEach(async () => {
+			await deleteUser(user);
+		});
+
+		it('should block by IP when the limit amount of failed attempts is reached', async () => {
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+
+			await shouldBlockLogin(user.username, password, 'user');
+		});
+
+		it('should unblock user after block time', async () => {
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+
+			await shouldBlockLogin(user.username, password, 'user');
+			await sleep(userBlockSeconds * 1000);
+			await shouldSuccesfullyLoginWithUser(user.username, password);
+		});
+
+		it('should reset counter of failed attempts after a successful login', async () => {
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+
+			await shouldBlockLogin(user.username, password, 'user');
+			await sleep(userBlockSeconds * 1000);
+
+			await shouldFailLoginWithUser(user.username, `${password}-incorrect`);
+			await shouldSuccesfullyLoginWithUser(user.username, password);
+			await shouldLogoutUser(user._id);
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+		});
+
+		it('should count failed attempts by user', async () => {
+			const newUser = await createUser();
+
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+			await shouldFailLoginWithUser(newUser.username, `${password}-incorrect`);
+			await shouldSuccesfullyLoginWithUser(newUser.username, password);
+
+			await deleteUser(newUser);
+		});
+	});
+
+	describe('[Block by IP]', () => {
+		let user: Awaited<ReturnType<typeof createUser>> | undefined;
+		let user2: Awaited<ReturnType<typeof createUser>> | undefined;
+		let userLogin: Awaited<ReturnType<typeof createUser>> | undefined;
+
+		beforeEach(async () => {
+			user = await createUser();
+			user2 = await createUser();
+			userLogin = await createUser();
+		});
+
+		afterEach(async () => {
+			await deleteUser(user);
+			await deleteUser(user2);
+			await deleteUser(userLogin);
+		});
+
+		afterEach(async () => {
+			// reset counter
+			await sleep(ipBlockSeconds * 1000);
+		});
+
+		it('should block by IP when trying to login with one user and the limit amount of failed attempts is reached', async () => {
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+			await sleep(userBlockSeconds * 1000);
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+
+			await shouldBlockLogin(user.username, password, 'ip');
+		});
+
+		it('should block by IP when trying to login with multiple users and the limit amount of failed attempts is reached', async () => {
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+			await failMaxAttempts(user2.username, `${password}-incorrect`);
+
+			await shouldBlockLogin(userLogin.username, password, 'ip');
+		});
+
+		it('should unblock IP after block time', async () => {
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+			await failMaxAttempts(user2.username, `${password}-incorrect`);
+
+			await shouldBlockLogin(userLogin.username, password, 'ip');
+			await sleep(ipBlockSeconds * 1000);
+			await shouldSuccesfullyLoginWithUser(userLogin.username, password);
+		}).timeout(20000);
+
+		it('should reset counter of failed attempts after a successful login', async () => {
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+			await failMaxAttempts(user2.username, `${password}-incorrect`);
+
+			await sleep(ipBlockSeconds * 1000);
+			await shouldFailLoginWithUser(userLogin.username, `${password}-incorrect`);
+			await shouldSuccesfullyLoginWithUser(userLogin.username, password);
+			await shouldLogoutUser(userLogin._id);
+
+			await failMaxAttempts(user.username, `${password}-incorrect`);
+			await failMaxAttempts(user2.username, `${password}-incorrect`);
+			await shouldBlockLogin(userLogin.username, password, 'ip');
+		}).timeout(20000);
+	});
+});