Skip to content

Develop#210

Open
melarkkkk wants to merge 6 commits intomate-academy:masterfrom
melarkkkk:develop
Open

Develop#210
melarkkkk wants to merge 6 commits intomate-academy:masterfrom
melarkkkk:develop

Conversation

@melarkkkk
Copy link

No description provided.

Copy link

@mateacademy-ai-mentor mateacademy-ai-mentor left a 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 this submission as an educational override to help you progress, but there are several critical issues to address. You've built a great foundation with a well-organized project structure! However, your authentication middleware in src/middlewares/authMiddleware.js is incomplete—after verifying the token, you must fetch the user from the database and attach them to req.user. The logout functionality is also insecure; the route in src/routes/auth.route.js needs the authMiddleware and should use the POST method, not GET. Additionally, the User model is missing the resetToken and resetTokenExpire fields, which breaks the password reset flow. To fix the failed check run, simply rename .eslintrc.js to .eslintrc.cjs. Addressing these points will make your application much more robust and secure!


✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨

Footnotes

  1. Rate AI review example

user.activationToken = null;
await user.save();

return res.redirect(`${process.env.CLIENT_HOST}/profile`);

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 correctly redirect to the profile page after activation. However, the user is not authenticated at this point. This means the client-side application will likely redirect them to the login page immediately, which isn't a great user experience. Consider automatically logging the user in by generating and providing authentication tokens after a successful activation.

Comment on lines +156 to +169
const logout = async (req, res) => {
const { refreshToken } = req.cookies;
const user = await jwtService.verifyRefresh(refreshToken);

if (!user || !refreshToken) {
throw ApiError.unauthorized();
}

await tokenService.remove(user.id);

res.clearCookie('refreshToken');

return res.sendStatus(204);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task requires the logout functionality to be available only to authenticated users. While you check for a refresh token, this endpoint should be protected by the authMiddleware to ensure a valid access token is present, which is the standard way to protect routes. Additionally, logout is an action that changes the server's state (by invalidating a token), so it should be handled by a POST or DELETE request, not a GET request.

Comment on lines +191 to +211
const resetPassword = async (req, res) => {
const { token } = req.params;
const { password, confirm } = req.body;

if (password !== confirm) {
throw ApiError.badRequest('Passwords do not match');
}

const user = await User.findOne({ where: { resetToken: token } });

if (!user || Date.now() > user.resetTokenExpire) {
throw ApiError.badRequest('Invalid or expired token');
}

user.password = await bcrypt.hash(password, 10);
user.resetToken = null;
user.resetTokenExpire = null;
await user.save();

res.json({ message: 'Password updated' });
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new password provided during a password reset is not being validated against the rules defined in your validatePassword function. This could allow users to set a weak password, which contradicts the rules enforced during registration. You should add a validation step for the new password here.

Comment on lines +45 to +50
const changeEmail = async (req, res) => {
const { password, newEmail } = req.body;

if (!password || !newEmail) {
throw ApiError.badRequest('All fields are required');
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the requirements, the user must confirm their new email. Your current implementation validates the user's password and the presence of a new email, but it's missing a check to confirm the new email (e.g., by comparing newEmail with a confirmNewEmail field from the request body).


app.use(authRouter);
app.use('/users', userRouter);
app.use(profileRouter);

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 a good practice to group related routes under a common prefix. Mounting the profileRouter at the root (/) means your profile endpoints are available at paths like /, /name, and /password. Consider mounting it with a prefix, like app.use('/profile', profileRouter);, to make your API structure more organized and avoid potential conflicts.

Comment on lines +11 to +13
if (err) {
return res.status(500).json({ error: 'Server error' });
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch-all block incorrectly reports all non-ApiError types with a generic 500 status. This means your 404 "Not Found" error, which has a status of 404, will be incorrectly reported as a 500 server error. You should check if the err object has its own status property and use that instead of hardcoding 500.

return res.status(500).json({ error: 'Server error' });
}

next();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This next() call is unreachable. Error-handling middleware is only invoked when an error exists. If err is truthy, one of the return statements above will execute, preventing this line from being reached. This line can be safely removed.

router.get('/activate/:activationToken', catchError(authController.activate));
router.post('/login', catchError(authController.login));
router.get('/refresh', catchError(authController.refresh));
router.get('/logout', catchError(authController.logout));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the task requirements, the logout functionality should only be available to authenticated users. This route is missing the authMiddleware to protect it.

Additionally, logout is an action that modifies server state (invalidates a token), so it should use the POST or DELETE HTTP method, not GET.

Comment on lines +4 to +21
export const User = client.define('user', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
activationToken: {
type: DataTypes.STRING,
},
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your application's password reset functionality uses resetToken and resetTokenExpire fields. While Sequelize might save these dynamically, it's a best practice to explicitly define all model attributes in the schema. Please add resetToken (STRING) and resetTokenExpire (DATE or BIGINT) to this model to ensure data integrity and make the database schema clear.

Comment on lines +14 to +16
function normalize({ id, email }) {
return { id, email };
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The normalize function only returns the user's id and email. According to the requirements, the profile page should display the user's name. To support this, you should also include the name field in the object returned by this function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants