Authentication logic changed
JWT is now stored in cookie instead of localStorage changed APIs: -> /api/auth/login -> /api/auth/signup new APIs: -> /api/auth/logout
This commit is contained in:
+11
-7
@@ -5,6 +5,7 @@ const authRoutes = require("./routes/auth");
|
||||
const userRoutes = require("./routes/user");
|
||||
const roomRoutes = require("./routes/room");
|
||||
const mongoose = require("mongoose");
|
||||
const cookieParser = require("cookie-parser");
|
||||
require("dotenv").config();
|
||||
|
||||
mongoose
|
||||
@@ -17,14 +18,15 @@ const http = require("http");
|
||||
const server = http.createServer(app);
|
||||
const { socketIOServerInit } = require("./socket");
|
||||
|
||||
app.use(cors({ origin: "*" }));
|
||||
app.use(cors({ origin: "http://localhost:5173", credentials: true }));
|
||||
app.use(bodyParser.json());
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization");
|
||||
// res.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
|
||||
// res.setHeader("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE");
|
||||
// res.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization");
|
||||
next();
|
||||
});
|
||||
app.use(cookieParser());
|
||||
|
||||
socketIOServerInit(server);
|
||||
|
||||
@@ -40,9 +42,11 @@ app.use("/api/room", roomRoutes);
|
||||
|
||||
app.use((error, req, res, next) => {
|
||||
const status = error.status || 500;
|
||||
const message = error.message || "Something went wrong.";
|
||||
console.log(error)
|
||||
res.status(status).json({ message: message });
|
||||
console.log(error);
|
||||
res.status(status).json({
|
||||
userMessage: "Something went wrong",
|
||||
devMessage: error?.message || "Internal server error",
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(8080, () => {
|
||||
|
||||
Generated
+31
-1
@@ -12,6 +12,7 @@
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.20.2",
|
||||
"chess.js": "^1.0.0-beta.6",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
@@ -19,7 +20,8 @@
|
||||
"mongoose": "^7.2.1",
|
||||
"nodemailer": "^6.9.3",
|
||||
"socket.io": "^4.6.1",
|
||||
"uuid": "^9.0.0"
|
||||
"uuid": "^9.0.0",
|
||||
"zod": "^3.21.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
@@ -177,6 +179,26 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser": {
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
||||
"dependencies": {
|
||||
"cookie": "0.4.1",
|
||||
"cookie-signature": "1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser/node_modules/cookie": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
@@ -1225,6 +1247,14 @@
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.21.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
|
||||
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.20.2",
|
||||
"chess.js": "^1.0.0-beta.6",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
@@ -21,6 +22,7 @@
|
||||
"mongoose": "^7.2.1",
|
||||
"nodemailer": "^6.9.3",
|
||||
"socket.io": "^4.6.1",
|
||||
"uuid": "^9.0.0"
|
||||
"uuid": "^9.0.0",
|
||||
"zod": "^3.21.4"
|
||||
}
|
||||
}
|
||||
|
||||
+92
-63
@@ -1,93 +1,122 @@
|
||||
const express = require("express");
|
||||
const { createJSONToken, isValidPassword, validateJSONToken, generatePasswordHash } = require("../util/auth");
|
||||
const {
|
||||
createJSONToken,
|
||||
isValidPassword,
|
||||
validateJSONToken,
|
||||
generatePasswordHash,
|
||||
checkAuth,
|
||||
} = require("../util/auth");
|
||||
const { isValidEmail, isValidText } = require("../util/validation");
|
||||
const { User } = require("../models/user");
|
||||
const { z, ZodError } = require("zod");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const loginSchema = z
|
||||
.object({
|
||||
username: z
|
||||
.string()
|
||||
.min(5, { message: "Username should not be less than 5 characters" })
|
||||
.max(15, { message: "Username should not be more than 15 characters" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(8, { message: "Password should not be less than 8 characters" })
|
||||
.max(15, { message: "Password should not be more than 15 characters" }),
|
||||
})
|
||||
.required();
|
||||
|
||||
const signupSchema = z
|
||||
.object({
|
||||
username: z
|
||||
.string()
|
||||
.min(5, { message: "Username should not be less than 5 characters" })
|
||||
.max(15, { message: "Username should not be more than 15 characters" }),
|
||||
email: z.string().email({ message: "Please enter a valid email address" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(8, { message: "Password should not be less than 8 characters" })
|
||||
.max(15, { message: "Password should not be more than 15 characters" }),
|
||||
})
|
||||
.required();
|
||||
|
||||
router.post("/signup", async (req, res, next) => {
|
||||
console.log(req.body);
|
||||
const data = { email: req.body.email, password: req.body.password, username: req.body.username };
|
||||
let errors = {};
|
||||
|
||||
if (!isValidEmail(data.email)) {
|
||||
errors.email = "Invalid email.";
|
||||
} else {
|
||||
try {
|
||||
let user = await User.findOne({ email: data.email });
|
||||
if (user) {
|
||||
errors.email = "Email exists already.";
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidText(data.password, 6)) {
|
||||
errors.password = "Password must be at least 6 characters long.";
|
||||
}
|
||||
|
||||
let user = await User.findOne({ username: data.username });
|
||||
if (user) errors.username = "Username already taken";
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
console.log(errors);
|
||||
return res.status(409).json({
|
||||
success: false,
|
||||
message: "User signup failed due to validation errors.",
|
||||
errors,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const data = { email: req.body.email, password: req.body.password, username: req.body.username };
|
||||
|
||||
signupSchema.parse(data);
|
||||
|
||||
// check if username or email already exists
|
||||
if (await User.findOne({ username: data.username }))
|
||||
return res.status(409).json({ error: { username: "username already taken" } });
|
||||
if (await User.findOne({ email: data.email }))
|
||||
return res.status(409).json({ error: { email: "user with this email already exists" } });
|
||||
|
||||
let userData = {
|
||||
email: data.email,
|
||||
username: data.username,
|
||||
password_hash: await generatePasswordHash(data.password),
|
||||
};
|
||||
let userDoc = new User(userData);
|
||||
await userDoc.save();
|
||||
console.log(userDoc.id)
|
||||
let userDoc = await User.create(userData);
|
||||
const authToken = createJSONToken(userDoc.id);
|
||||
|
||||
const { id, username, email } = userDoc;
|
||||
res.status(201).json({
|
||||
res.status(201).cookie("auth-token", authToken, { httpOnly: true, sameSite: "strict" }).json({
|
||||
success: true,
|
||||
message: "User created.",
|
||||
user: { id, username, email },
|
||||
token: authToken,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof ZodError) {
|
||||
return res.status(400).json({ userMessage: "Invalid data submitted", devMessage: "Invalid schema" });
|
||||
}
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/login", async (req, res, next) => {
|
||||
try {
|
||||
let username = req.body.username,
|
||||
password = req.body.password;
|
||||
|
||||
loginSchema.parse({ username, password });
|
||||
|
||||
let user;
|
||||
user = await User.findOne({ username });
|
||||
if (!user)
|
||||
return res.status(404).json({
|
||||
userMessage: "User does not exist",
|
||||
devMessage: "'username' not found in db",
|
||||
});
|
||||
|
||||
const pwIsValid = await isValidPassword(password, user.password_hash);
|
||||
if (!pwIsValid) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
userMessage: "Invalid credentials",
|
||||
devMessage: "Invalid credentials",
|
||||
});
|
||||
}
|
||||
|
||||
const token = createJSONToken(user.id);
|
||||
res.cookie("auth-token", token, { httpOnly: true, sameSite: "strict" });
|
||||
return res
|
||||
.status(200)
|
||||
.json({ token, user: { id: user.id, username: user.username, email: user.email }, success: true });
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
return res.status(401).json({ userMessage: "Invalid Credentials", devMessage: "Invalid schema" });
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/login", async (req, res) => {
|
||||
const username = req.body.username;
|
||||
const password = req.body.password;
|
||||
|
||||
let user;
|
||||
router.delete("/logout", checkAuth, (req, res, next) => {
|
||||
try {
|
||||
user = await User.findOne({ username });
|
||||
if (!user) return res.status(401).json({ message: "User not found" });
|
||||
} catch (error) {
|
||||
return res.status(401).json({ success: false, message: "AutheFntication failed." });
|
||||
res.clearCookie("auth-token", { httpOnly: true, sameSite: "strict" });
|
||||
res.status(200).json({ success: true });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
const pwIsValid = await isValidPassword(password, user.password_hash);
|
||||
if (!pwIsValid) {
|
||||
return res.status(422).json({
|
||||
success: false,
|
||||
message: "Invalid credentials.",
|
||||
errors: { credentials: "Invalid email or password entered." },
|
||||
});
|
||||
}
|
||||
|
||||
const token = createJSONToken(user.id);
|
||||
console.log(username)
|
||||
return res
|
||||
.status(200)
|
||||
.json({ token, user: { id: user.id, username: user.username, email: user.email }, success: true });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
+29
-11
@@ -21,27 +21,45 @@ function isValidPassword(password, storedPassword) {
|
||||
return compare(password, storedPassword);
|
||||
}
|
||||
|
||||
// function checkAuthMiddleware(req, res, next) {
|
||||
// if (req.method === "OPTIONS") {
|
||||
// return next();
|
||||
// }
|
||||
// if (!req.headers.authorization) {
|
||||
// console.log("NOT AUTH. AUTH HEADER MISSING.");
|
||||
// return next(new NotAuthError("Not authenticated."));
|
||||
// }
|
||||
// const authFragments = req.headers.authorization.split(" ");
|
||||
|
||||
// if (authFragments.length !== 2) {
|
||||
// console.log("NOT AUTH. AUTH HEADER INVALID.");
|
||||
// return next(new NotAuthError("Not authenticated."));
|
||||
// }
|
||||
// const authToken = authFragments[1];
|
||||
// try {
|
||||
// const validatedToken = validateJSONToken(authToken);
|
||||
// req.userid = validatedToken;
|
||||
// } catch (error) {
|
||||
// console.log("NOT AUTH. TOKEN INVALID.");
|
||||
// return next(new NotAuthError("Not authenticated."));
|
||||
// }
|
||||
// next();
|
||||
// }
|
||||
|
||||
function checkAuthMiddleware(req, res, next) {
|
||||
if (req.method === "OPTIONS") {
|
||||
return next();
|
||||
}
|
||||
if (!req.headers.authorization) {
|
||||
console.log("NOT AUTH. AUTH HEADER MISSING.");
|
||||
return next(new NotAuthError("Not authenticated."));
|
||||
let authToken = req.cookies["auth-token"];
|
||||
if (!authToken) {
|
||||
return res.status(401).json({ userMessage: "Not authenticated", devMessage: "Auth token not found" });
|
||||
}
|
||||
const authFragments = req.headers.authorization.split(" ");
|
||||
|
||||
if (authFragments.length !== 2) {
|
||||
console.log("NOT AUTH. AUTH HEADER INVALID.");
|
||||
return next(new NotAuthError("Not authenticated."));
|
||||
}
|
||||
const authToken = authFragments[1];
|
||||
try {
|
||||
const validatedToken = validateJSONToken(authToken);
|
||||
req.userid = validatedToken;
|
||||
} catch (error) {
|
||||
console.log("NOT AUTH. TOKEN INVALID.");
|
||||
return next(new NotAuthError("Not authenticated."));
|
||||
return res.status(401).json({ userMessage: "Not authenticated", devMessage: "Invalid auth token" });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user