Skip to content

Commit ca97b32

Browse files
author
Matheus Ishiyama
authored
Create user authenticate workflow (#2)
2 parents 86a6936 + 1e83026 commit ca97b32

File tree

12 files changed

+319
-16
lines changed

12 files changed

+319
-16
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"dotenv": "^8.2.0",
2929
"email-validator": "^2.0.4",
3030
"express": "^4.17.1",
31+
"jsonwebtoken": "^8.5.1",
3132
"mongoose": "^5.12.3",
3233
"nodemailer": "^6.5.0"
3334
},

src/app.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ app.use(express.urlencoded({ extended: true }));
77

88
//* routes
99
app.use("/user", require("./routes/user"));
10+
app.use("/auth", require("./routes/auth"));
1011

1112
module.exports = app;

src/middlewares/validateAuth.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const validateAuth = (req, res, next) => {
2+
if (!req.body.username)
3+
return res.status(400).json({ message: "No username provided" });
4+
if (!req.body.password)
5+
return res.status(400).json({ message: "No password provided" });
6+
7+
next();
8+
};
9+
10+
module.exports = validateAuth;

src/middlewares/validateCode.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module.exports = (req, res, next) => {
1+
const validateCode = (req, res, next) => {
22
if (!req.params.verifyCode)
33
return res.status(400).json({ message: "No verifyCode provided" });
44

@@ -7,3 +7,5 @@ module.exports = (req, res, next) => {
77

88
next();
99
};
10+
11+
module.exports = validateCode;

src/middlewares/validateRegister.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ const User = require("../models/user");
22
const EmailValidator = require("email-validator");
33
const validateBody = require("../services/validateBody");
44

5-
module.exports = async (req, res, next) => {
6-
if (!req.body) return res.status(400).json({ message: "No body provided" });
5+
const validateRegister = async (req, res, next) => {
76
const { username, name, email, password, checkPassword } = req.body;
87

98
//* validate body
@@ -38,7 +37,11 @@ module.exports = async (req, res, next) => {
3837
const emailExists = await User.findOne({ email });
3938

4039
if (userExists || emailExists)
41-
return res.status(400).json({ message: "Username or Email already exists"});
40+
return res
41+
.status(400)
42+
.json({ message: "Username or Email already exists" });
4243

4344
next();
4445
};
46+
47+
module.exports = validateRegister;

src/routes/auth.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const express = require("express");
2+
const authRoutes = express.Router();
3+
const authenticate = require("../services/authenticate");
4+
const validateAuth = require('../middlewares/validateAuth');
5+
6+
authRoutes.post("/", validateAuth, (req, res) => {
7+
const { username, password } = req.body;
8+
authenticate(username, password, res);
9+
});
10+
11+
module.exports = authRoutes;

src/services/authenticate.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const User = require("../models/user");
2+
const checkPassword = require("./password");
3+
const jwt = require("jsonwebtoken");
4+
require("dotenv").config();
5+
6+
const authenticate = async (username, password, res) => {
7+
if (!username)
8+
return res.status(400).json({ message: "No username provided" });
9+
10+
if (!password)
11+
return res.status(400).json({ message: "No password provided" });
12+
13+
const userExists = await User.findOne({ username });
14+
15+
if (userExists) {
16+
const isValid = checkPassword.validate(password, userExists.password);
17+
18+
if (isValid) {
19+
userExists.password = undefined;
20+
21+
if (!userExists.verified)
22+
return res.status(401).json({ message: "User unverified" });
23+
24+
const token = jwt.sign(userExists.username, process.env.JWT_SECRET);
25+
26+
return res.status(202).json({ token });
27+
}
28+
29+
return res
30+
.status(401)
31+
.json({ message: "Invalid username or password" });
32+
}
33+
return res.status(401).json({ message: "Invalid username or password" });
34+
};
35+
36+
module.exports = authenticate;

src/tests/integration/validateRegister.test.js renamed to src/tests/integration/validateRegister.spec.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,6 @@ describe("test validateRegister middleware", () => {
2626
console.log("[DEV_DATABASE] Connected");
2727
});
2828

29-
it("undefined body", async () => {
30-
const req = mockRequest();
31-
const res = mockResponse();
32-
33-
const response = await validateRegister(req, res);
34-
expect(response.status).toHaveBeenCalledWith(400);
35-
expect(response.json).toHaveBeenCalledWith({
36-
message: "No body provided",
37-
});
38-
});
39-
4029
describe('test body["username"]', () => {
4130
it("undefined username", async () => {
4231
const req = mockRequest({ email: "test" });

src/tests/units/authenticate.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const authenticate = require("../../services/authenticate");
2+
const password = require("../../services/password");
3+
const User = require("../../models/user");
4+
const mongoose = require("mongoose");
5+
require("dotenv").config();
6+
7+
const mockResponse = () => {
8+
const res = {};
9+
res.status = jest.fn().mockReturnValue(res);
10+
res.json = jest.fn().mockReturnValue(res);
11+
return res;
12+
};
13+
14+
beforeAll(async () => {
15+
await mongoose.disconnect();
16+
await mongoose.connect(process.env.MONGO_DEV, {
17+
useCreateIndex: true,
18+
useUnifiedTopology: true,
19+
useNewUrlParser: true,
20+
useFindAndModify: false,
21+
});
22+
23+
console.log("[DEV_DATABASE] Connected");
24+
});
25+
26+
afterAll(async () => {
27+
await User.findOneAndDelete({ username: "test_test" });
28+
await User.findOneAndDelete({ username: "testTesting" });
29+
await User.findOneAndDelete({ username: "test123test" });
30+
await mongoose.disconnect();
31+
console.log("[DEV_DATABASE] Disconnected");
32+
});
33+
34+
describe("test authenticate", () => {
35+
it("undefined username", async () => {
36+
const res = mockResponse();
37+
38+
const response = await authenticate(undefined, undefined, res);
39+
expect(response.status).toHaveBeenCalledWith(400);
40+
expect(response.json).toHaveBeenCalledWith({
41+
message: "No username provided",
42+
});
43+
});
44+
45+
it("undefined password", async () => {
46+
const res = mockResponse();
47+
48+
const response = await authenticate("test_test", undefined, res);
49+
expect(response.status).toHaveBeenCalledWith(400);
50+
expect(response.json).toHaveBeenCalledWith({
51+
message: "No password provided",
52+
});
53+
});
54+
55+
it("invalid username", async () => {
56+
const res = mockResponse();
57+
58+
const response = await authenticate("test_test", "testTEST12!@", res);
59+
expect(response.status).toHaveBeenCalledWith(401);
60+
expect(response.json).toHaveBeenCalledWith({
61+
message: "Invalid username or password",
62+
});
63+
});
64+
65+
it("invalid password", async () => {
66+
const res = mockResponse();
67+
const pwd = await password.hash("test");
68+
const user = new User({
69+
username: "test_test",
70+
name: "test_test",
71+
72+
verifyCode: "testtest",
73+
verified: false,
74+
password: pwd,
75+
createdAt: Date.now(),
76+
});
77+
await user.save();
78+
79+
const response = await authenticate("test_test", "testTEST12!@", res);
80+
expect(response.status).toHaveBeenCalledWith(401);
81+
expect(response.json).toHaveBeenCalledWith({
82+
message: "Invalid username or password",
83+
});
84+
});
85+
86+
it("valid username and password but not verified", async () => {
87+
const res = mockResponse();
88+
const pwd = await password.hash("test");
89+
const user = new User({
90+
username: "testTesting",
91+
name: "test_test",
92+
93+
verifyCode: "testtest",
94+
verified: false,
95+
password: pwd,
96+
createdAt: Date.now(),
97+
});
98+
await user.save();
99+
100+
const response = await authenticate("testTesting", "test", res);
101+
expect(response.status).toHaveBeenCalledWith(401);
102+
expect(response.json).toHaveBeenCalledWith({
103+
message: "User unverified",
104+
});
105+
});
106+
107+
it("valid username and password", async () => {
108+
const res = mockResponse();
109+
const pwd = await password.hash("test");
110+
const user = new User({
111+
username: "test123test",
112+
name: "test_test",
113+
114+
verifyCode: "verified",
115+
verified: true,
116+
password: pwd,
117+
createdAt: Date.now(),
118+
});
119+
await user.save();
120+
121+
const response = await authenticate("test123test", "test", res);
122+
expect(response.status).toHaveBeenCalledWith(202);
123+
expect(response.json).toHaveBeenCalled();
124+
});
125+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const validateAuth = require("../../middlewares/validateAuth");
2+
3+
const mockRequest = (content) => {
4+
return { body: content };
5+
};
6+
7+
const mockResponse = () => {
8+
const res = {};
9+
res.status = jest.fn().mockReturnValue(res);
10+
res.json = jest.fn().mockReturnValue(res);
11+
return res;
12+
};
13+
14+
describe("test validate auth", () => {
15+
it("undefined username", () => {
16+
const req = mockRequest({ username: undefined });
17+
const res = mockResponse();
18+
19+
const response = validateAuth(req, res);
20+
expect(response.status).toHaveBeenCalledWith(400);
21+
expect(response.json).toHaveBeenCalledWith({
22+
message: "No username provided",
23+
});
24+
});
25+
26+
it("undefined password", () => {
27+
const req = mockRequest({ username: "Test" });
28+
const res = mockResponse();
29+
30+
const response = validateAuth(req, res);
31+
expect(response.status).toHaveBeenCalledWith(400);
32+
expect(response.json).toHaveBeenCalledWith({
33+
message: "No password provided",
34+
});
35+
});
36+
37+
it("username and password provided", async () => {
38+
const req = mockRequest({ username: "Test", password: "test" });
39+
const res = mockResponse();
40+
const next = jest.fn();
41+
42+
await validateAuth(req, res, next);
43+
expect(next).toHaveBeenCalled();
44+
});
45+
});

0 commit comments

Comments
 (0)