add task solution#288
Conversation
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
The implementation is incomplete due to missing guest/auth-check middleware on non-authenticated routes and lack of confirmPassword validation. Critical security issues exist that must be addressed before approval.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
| if (!userData) { | ||
| throw ApiError.unauthorized(); | ||
| } | ||
|
|
||
| const user = await userService.findByEmail(userData.email); | ||
|
|
||
| if (newPassword !== confirmPassword) { | ||
| throw ApiError.badRequest('password and confirmation fields must be equal'); | ||
| } |
There was a problem hiding this comment.
The password validation only checks for minimum 6 characters. The task mentions 'Inform the users about the rules for a password and check them' - consider defining and documenting the complete password requirements (uppercase, lowercase, numbers, special characters, etc.) if these are part of the application specification.
| const token = jwt.sign(user, process.env.JWT_KEY, { | ||
| expiresIn: '120s', | ||
| }); | ||
|
|
There was a problem hiding this comment.
The registration route lacks middleware to prevent already-authenticated users from registering. According to requirements, registration should be 'only non authenticated'.
| } | ||
|
|
||
| function verify(token) { | ||
| try { |
There was a problem hiding this comment.
The activation route lacks middleware to prevent already-authenticated users from accessing. Per requirements, activation should be 'only non authenticated'.
|
|
||
| function verify(token) { | ||
| try { | ||
| return jwt.verify(token, process.env.JWT_KEY); |
There was a problem hiding this comment.
The login route lacks middleware to prevent already-authenticated users from accessing. According to requirements, login should be 'only non authenticated'.
| return jwt.verify(token, process.env.JWT_KEY); | ||
| } catch (error) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
The sendResetMessage route lacks middleware to prevent already-authenticated users from accessing. According to requirements, password reset should be 'only non authenticated'.
|
|
||
| function signRefresh(user) { | ||
| const token = jwt.sign(user, process.env.JWT_REFRESH_KEY); | ||
|
|
There was a problem hiding this comment.
The resetPassword route lacks middleware to prevent already-authenticated users from accessing. According to requirements, password reset should be 'only non authenticated'.
| import { authController } from '../controllers/auth.controller.js'; | ||
| import { catchError } from '../utils/catchError.js'; | ||
| import { authMiddleware } from '../middlewares/authMiddleware.js'; | ||
|
|
There was a problem hiding this comment.
The access token expiration of 120s (2 minutes) is very short. This may cause frequent token refreshes in a real application. Consider increasing to a more practical duration (e.g., 15-60 minutes).
| pass: process.env.EMAIL_PASSWORD, | ||
| }, | ||
| }); | ||
|
|
||
| function send(email, subject, html) { | ||
| return transporter.sendMail({ | ||
| from: 'Auth API', | ||
| to: email, | ||
| subject, | ||
| html, | ||
| }); | ||
| } | ||
|
|
||
| function sendActivationEmail(email, token) { | ||
| const href = `${process.env.CLIENT_HOST}/activation/${token}`; |
There was a problem hiding this comment.
The auth.route.js imports authMiddleware but doesn't use it for routes that should be 'only non-authenticated'. Consider adding middleware to check if a user is NOT authenticated for registration, login, activation, and password reset routes. An alternative approach would be to add a 'guestMiddleware' that blocks authenticated users from accessing these endpoints.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
The provided response contains markdown formatting and text comments instead of valid JSON. The JSON must strictly adhere to the schema with only a 'decision' string (either 'APPROVE' or 'REQUEST_CHANGES') and a 'reason' string. Remove all markdown, comments, and extraneous text, and ensure the structure matches the expected schema exactly.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
| 'use strict'; | ||
|
|
||
| import express from 'express'; | ||
| import 'dotenv/config'; |
There was a problem hiding this comment.
Missing guestMiddleware on the registration route. According to the requirements, registration should be available only to non-authenticated users.
|
|
||
| const isPasswordValid = await bcrypt.compare(oldPassword, user.password); | ||
|
|
||
| if (!isPasswordValid) { | ||
| throw ApiError.unauthorized({ password: 'wrong password' }); | ||
| } | ||
|
|
||
| const errors = { | ||
| newPassword: validatePassword(newPassword), | ||
| }; | ||
|
|
||
| if (Object.values(errors).some((error) => error)) { | ||
| throw ApiError.badRequest('Validation error', errors); | ||
| } | ||
|
|
||
| const hashedPass = await bcrypt.hash(newPassword, 10); | ||
|
|
||
| await userService.changePassword(userData.email, hashedPass); | ||
|
|
||
| res.send({ |
There was a problem hiding this comment.
Registration endpoint is missing guestMiddleware. All other non-authenticated routes (login, activation, password reset) have this protection, but registration should also be restricted to non-authenticated users only.
| if (Object.values(errors).some((error) => error)) { | ||
| throw ApiError.badRequest('Validation error', errors); | ||
| } | ||
|
|
||
| if (oldEmail === newEmail) { | ||
| throw ApiError.badRequest('new email must be different'); | ||
| } | ||
|
|
||
| await userService.changeEmail(userData.email, newEmail); | ||
|
|
||
| res.send({ | ||
| message: 'OK', | ||
| }); | ||
| }; |
There was a problem hiding this comment.
The /refresh endpoint has guestMiddleware, but this could block valid refresh attempts if users include a Bearer token in the Authorization header. The refresh endpoint typically needs to handle both authenticated refreshes and guest token generation.
| message, | ||
| errors, |
There was a problem hiding this comment.
The registration route is missing guestMiddleware. According to the requirements, registration should be 'only non authenticated'. Other non-authenticated routes (login, refresh, password reset) correctly use guestMiddleware at lines 18, 23, 29, 35 - this route should follow the same pattern.
| const emailPattern = /^[\w.+-]+@([\w-]+\.){1,3}[\w-]{2,}$/; | ||
|
|
||
| if (!email) { | ||
| return 'Email is required'; |
There was a problem hiding this comment.
The registration route is missing guestMiddleware. According to requirements, registration should only be accessible to non-authenticated users. Add guestMiddleware to this route.
| allowNull: false, | ||
| unique: true, |
There was a problem hiding this comment.
Registration route is missing guestMiddleware. All other non-authenticated routes (activation, login, refresh, password reset) have this protection. The registration endpoint should also only be accessible to non-authenticated users.
| import { catchError } from '../utils/catchError.js'; | ||
|
|
||
| export const userRouter = express.Router(); | ||
|
|
There was a problem hiding this comment.
The /registration route is missing guestMiddleware. According to requirements, registration should be available 'only non authenticated' users. Other guest routes (login, activation, password reset) correctly use guestMiddleware, but this one doesn't.
|
|
||
| userRouter.get( | ||
| '/profile/email/change/activation/:emailChangeToken', |
There was a problem hiding this comment.
The email change activation endpoint is missing authMiddleware. Users should be authenticated to confirm email changes. Add authMiddleware to protect this route.
| auth: { | ||
| user: process.env.EMAIL, | ||
| pass: process.env.EMAIL_PASSWORD, | ||
| }, |
There was a problem hiding this comment.
Registration endpoint is missing guestMiddleware. According to requirements, registration should only be for non-authenticated users. Add guestMiddleware similar to login and other guest routes.
| <h1>Your email has been changed</h1> | ||
| `; | ||
|
|
There was a problem hiding this comment.
Email change activation endpoint is missing authMiddleware. Users should be authenticated to confirm email changes. This is a security issue since anyone with the token could change an account's email without being logged in.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
Educational Override Approval — This is an approval to help you continue learning while you address the identified issues.
Your implementation covers all core authentication requirements well: registration with email activation, login/logout flows, password reset, and profile management are all properly structured with good separation of concerns.
Critical fixes needed:
- The email change activation route (
user.route.jsline 30) usesauthMiddlewarebut should useguestMiddlewaresince users access this endpoint via email links without being authenticated — this currently makes the feature inaccessible. - Several files (
guestMiddleware.js,errorMiddleware.js,token.js) import modules without.jsextensions while other files in your project consistently use them, which can cause module resolution issues — make them consistent.
Minor improvements:
auth.controller.jsline ~18 uses'password'as error key but the parameter isnewPassword— align it with the pattern used inchangePasswordAuthenticated.jwt.service.jsfindByResetPasswordTokendoesn't validate token expiration — consider adding an expiry timestamp for security.- The
oldEmailparameter inuser.controller.jschangeEmail function is unused.
Keep up the good work on the overall architecture and structure! These fixes will polish the implementation nicely.
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Footnotes
|
|
||
| const errors = { |
There was a problem hiding this comment.
The error key is 'password' but the validated parameter is 'newPassword'. For consistency with other validation error responses (like changePasswordAuthenticated which correctly uses 'newPassword'), this should be newPassword: validatePassword(newPassword)
| const activate = async (req, res, next) => { | ||
| const { activationToken } = req.params; | ||
| const user = await userService.findByActivationToken(activationToken); | ||
|
|
||
| if (!user) { |
There was a problem hiding this comment.
The email change activation endpoint uses authMiddleware but should be accessible without authentication since the user confirms via the token in the email link. Consider using guestMiddleware instead, similar to the account activation endpoint.
| }); | ||
| }; | ||
|
|
||
| const changeEmail = async (req, res, next) => { |
There was a problem hiding this comment.
The oldEmail parameter from req.body is not used in this function. The authenticated user's email is obtained from userData.email on line 68. You can remove this unused parameter.
| import { ApiError } from '../exceptions/api.error'; | ||
| import { jwtService } from '../services/jwt.service'; |
There was a problem hiding this comment.
Inconsistent import syntax compared to other files. This file uses import ... from '../exceptions/api.error' without the .js extension, while other files in the project use .js extension (e.g., api.error.js). For consistency, consider using: import { ApiError } from '../exceptions/api.error.js'; and import { jwtService } from '../services/jwt.service.js';
| import { ApiError } from '../exceptions/api.error.js'; | ||
|
|
There was a problem hiding this comment.
Inconsistent import syntax: this file uses imports without .js extension (e.g., '../exceptions/api.error'), but other files in the project use .js extensions (e.g., '../exceptions/api.error.js'). For consistency and to avoid potential module resolution issues in ES modules, use the same format as the other files.
| import { DataTypes } from 'sequelize'; | ||
| import { sequelize } from '../utils/db.js'; |
There was a problem hiding this comment.
Import paths are missing .js extensions. Other files in the project consistently use extensions (e.g., api.error.js, jwt.service.js). This should be from '../exceptions/api.error.js' and from '../services/jwt.service.js'.
| const html = ` | ||
| <h1>Your email has been changed</h1> | ||
| `; | ||
|
|
There was a problem hiding this comment.
The email change activation endpoint uses authMiddleware, but users access this via a link in their email without being logged in. The token itself should be sufficient for verification. Consider using guestMiddleware instead, similar to how the account activation endpoint handles unauthenticated users.
| return token; | ||
| } |
There was a problem hiding this comment.
The password reset token lacks expiration validation. The findByResetPasswordToken function doesn't check if the token has expired, meaning old tokens would still work. Consider adding a timestamp field for resetPasswordTokenExpiry and validating it before allowing password change.
No description provided.