Skip to content

Solution#250

Open
alyonashunevych wants to merge 2 commits intomate-academy:masterfrom
alyonashunevych:develop
Open

Solution#250
alyonashunevych wants to merge 2 commits intomate-academy:masterfrom
alyonashunevych:develop

Conversation

@alyonashunevych
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

This is a very solid and well-structured project, and you've successfully implemented most of the features. However, I'm requesting changes because there are a few critical issues that prevent the application from running and meeting key security requirements.

Here are the main points to address:

  1. The server will not start. Your nodemon.json and src/server/app.js are configured to run TypeScript files with the standard node runtime, which will cause a crash. You need to use a TypeScript runner like tsx or ts-node in your exec command.
  2. Insecure Profile Updates: The backend endpoint in src/server/controllers/users.controller.ts does not validate the user's current password when changing their password or email. This is a critical security vulnerability and a direct requirement of the task.
  3. Client Imports Server Code: The client-side component src/client/components/FormTemplate.tsx directly imports code from the src/server directory. Client and server code must be completely separate; this needs to be refactored.
  4. Missing Email Confirmation: The form for changing an email in src/client/components/ChangeEmailForm.tsx is missing the required confirmation field to prevent typos.

Please address these issues to make your application functional and secure.


✨ 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

  1. Rate AI review example

  2. Re-request AI review example

  3. Add AI review comments example

"watch": ["src"],
"ext": "ts,js,json",
"ignore": ["node_modules"],
"exec": "node src/server/app.js"

Choose a reason for hiding this comment

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

The exec command node src/server/app.js will cause an error because app.js imports .ts files directly. The standard node runtime cannot handle TypeScript modules. Consider using a tool like tsx or ts-node to run your server in development, which can execute TypeScript files on the fly. For example: "exec": "tsx src/server/app.js".

Comment on lines +96 to +116
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="sign-up" element={<SignUpPage />} />
<Route
path="auth/activation/:email/:activationToken"
element={<AccountActivationPage />}
/>
<Route
path="reset-password/:resetToken"
element={<ResetPassPage />}
/>
<Route path="reset-password" element={<ResetPassPage />} />
<Route path="login" element={<LoginPage />} />

<Route path="/" element={<RequireAuth />}>
<Route path="profile" element={<ProfilePage />} />
<Route
path="profile/change/:paramToChange"
element={<ChangePage />}
/>
</Route>

Choose a reason for hiding this comment

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

While the individual page components handle the redirection for authenticated users, a more consistent approach would be to use a layout route with the RequireNonAuth component to protect all non-authenticated routes. This would centralize your authorization logic and align with how you're using RequireAuth for protected routes.

Consider wrapping SignUpPage, AccountActivationPage, ResetPassPage, and LoginPage in a parent route that uses RequireNonAuth as its element.

Comment on lines +14 to +16
const fields = {
newEmail: { type: 'email', label: 'New email' },
} as Fields;

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, changing an email should include a confirmation step for the new email address. This form currently only has a single newEmail field. You should add a second field (e.g., confirmNewEmail) and add validation to ensure both email fields match before allowing the form to be submitted.

import React from 'react';
import cn from 'classnames';
import { Link, useNavigate } from 'react-router-dom';
import { userService } from '../../server/services/user.service.ts';

Choose a reason for hiding this comment

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

Client-side components should not import code directly from the server directory. This is a significant architectural issue that can lead to security vulnerabilities and incorrect application bundling. Validation logic, if needed on both client and server, should be placed in a shared directory that both can access, or the validation rules should be duplicated on the client side.


// add `Authorization` header to all requests
httpClient.interceptors.request.use((request) => {
const accessToken = localStorage.getItem('accessToken');

Choose a reason for hiding this comment

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

You have an accessTokenService that abstracts away the details of token storage. For better consistency and maintainability, it would be ideal to use accessTokenService.get() here instead of accessing localStorage directly.

Comment on lines +14 to +19
<Link to={`/profile/change/${item}`} key={item}>
<li className="button menu-item-box">
<p style={{ margin: 0 }}>Change {item}</p>
<i className="fa-sharp-duotone fa-solid fa-chevron-right"></i>
</li>
</Link>

Choose a reason for hiding this comment

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

For semantically correct HTML, the <Link> component should be nested inside the <li> element, not the other way around. A <ul> element's direct children should only be <li> elements. The correct structure would be <li><Link to={...}>...</Link></li>.

throw ApiError.badRequest('Email is already taken');
}

const activationToken = bcrypt.genSaltSync(1);

Choose a reason for hiding this comment

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

Using bcrypt.genSaltSync(1) is not a secure or appropriate method for generating an activation token. This function is designed to create salts for password hashing, not for generating unique, unpredictable tokens. You should use a cryptographically secure random string generator, like crypto.randomBytes(32).toString('hex'), which is already used correctly elsewhere in the application for registration.

Comment on lines +102 to +119
switch (type) {
case 'name':
dataToUpdate = await checkNewName(req, user);
break;

case 'password':
dataToUpdate = await checkNewPassword(req);
break;

case 'email':
await checkNewEmail(req, user);
res.sendStatus(204);

return;

default:
throw ApiError.badRequest('Invalid update type');
}

Choose a reason for hiding this comment

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

The task requires users to provide their current password when changing their password or email. While the client-side handles this in a separate step, this API endpoint does not enforce it. For security, the logic within the 'password' and 'email' cases should also require the user's current password in the request body and validate it before making any changes.

Comment on lines +72 to +93
const activate: RequestHandler = async (req, res) => {
const { token } = req.params;
const tokenString = Array.isArray(token) ? token[0] : token;

const user = await usersRepository.getByActivationToken(tokenString);

if (!user) {
throw ApiError.notFound();
}

const updatedUser = await usersRepository.update(user.id, {
email: user.pendingEmail ?? user.email,
pendingEmail: null,
activationToken: null,
});

await sendAuthentication(res, updatedUser);

if (user.pendingEmail) {
await mailer.sendEmailChangeNotification(user.email, user.pendingEmail);
}
};

Choose a reason for hiding this comment

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

The activate function should verify that the activation token belongs to the user with the email address provided in the URL. Currently, it only uses the token to find the user, ignoring the email parameter from the request. You should retrieve both email and token from req.params and ensure the user found by the token also has a matching email or pendingEmail.

Comment on lines +6 to +9
import { authRouter } from './routes/auth.router.ts';
import { errorMiddleware } from './middlewares/errorMiddleware.ts';
import { userRouter } from './routes/user.router.ts';
import { authMiddleware } from './middlewares/authMiddleware.ts';

Choose a reason for hiding this comment

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

This server file is a JavaScript file (.js), but it's importing TypeScript files (.ts) directly. The standard node runtime cannot execute TypeScript code. This will cause a runtime error.

To fix this, you should either:

  1. Rename this file to app.ts and use a TypeScript runner like tsx or ts-node to start your server.
  2. Compile your TypeScript files to JavaScript and import the compiled .js files instead.

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