-
Notifications
You must be signed in to change notification settings - Fork 299
Develop #206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Develop #206
Conversation
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Summary
You've built a solid foundation for the authentication system with a well-organized structure. However, this requires changes because several core requirements are missing or incomplete, which prevents the project from being fully functional.
Here are the main issues to address:
- Incomplete Registration: The
namefield is missing from the user registration flow. Please add it to theUsermodel,auth.controller.js, anduser.services.js. - Missing Core Features: The entire Password Reset and Profile Management (updating name, email, password) functionalities are missing. You'll need to add the necessary routes, controller logic, and service methods for these.
- Login Flaw: The login controller in
auth.controller.jsallows users to log in without activating their accounts. You need to add a check to ensure only activated users can proceed. - Incomplete Auth Middleware: In
src/middlewares/authMiddlewares.js, after verifying the token, you must attach the decoded user data to the request object (e.g.,req.user = userData). This is crucial for protected routes. - No 404 Handler: The application is missing a catch-all route in
src/createServer.jsto handle requests to undefined paths and return a 404 status as required.
Addressing these points will bring the project in line with the requirements. Keep up the great work!
✨ 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
src/controllers/auth.controller.js
Outdated
| }; | ||
|
|
||
| const register = async (req, res, next) => { | ||
| const { email, password } = req.body; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirements state that registration should use name, email, and password. The name field is missing from the request body destructuring and subsequent logic.
src/controllers/auth.controller.js
Outdated
| return; | ||
| } | ||
| user.activationToken = null; | ||
| user.save(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
user.save() is an asynchronous operation. You should use await here to ensure the user's activation status is saved to the database before the server sends a response.
| } | ||
| user.activationToken = null; | ||
| user.save(); | ||
| res.send(user); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the requirements, you should redirect the user to the Profile page after successful activation. This implementation sends the user object as a JSON response instead.
| if (!user) { | ||
| throw ApiError.badRequest('No such user'); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirements specify that if a user is not active, you should ask them to activate their email. A check is missing here to verify if the user's account has been activated (i.e., user.activationToken is null) before proceeding with the login.
|
|
||
| await tokenService.remove(payload.user.id); | ||
| res.clearCookie('refreshToken', { httpOnly: true }); | ||
| res.sendStatus(204); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The task requires redirecting the user to the login page after logging out. Sending a 204 No Content status is a common practice for APIs, but it doesn't fulfill the specific requirement for a redirect.
src/services/mail.services.js
Outdated
| const html = `< h1 > ActivateAcount </h1> | ||
| <a href= '${href}'>${href} </a>`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTML in this template has some syntax errors. Tags should not have spaces inside them (e.g., it should be <h1> instead of < h1 >). Also, the anchor tag is missing a space before the href attribute.
src/services/mail.services.js
Outdated
| }); | ||
| } | ||
|
|
||
| export const emailServices = { send, sendActivationEmail }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This service is missing functions for other required email communications. According to the task description, you also need to send an email for password resets and a notification to the old email address when a user changes their email.
| function normalize({ id, email }) { | ||
| return { id, email }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The task requirements state that users register with a name. This function, which prepares user data for the client, should also include the name field once it's added to the user model.
src/services/user.services.js
Outdated
| async function register(email, password) { | ||
| const activationToken = uuidv4(); | ||
| const existUser = await findByEmail(email); | ||
|
|
||
| if (existUser) { | ||
| throw ApiError.badRequest('User already exist', { | ||
| email: 'User already exist', | ||
| }); | ||
| } | ||
|
|
||
| await User.create({ email, password, activationToken }); | ||
|
|
||
| await emailServices.sendActivationEmail(email, activationToken); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the task description, user registration must include a name, email, and password. This function is missing the name parameter and doesn't save it when creating the new user.
| export const userService = { | ||
| getAllActivated, | ||
| normalize, | ||
| findByEmail, | ||
| register, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This service is missing several key functions required by the task. You need to implement methods to handle changing a user's name, password, and email for the Profile page, as well as the entire password reset process.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Summary
Great job on the authentication system! You've successfully implemented all the major features, and the overall structure is solid. However, changes are needed to fix a critical issue with asynchronous operations. Several database calls are missing the await keyword, which causes functionality like password resets to fail because the server responds before the data is actually saved. For example, you need to await the user.save() and userService.confirmReset() calls in src/controllers/auth.controller.js, and the findUser.save() calls in src/services/user.services.js. Please address these to ensure the application behaves correctly.
✨ 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
| findUser.email = findUser.newEmail; | ||
| findUser.newEmail = null; | ||
| await findUser.save(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The activationToken should be cleared after it has been used to prevent it from being used again. Reusing the token could lead to unexpected errors, such as setting the user's email to null if the link is clicked a second time. Consider setting findUser.activationToken = null; before saving the user.
| }; | ||
| const validateName = (value) => { | ||
| if (!value) { | ||
| return 'Name is last'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error message appears to have a typo. It should probably be something like 'Name is required'.
src/controllers/auth.controller.js
Outdated
| } | ||
| user.activationToken = null; | ||
|
|
||
| user.save(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The user.save() method is asynchronous and returns a Promise. You need to await it to ensure the user's data is saved to the database before sending the response.
src/controllers/auth.controller.js
Outdated
| if (!isPasswordValid) { | ||
| throw ApiError.badRequest('Wrong password'); | ||
| } | ||
| generateTokens(res, user); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generateTokens is an async function. It should be awaited here. Without await, the login function will not wait for generateTokens to complete, and any errors thrown inside it will not be caught by the catchError middleware, leading to an unhandled promise rejection.
src/controllers/auth.controller.js
Outdated
| const confirm = async (req, res) => { | ||
| const { password, reppassword, token } = req.body; | ||
|
|
||
| userService.confirmReset(password, reppassword, token); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The userService.confirmReset function is async and must be awaited. Otherwise, the response will be sent before the password reset is confirmed in the database, and any potential errors will not be handled correctly.
src/services/user.services.js
Outdated
|
|
||
| async function confirmReset(password1, password2, resetToken) { | ||
| if (password1 !== password2 || !resetToken) { | ||
| throw ApiError.badRequest('Password do not mach'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a small typo in the error message. It should be "match" instead of "mach".
src/services/user.services.js
Outdated
|
|
||
| findUser.resetToken = null; | ||
| findUser.password = hashedPass; | ||
| findUser.save(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the other save() call, this is an asynchronous operation and must be awaited. Without await, the controller might send a success response before the user's password has been updated in the database.
src/services/mail.services.js
Outdated
|
|
||
| function sendActivationEmail(name, email, token) { | ||
| const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}`; | ||
| const html = `<h1> ActivateAcount from ${name} </h1> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a small typo in the heading. It should be "Activate Account".
src/services/mail.services.js
Outdated
| const html = `<h2> Reset email for ${email}</h2> | ||
| <h1> ON ${newEmail}<h1> | ||
| <a href=${href}> ${href} </a>`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This HTML is a bit confusing and contains a typo.
- The heading says "Reset email", but this email is for notifying about a change of email. A clearer message would be "Request to change your email address".
- The second
<h1>tag is not closed properly; it should be</h1>.
| const html = `<h2> your new email for login ${email}</h2> | ||
| <h1> ON ${email}<h1> | ||
| <a href=${href}> ${href} </a>`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the previous email template, this one has a small formatting issue. The second <h1> tag should be closed with </h1>.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Summary
I am approving your solution so you can move forward, but please take a moment to review the important feedback below. Great job fixing all the await issues from the last review—your code is much more robust now!
However, there are a couple of critical points to address:
- In
src/controllers/auth.controller.js, therefreshfunction is missing anawaitforgenerateTokens. This could lead to unhandled errors and crash the server. - The
activatefunction in the same file needs to be updated. It should log the user in by generating tokens to meet the redirect requirement and must avoid sending the entire user object in the response to prevent exposing sensitive data.
I've also left some minor suggestions in the code comments regarding error messages and unused route parameters. Keep up the great work, and pay close attention to these details in your future projects!
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Footnotes
| }; | ||
| const validateName = (value) => { | ||
| if (!value) { | ||
| return 'Name is last'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there's a small typo in this error message. It probably should be 'Name is required' to be consistent with the other validation messages.
| user.activationToken = null; | ||
|
|
||
| await user.save(); | ||
| res.send(user); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job implementing the activation logic. However, there are two points to address here to fully meet the requirements:
- The requirement is to "redirect to Profile after the activation". Since the profile page requires authentication, this endpoint should log the user in by generating and returning access tokens. You can call the
generateTokensfunction here. - The response currently sends the entire
userdatabase object, which can expose sensitive data like the password hash. It's best practice to send a normalized user object, similar to whatgenerateTokensdoes.
| throw ApiError.unauthorized(); | ||
| } | ||
|
|
||
| generateTokens(res, user); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generateTokens function is async and performs database operations. It's important to await its result here to ensure any potential errors are caught and handled correctly by your catchError middleware. Without await, an error inside generateTokens could lead to an unhandled promise rejection.
| res.statusCode = 500; | ||
|
|
||
| res.send({ | ||
| message: 'server errorooooooooooooooo', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this works, it's generally better to use a more professional and generic message for 500 errors, such as 'Internal Server Error'. This avoids confusion during debugging and maintains a professional appearance for the API.
| authRouter.post('/registration', catchError(authController.register)); | ||
|
|
||
| authRouter.get( | ||
| '/activation/:email/:activationToken', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This route includes an :email parameter, but the corresponding authController.activate function only uses the :activationToken. To keep the API design clean and simple, consider removing the unused :email parameter from both this route and the activation link sent in the email.
| if (!findUser) { | ||
| throw ApiError.badRequest('sorry'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To prevent attackers from discovering which emails are registered (an attack called 'email enumeration'), it's a security best practice to avoid throwing an error when a user is not found. You could restructure this to only perform the actions if the user exists, but the controller would always return a success-like message.
For example:
if (findUser) {
findUser.resetToken = resToken;
await findUser.save();
await emailServices.sendResetEmail(email, resToken);
}
// No 'else' block, so the function finishes without error.This way, the user interface can always show the "email sent" page, regardless of whether the email was in the database.
| const findUser = await User.findOne({ where: { resetToken } }); | ||
|
|
||
| if (!findUser) { | ||
| throw ApiError.badRequest('try again'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error message is not very descriptive for the user. A more helpful message would be something like 'Invalid or expired reset token.' This gives the user a better idea of what might be wrong.
| } | ||
|
|
||
| function sendActivationEmail(name, email, token) { | ||
| const href = `${process.env.CLIENT_HOST}/activate/${encodeURIComponent(email)}/${token}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted in the review for auth.route.js, the :email parameter in this activation URL is not actually used by the backend controller. To simplify the API, you could remove it from here and the corresponding route definition. This suggestion also applies to the other email links in this file.
| const href = `${process.env.CLIENT_HOST}/change/${encodeURIComponent(email)}/${token}`; | ||
|
|
||
| const html = `<h2> change of email ${email}</h2> | ||
| <h1> ON ${newEmail}<h1> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there's a small typo in this HTML tag. The closing tag for the second <h1> is missing a forward slash (/). It should be </h1>.
No description provided.