Skip to content

Commit 136a08f

Browse files
committed
Require accounts to be verified before login
1 parent e88c6f8 commit 136a08f

File tree

5 files changed

+89
-18
lines changed

5 files changed

+89
-18
lines changed

backend/user-service/controller/auth-controller.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ export async function handleLogin(
1717
return res.status(401).json({ message: "Wrong email and/or password" });
1818
}
1919

20+
if (!user.isVerified) {
21+
return res.status(401).json({
22+
message: "User not verified. Please check your email for the link",
23+
});
24+
}
25+
2026
const match = await bcrypt.compare(password, user.password);
2127
if (!match) {
2228
return res.status(401).json({ message: "Wrong email and/or password" });

backend/user-service/controller/user-controller.ts

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,8 @@ export async function createUser(
8383
hashedPassword
8484
);
8585

86-
const emailToken = crypto.randomBytes(16).toString("hex");
87-
await redisClient.set(email, emailToken, { EX: 60 * 5 }); // expire in 5 minutes
88-
await sendAccVerificationMail(
89-
email,
90-
ACCOUNT_VERIFICATION_SUBJ,
91-
username,
92-
`http://localhost:3001/api/users/verify-email/${email}/${emailToken}`
93-
);
94-
9586
return res.status(201).json({
96-
message: `Created new user ${username} successfully`,
87+
message: `Created new user ${username} successfully.`,
9788
data: formatUserResponse(createdUser),
9889
});
9990
} else {
@@ -109,6 +100,38 @@ export async function createUser(
109100
}
110101
}
111102

103+
export const sendVerificationMail = async (
104+
req: Request,
105+
res: Response
106+
): Promise<Response> => {
107+
try {
108+
const { email } = req.body;
109+
const user = await _findUserByEmail(email);
110+
111+
if (!user) {
112+
return res.status(404).json({ message: `User ${email} not found` });
113+
}
114+
115+
const emailToken = crypto.randomBytes(16).toString("hex");
116+
await redisClient.set(email, emailToken, { EX: 60 * 5 }); // expire in 5 minutes
117+
await sendAccVerificationMail(
118+
email,
119+
ACCOUNT_VERIFICATION_SUBJ,
120+
user.username,
121+
`http://localhost:3001/api/users/verify-email/${email}/${emailToken}`
122+
);
123+
124+
return res
125+
.status(200)
126+
.json({ message: "Verification email sent. Please check your inbox." });
127+
} catch (error) {
128+
return res.status(500).json({
129+
message: "Unknown error when sending verification email!",
130+
error,
131+
});
132+
}
133+
};
134+
112135
export const verifyUser = async (
113136
req: Request,
114137
res: Response
@@ -137,10 +160,10 @@ export const verifyUser = async (
137160
return res
138161
.status(200)
139162
.json({ message: `User ${email} verified successfully.` });
140-
} catch {
163+
} catch (error) {
141164
return res
142165
.status(500)
143-
.json({ message: "Unknown error when verifying user!" });
166+
.json({ message: "Unknown error when verifying user!", error });
144167
}
145168
};
146169

backend/user-service/routes/user-routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
deleteUser,
77
getAllUsers,
88
getUser,
9+
sendVerificationMail,
910
updateUser,
1011
updateUserPrivilege,
1112
verifyUser,
@@ -31,6 +32,8 @@ router.post("/", createUser);
3132

3233
router.post("/images", createImageLink);
3334

35+
router.post("/send-verification-email", sendVerificationMail);
36+
3437
router.get("/:id", getUser);
3538

3639
router.get("/verify-email/:email/:token", verifyUser);

backend/user-service/swagger.yml

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ components:
7171
password:
7272
type: string
7373
required: true
74+
EmailVerification:
75+
properties:
76+
email:
77+
type: string
78+
required: true
7479
UserResponse:
7580
properties:
7681
message:
@@ -333,18 +338,51 @@ paths:
333338
application/json:
334339
schema:
335340
$ref: "#/components/schemas/ServerErrorResponse"
336-
/api/users/verify-email:
341+
/api/users/send-verification-email:
342+
post:
343+
summary: Send verification email
344+
tags:
345+
- users
346+
requestBody:
347+
content:
348+
application/json:
349+
schema:
350+
$ref: "#/components/schemas/EmailVerification"
351+
responses:
352+
200:
353+
description: Successful Response
354+
content:
355+
application/json:
356+
schema:
357+
type: object
358+
properties:
359+
message:
360+
type: string
361+
description: Message
362+
404:
363+
description: Not Found
364+
content:
365+
application/json:
366+
schema:
367+
$ref: "#/components/schemas/ErrorResponse"
368+
500:
369+
description: Internal Server Error
370+
content:
371+
application/json:
372+
schema:
373+
$ref: "#/components/schemas/ServerErrorResponse"
374+
/api/users/verify-email/{email}/{token}:
337375
get:
338376
summary: Verify email address
339377
tags:
340378
- users
341379
parameters:
342-
- in: query
380+
- in: path
343381
name: email
344382
required: true
345383
schema:
346384
type: string
347-
- in: query
385+
- in: path
348386
name: token
349387
required: true
350388
schema:

frontend/src/contexts/AuthContext.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ const AuthProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
7575
email: email,
7676
password: password,
7777
})
78-
.then(() => {
79-
login(email, password);
80-
toast.success(SUCCESSFUL_SIGNUP);
78+
.then(() => userClient.post("users/send-verification-email", { email }))
79+
.then((res) => {
80+
navigate("/login");
81+
toast.success(res.data.message);
8182
})
8283
.catch((err) => {
8384
setUser(null);

0 commit comments

Comments
 (0)