Skip to content

Commit

Permalink
Merge pull request #44 from Jiayan-Lim/user-ui
Browse files Browse the repository at this point in the history
User UI
  • Loading branch information
Jiayan-Lim authored Oct 18, 2024
2 parents 925621b + c9f2411 commit e0f29ef
Show file tree
Hide file tree
Showing 35 changed files with 3,339 additions and 362 deletions.
12 changes: 12 additions & 0 deletions backend/user-service/.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,17 @@ PORT=3001
# Will use cloud MongoDB Atlas database
ENV=PROD

# email details
EMAIL_USER=[email protected]
EMAIL_PASS=vhgj idnk fhme ooim

# backup email
BACKUP_EMAIL_USER=[email protected]
BACKUP_EMAIL_PASS=vvkj xhtv twsf roeh

# frontend host
FRONTEND_HOST=http://localhost
FRONTEND_PORT=3000

# Secret for creating JWT signature
JWT_SECRET=you-can-replace-this-with-your-own-secret
156 changes: 153 additions & 3 deletions backend/user-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,32 @@
|-----------------------------|-------------------------------------------------------|
| 201 (Created) | User created successfully, created user data returned |
| 400 (Bad Request) | Missing fields |
| 403 (Forbidden) | User hasn't verified their email |
| 409 (Conflict) | Duplicate username or email encountered |
| 500 (Internal Server Error) | Database or server error |

### Check User Exist by Email or Id

- This endpoint allows checking if a user exists in the database based on their email address.

- HTTP Method: `GET`

- Endpoint: http://localhost:3001/users/check

- Parameters
- Required: at least one of `email`, `id` path parameter
- Example: `http://localhost:3001/[email protected]`

- Responses:

| Response Code | Explanation |
|-----------------------------|----------------------------------------------------------|
| 200 (OK) | User found |
| 400 (Bad Request) | Bad request, parameter is missing. |
| 404 (Not Found) | User with the specified email not found |
| 500 (Internal Server Error) | Database or server error |


### Get User

- This endpoint allows retrieval of a single user's data from the database using the user's ID.
Expand Down Expand Up @@ -134,13 +157,14 @@
- Required: `userId` path parameter

- Body
- At least one of the following fields is required: `username` (string), `email` (string), `password` (string)
- At least one of the following fields is required: `username` (string), `email` (string), `password` (string), `isVerified` (boolean)

```json
{
"username": "SampleUserName",
"email": "[email protected]",
"password": "SecurePassword"
"password": "SecurePassword",
"isVerified": true,
}
```

Expand Down Expand Up @@ -253,6 +277,7 @@
| 200 (OK) | Login successful, JWT token and user data returned |
| 400 (Bad Request) | Missing fields |
| 401 (Unauthorized) | Incorrect email or password |
| 403 (Unauthorized) | User hasn't verified their email |
| 500 (Internal Server Error) | Database or server error |

### Verify Token
Expand All @@ -269,4 +294,129 @@
|-----------------------------|----------------------------------------------------|
| 200 (OK) | Token verified, authenticated user's data returned |
| 401 (Unauthorized) | Missing/invalid/expired JWT |
| 500 (Internal Server Error) | Database or server error |
| 500 (Internal Server Error) | Database or server error |

### Send Email

- This endpoint allows sending an email to the user.
- HTTP Method: `POST`
- Endpoint: http://localhost:3001/email//send-verification-email
- Body
- Required: `email` (string), `title` (string), `html` (string)

```json
{
"email": "[email protected]",
"title": "Confirm Your Email for PeerPrep",
"html": "<p>Click the link below to verify your email: </p>"
}
```

- Responses:

| Response Code | Explanation |
|-----------------------------|----------------------------------------------------|
| 200 (OK) | Verification email sent successfully. |
| 400 (Bad Request) | Missing or invalid fields (email, title, html). |
| 500 (Internal Server Error) | Database or server error |

### Send OTP Email

- This endpoint allows sending an email containing OTP to the user after they sign up.
- HTTP Method: `POST`
- Endpoint: http://localhost:3001/email//send-otp-email
- Body
- Required: `email` (string), `username` (string)

```json
{
"email": "[email protected]",
"password": "Confirm Your Email for PeerPrep",
"html": "<p>Click the link below to verify your email: </p>"
}
```

- Responses:

| Response Code | Explanation |
|-----------------------------|----------------------------------------------------|
| 200 (OK) | Verification email sent successfully. |
| 400 (Bad Request) | Missing or invalid fields (email, title, html). |
| 404 (Not Found) | User with specified email not found |
| 500 (Internal Server Error) | Database or server error |

### Send Verification Link Email

- This endpoint sends a verification email containing a verification link to the user after they sign up.
- HTTP Method: `POST`
- Endpoint: http://localhost:3001/email/send-verification-email
- Body
- Required: `email` (string), `username` (string), `id` (string), `type` (string)

```json
{
"email": "[email protected]",
"username": "us",
"id": "avid0ud9ay2189rgdbjvdak",
"type": "sign-up" // or "update"
}
```

