better username validator, server improvements,
This commit is contained in:
@@ -51,6 +51,7 @@ export default function Signup() {
|
||||
if (err.response) {
|
||||
setMessage('');
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
setMessage(err.response.data.message);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
16
server/.env
16
server/.env
@@ -1,21 +1,11 @@
|
||||
#SERVER_PORT=3000
|
||||
#JWT_SECRET=fdsfsdfds@
|
||||
#ORIGIN=http://localhost:5173
|
||||
#
|
||||
#PG_USER=postgres
|
||||
#PG_PASSWORD=haslo
|
||||
#PG_DATABASE=relay
|
||||
#PG_HOST=192.168.47
|
||||
#PG_PORT=5433
|
||||
|
||||
CLIENT_PORT=80
|
||||
#DO NOT CHANGE
|
||||
SERVER_PORT=3000
|
||||
|
||||
SERVER_PORT=3000 #DO NOT CHANGE
|
||||
JWT_SECRET=fdsfsdfds@
|
||||
ORIGIN=http://localhost:5173
|
||||
|
||||
PG_USER=postgres
|
||||
PG_PASSWORD=haslo
|
||||
PG_DATABASE=relay
|
||||
PG_HOST=192.168.0.47
|
||||
PG_PORT=5433
|
||||
PG_HOST=192.168.0.47
|
||||
@@ -1,6 +1,7 @@
|
||||
const jwt = require("jsonwebtoken");
|
||||
const jwtSecret = process.env.JWT_SECRET;
|
||||
const filter = require("../utils/filter");
|
||||
const { isValidUsername } = require("../utils/filter");
|
||||
|
||||
function generateJwtToken(username, user_id) {
|
||||
try {
|
||||
return jwt.sign({ username: username, user_id: user_id }, jwtSecret, {
|
||||
@@ -19,8 +20,12 @@ function verifyJwtToken(token) {
|
||||
if (!decoded?.user_id) {
|
||||
throw new Error("Token verification failed - missing user_id");
|
||||
}
|
||||
const username = filter(decoded.username);
|
||||
return { username: username, user_id: decoded.user_id };
|
||||
|
||||
if (!decoded?.username) {
|
||||
throw new Error("Token verification failed - missing username");
|
||||
}
|
||||
|
||||
return { username: decoded.username, user_id: decoded.user_id };
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
throw e;
|
||||
|
||||
@@ -6,7 +6,7 @@ const client = new Client({
|
||||
password: process.env.PG_PASSWORD,
|
||||
database: process.env.PG_DATABASE,
|
||||
host: process.env.PG_HOST,
|
||||
port: process.env.PG_PORT,
|
||||
port: 5433,
|
||||
});
|
||||
|
||||
client
|
||||
@@ -168,9 +168,10 @@ async function checkUserExist(username) {
|
||||
}
|
||||
|
||||
async function getPassword(username) {
|
||||
console.log(`Get password for: ${username}`);
|
||||
const query = `
|
||||
SELECT password FROM accounts
|
||||
WHERE username = $1;
|
||||
WHERE LOWER(username) = LOWER($1);
|
||||
`;
|
||||
try {
|
||||
const result = await client.query(query, [username]);
|
||||
|
||||
@@ -13,8 +13,6 @@ const io = new Server(server);
|
||||
require("dotenv").config();
|
||||
const PORT = process.env.SERVER_PORT;
|
||||
const {
|
||||
initializeDatabaseConnection,
|
||||
client,
|
||||
insertUser,
|
||||
insertMessage,
|
||||
checkUserExist,
|
||||
@@ -26,7 +24,7 @@ const {
|
||||
getMessages,
|
||||
} = require("./db/db.js");
|
||||
const authorizeUser = require("./utils/authorize");
|
||||
const filter = require("./utils/filter");
|
||||
const { isValidUsername } = require("./utils/filter");
|
||||
const { generateJwtToken, verifyJwtToken } = require("./auth/jwt");
|
||||
const { initializeSocket } = require("./socket/socket");
|
||||
const { getContacts, insertContact } = require("./db/db");
|
||||
@@ -45,18 +43,38 @@ app.use(cookieParser());
|
||||
|
||||
app.post("/api/auth/signup", async (req, res) => {
|
||||
try {
|
||||
const username = req.body.username.trim().replace(/[^a-zA-Z0-9]/g, "");
|
||||
const username = req.body.username;
|
||||
const password = req.body.password;
|
||||
console.log(username);
|
||||
// Validate form data length
|
||||
if (!username || username.length < 4 || username.length > 20) {
|
||||
return res.status(400).json({ message: "Invalid username length" });
|
||||
|
||||
if (!username) {
|
||||
return res.status(400).json({ message: "No username provided" });
|
||||
} else if (!password) {
|
||||
return res.status(400).json({ message: "No password provided" });
|
||||
}
|
||||
|
||||
if (typeof password && typeof username !== "string") {
|
||||
return res.status(400).json({ message: "Internal server error" });
|
||||
}
|
||||
|
||||
// Check for invalid characters in password
|
||||
const validChars = /^[A-Za-z0-9!@#$%^&*(),.?":{}|<>]+$/;
|
||||
if (!validChars.test(password)) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Username contains invalid character" });
|
||||
}
|
||||
|
||||
// Validate username for invalid characters, length, and type
|
||||
if (!isValidUsername(username)) {
|
||||
return res.status(400).json({ message: "Invalid username provided" });
|
||||
}
|
||||
|
||||
// Validate form data length
|
||||
if (!password || password.length < 8 || password.length > 128) {
|
||||
return res.status(400).json({ message: "Invalid password length" });
|
||||
}
|
||||
|
||||
// Checks if the user already exists in database (returns result.rows[0].count > 0;)
|
||||
// Checks if user already exist in database
|
||||
const exist = await checkUserExist(username);
|
||||
if (exist) {
|
||||
return res.status(409).json({ message: "User already exist" });
|
||||
@@ -65,9 +83,12 @@ app.post("/api/auth/signup", async (req, res) => {
|
||||
// Hash password and insert hash and username to database
|
||||
const hash = await bcrypt.hash(password, saltRounds);
|
||||
|
||||
// Insert username and password hash to database
|
||||
await insertUser(username, hash);
|
||||
const user_id = await getUserId(username);
|
||||
|
||||
// Get user id from database to store it in jwt token
|
||||
const user_id = await getUserId(username);
|
||||
console.log(`Registered: ${username} with id: ${user_id}`);
|
||||
// Set JWT token to cookies
|
||||
const token = generateJwtToken(username, user_id);
|
||||
res.cookie("token", token, {
|
||||
@@ -83,9 +104,13 @@ app.post("/api/auth/signup", async (req, res) => {
|
||||
|
||||
app.post("/api/auth/login", async (req, res) => {
|
||||
try {
|
||||
const username = req.body.username.trim().toLowerCase();
|
||||
const username = req.body.username?.trim().toLowerCase();
|
||||
const password = req.body.password;
|
||||
|
||||
if (!isValidUsername(username)) {
|
||||
return res.status(400).json({ message: "Invalid credentials" });
|
||||
}
|
||||
|
||||
if (
|
||||
!username ||
|
||||
!password ||
|
||||
@@ -140,7 +165,12 @@ app.delete("/api/chat/contacts/:contact", authorizeUser, async (req, res) => {
|
||||
.status(400)
|
||||
.json({ message: "Missing usernameContact parameter" });
|
||||
}
|
||||
const usernameContact = filter(req.params.contact);
|
||||
const usernameContact = req.params.contact;
|
||||
|
||||
// Validate username for invalid characters, length, and type
|
||||
if (!isValidUsername(usernameContact)) {
|
||||
return res.status(400).json({ message: "Invalid username provided" });
|
||||
}
|
||||
|
||||
await deleteContact(req.user.username, usernameContact);
|
||||
return res.status(200).json({ message: "Successfully deleted contact" });
|
||||
@@ -148,11 +178,15 @@ app.delete("/api/chat/contacts/:contact", authorizeUser, async (req, res) => {
|
||||
|
||||
app.put("/api/chat/contacts/:contact", authorizeUser, async (req, res) => {
|
||||
if (!req.params.contact) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Missing usernameContact parameter" });
|
||||
return res.status(400).json({ message: "Missing contact parameter" });
|
||||
}
|
||||
const usernameContact = filter(req.params.contact);
|
||||
const usernameContact = req.params.contact;
|
||||
|
||||
// Validate username for invalid characters, length, and type
|
||||
if (!isValidUsername(usernameContact)) {
|
||||
return res.status(400).json({ message: "Invalid contact provided" });
|
||||
}
|
||||
|
||||
const read = req.body.status;
|
||||
await updateContactStatus(req.user.username, usernameContact, read);
|
||||
|
||||
@@ -165,7 +199,13 @@ app.post("/api/chat/contact/:contact", authorizeUser, async (req, res) => {
|
||||
if (!req.params.contact) {
|
||||
return res.status(400).json({ message: "Missing contact parameter" });
|
||||
}
|
||||
const usernameContact = filter(req.params.contact);
|
||||
const usernameContact = req.params.contact;
|
||||
|
||||
// Validate username for invalid characters, length, and type
|
||||
if (!isValidUsername(usernameContact)) {
|
||||
return res.status(400).json({ message: "Invalid username provided" });
|
||||
}
|
||||
|
||||
await insertContact(req.user.username, usernameContact, true);
|
||||
|
||||
return res.status(200).json({ message: "Successfully inserted contact" });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { Server } = require("socket.io");
|
||||
const { insertMessage } = require("../db/db");
|
||||
const filter = require("../utils/filter");
|
||||
const { isValidUsername } = require("../utils/filter");
|
||||
const { verifyJwtToken } = require("../auth/jwt");
|
||||
const console = require("node:console");
|
||||
|
||||
@@ -19,7 +19,13 @@ function initializeSocket(io) {
|
||||
return next(new Error("(socket) Invalid token payload"));
|
||||
}
|
||||
|
||||
socket.username = filter(username);
|
||||
socket.username = username;
|
||||
|
||||
if (!isValidUsername(username)) {
|
||||
console.log("(socket) Invalid username");
|
||||
return;
|
||||
}
|
||||
|
||||
socket.user_id = user_id;
|
||||
console.log(
|
||||
`(socket) socket id: ${socket.id}, username: ${username}, user_id: ${user_id}`,
|
||||
@@ -32,7 +38,7 @@ function initializeSocket(io) {
|
||||
});
|
||||
|
||||
io.on("connection", async (socket) => {
|
||||
const username = filter(socket.username);
|
||||
const username = socket.username;
|
||||
if (!username) {
|
||||
socket.on("disconnect", () => {
|
||||
console.log(
|
||||
@@ -43,12 +49,21 @@ function initializeSocket(io) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isValidUsername(username)) {
|
||||
socket.on("disconnect", () => {
|
||||
console.log(
|
||||
"(socket)",
|
||||
socket.id,
|
||||
" disconnected due to: invalid username/token",
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
socket.join(username); // join username room
|
||||
|
||||
socket.on("chat message", async (msg) => {
|
||||
const { message } = msg;
|
||||
let { recipient } = msg;
|
||||
recipient = filter(recipient);
|
||||
const { message, recipient } = msg;
|
||||
const sender = username;
|
||||
if (!message || !recipient) {
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const { verifyJwtToken } = require("../auth/jwt");
|
||||
const { isValidUsername } = require("./filter");
|
||||
|
||||
function authorizeUser(req, res, next) {
|
||||
try {
|
||||
const token = req.cookies.token;
|
||||
@@ -11,6 +13,13 @@ function authorizeUser(req, res, next) {
|
||||
return res.status(401).json({ message: "Invalid token" });
|
||||
}
|
||||
|
||||
if (!isValidUsername(decoded.username)) {
|
||||
console.error("Invalid username on decoding JWT (that's weird)");
|
||||
return res.status(401).json({
|
||||
message: "Authorization failed, try to delete cookies and try again",
|
||||
});
|
||||
}
|
||||
|
||||
res.setHeader("X-Content-Type-Options", "nosniff");
|
||||
res.setHeader("X-Frame-Options", "DENY");
|
||||
res.setHeader("X-XSS-Protection", "1; mode=block");
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
function filter(text) {
|
||||
if (typeof text !== "string") {
|
||||
function isValidUsername(username) {
|
||||
if (typeof username !== "string") {
|
||||
return null;
|
||||
}
|
||||
if (text.length < 4 || text.length > 20) {
|
||||
|
||||
if (username.length < 4 || username.length > 20) {
|
||||
return null;
|
||||
}
|
||||
return text.replace(/[^a-zA-Z0-9]/g, "");
|
||||
|
||||
const validChars = /^[a-zA-Z0-9_]+$/;
|
||||
return validChars.test(username);
|
||||
}
|
||||
|
||||
module.exports = filter;
|
||||
module.exports = { isValidUsername };
|
||||
|
||||
Reference in New Issue
Block a user