Skip to content

Commit 86d6329

Browse files
fix(auth): enforce zxcvbn strength check on password reset (#3721)
The reset handler was calling hashPassword directly without first calling checkPassword, allowing weak passwords through the token-based reset flow. Add checkPassword call before hash (mirroring updatePassword), and add an integration test asserting weak passwords are rejected with 422.
1 parent d42eb12 commit 86d6329

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

modules/auth/controllers/auth.password.controller.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@ const reset = async (req, res) => {
9393
try {
9494
user = await UserService.getBrut({ resetPasswordToken: token });
9595
if (!user || !user.email) return responses.error(res, 400, 'Bad Request', 'Password reset token is invalid or has expired.')();
96+
const checkedPassword = AuthService.checkPassword(req.body.newPassword);
9697
const edit = {
97-
password: await AuthService.hashPassword(req.body.newPassword),
98+
password: await AuthService.hashPassword(checkedPassword),
9899
resetPasswordToken: null,
99100
resetPasswordExpires: null,
100101
};

modules/auth/tests/auth.integration.tests.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,37 @@ describe('Auth integration tests:', () => {
12451245
}
12461246
});
12471247

1248+
test('should reject password reset with a weak password (zxcvbn strength gate)', async () => {
1249+
// Trigger forgot to generate a reset token (email send fails in test env, which is expected)
1250+
try {
1251+
await agent.post('/api/auth/forgot').send({ email: credentials[0].email }).expect(400);
1252+
} catch (err) {
1253+
console.log(err);
1254+
expect(err).toBeFalsy();
1255+
}
1256+
1257+
// Fetch the token directly via UserService
1258+
let resetToken;
1259+
try {
1260+
const userWithToken = await UserService.getBrut({ email: credentials[0].email });
1261+
resetToken = userWithToken.resetPasswordToken;
1262+
expect(resetToken).toBeDefined();
1263+
} catch (err) {
1264+
console.log(err);
1265+
expect(err).toBeFalsy();
1266+
}
1267+
1268+
// Attempt reset with a weak password — must be rejected with 422
1269+
try {
1270+
const result = await agent.post('/api/auth/reset').send({ token: resetToken, newPassword: 'password' }).expect(422);
1271+
expect(result.body.message).toBe('Unprocessable Entity');
1272+
expect(result.body.description).toBe('Password too weak.');
1273+
} catch (err) {
1274+
console.log(err);
1275+
expect(err).toBeFalsy();
1276+
}
1277+
});
1278+
12481279
test('should successfully reset password with a valid token', async () => {
12491280
// Trigger forgot to generate a reset token (email send fails in test env, which is expected)
12501281
try {

0 commit comments

Comments
 (0)