- Responses:

| Response Code | Explanation |
|-----------------------------|----------------------------------------------------|
| 200 (OK) | Verification email sent successfully. |
| 400 (Bad Request) | Missing or invalid fields (email, title, html). |
| 500 (Internal Server Error) | Database or server error |

### Verify OTP

- This endpoint verifies the OTP (One-Time Password) sent to the user and returns a reset token upon success.
- HTTP Method: `POST`
- Endpoint: http://localhost:3001/auth/verif-otp
- Body
- Required: `email` (string), `otp` (string)

```json
{
"email": "[email protected]",
"otp": "123456"
}
```

- Responses:

| Response Code | Explanation |
|-----------------------------|-----------------------------------------------------------------|
| 200 (OK) | OTP verified successfully. Returns a reset token |
| 400 | Both email and otp are required fields. |
| 403 | No OTP request found for this user, or OTP expired or incorrect.|
| 404 | User with the provided email not found. |
| 500 | Database or server error |

### Reset Password

- This endpoint resets the user’s password if the provided reset token is valid.
- HTTP Method: `POST`
- Endpoint: http://localhost:3001/auth/verif-otp
- Body
- Required: `email` (string), `token` (string), `newPassword` (string)

```json
{
"email": "[email protected]",
"token": "reset_token_value",
"newPassword": "newpassword123"
}
```

- Responses:

| Response Code | Explanation |
|-----------------------------|---------------------------------------------------------------------------|
| 200 (OK) | OTP verified successfully. Returns a reset token |
| 400 | Missing required fields, token mismatch, expired token, or password reuse.|
| 403 | No OTP request found for this user, or OTP expired or incorrect. |
| 404 | User not found. |
| 500 | Database or server error |
98 changes: 96 additions & 2 deletions backend/user-service/controller/auth-controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import bcrypt from "bcrypt";
import crypto from 'crypto';
import jwt from "jsonwebtoken";
import { findUserByEmail as _findUserByEmail } from "../model/repository.js";
import { formatUserResponse } from "./user-controller.js";
Expand All @@ -9,12 +10,16 @@ export async function handleLogin(req, res) {
try {
const user = await _findUserByEmail(email);
if (!user) {
return res.status(401).json({ message: "Wrong email and/or password" });
return res.status(401).json({ message: "Wrong email" });
}

if (!user.isVerified) {
return res.status(403).json({ message: 'Please verify your email before logging in.' });
}

const match = await bcrypt.compare(password, user.password);
if (!match) {
return res.status(401).json({ message: "Wrong email and/or password" });
return res.status(401).json({ message: "Wrong password" });
}

const accessToken = jwt.sign({
Expand All @@ -39,3 +44,92 @@ export async function handleVerifyToken(req, res) {
return res.status(500).json({ message: err.message });
}
}

const generateResetToken = () => {
return crypto.randomBytes(64).toString('hex');
};

export async function verifOTP (req, res) {
try {
const { email, otp } = req.body;

if (!email || !otp) {
return res.status(400).json({ message: "Both 'email' and 'otp' are required." });
}

const user = await _findUserByEmail(email);

if (!user) {
return res.status(404).json({ message: `User with email '${email}' not found` });
}

if (!user.otp) {
return res.status(403).json({
message: 'No OTP request found for this user'
});
}

if (user.otp !== otp || new Date() > user.otpExpiresAt) {
return res.status(403).json({
message: new Date() > user.otpExpiresAt ? 'OTP has expired': 'Incorrect OTP provided'
});
}

user.resetToken = generateResetToken();
user.resetTokenExpiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15-minute expiration
await user.save()

return res.status(200).json({
message: 'OTP verified successfully',
data: {
token: user.resetToken,
}
});
} catch (error) {
console.error("error: ", error.message)
return res.status(500).json({ message: "Unknown error when verifying OTP!" });
}
};

export async function resetPassword(req, res) {
const { email, token, newPassword } = req.body;
try {
const user = await _findUserByEmail(email);

if (!user) {
return res.status(404).json({ message: 'User not found' });
}

if (!user.resetToken) {
return res.status(400).json({ message: 'No reset token for this user' });
}

if (user.resetToken !== token) {
return res.status(400).json({ message: 'Token not match' });
}

if (new Date() > user.resetTokenExpiresAt) {
return res.status(400).json({ message: 'Expired token' });
}

const isSamePassword = bcrypt.compareSync(newPassword, user.password);
if (isSamePassword) {
return res.status(400).json({ message: 'New password matches old password' });
}

// Hash the new password
const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(newPassword, salt);

user.password = hashedPassword;
user.otp = undefined; // Clear otp
user.otpExpiresAt = undefined;
user.resetToken = undefined; // Clear reset token
user.resetTokenExpiresAt = undefined; // Clear token expiration
await user.save();

res.status(200).json({ message: 'Password reset successfully' });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
Loading

0 comments on commit e0f29ef

Please sign in to comment.