Merge branch 'master' of bitbucket.org:jubilian/fcsc_ipi_backend
This commit is contained in:
commit
c90809b6ce
@ -12,20 +12,6 @@ const Establishment = db.Establishment;
|
|||||||
//Admin user and Establishment user login
|
//Admin user and Establishment user login
|
||||||
exports.login = async (req, res) => {
|
exports.login = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Set security headers at the beginning
|
|
||||||
const isProd = process.env.NODE_ENV === "production";
|
|
||||||
|
|
||||||
// HSTS Header - Forces HTTPS for 1 year
|
|
||||||
if (isProd) {
|
|
||||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additional security headers
|
|
||||||
res.setHeader('X-Frame-Options', 'DENY');
|
|
||||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
||||||
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
||||||
res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
|
|
||||||
|
|
||||||
const { email, password } = req.body;
|
const { email, password } = req.body;
|
||||||
|
|
||||||
let userRole = null;
|
let userRole = null;
|
||||||
@ -45,48 +31,18 @@ exports.login = async (req, res) => {
|
|||||||
|
|
||||||
// No user found
|
// No user found
|
||||||
if (!userData) {
|
if (!userData) {
|
||||||
// Set headers even for error responses
|
return res.status(404).json({ status: "failed", message: "Invalid user", data: "" });
|
||||||
if (isProd) {
|
|
||||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
||||||
}
|
|
||||||
res.setHeader('X-Frame-Options', 'DENY');
|
|
||||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
||||||
|
|
||||||
return res.status(404).json({
|
|
||||||
status: "failed",
|
|
||||||
message: "Invalid user",
|
|
||||||
data: ""
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ACTIVE status
|
// Check ACTIVE status
|
||||||
if (!userData.is_active) {
|
if (!userData.is_active) {
|
||||||
if (isProd) {
|
return res.status(403).json({ status: "failed", message: "User account is inactive", data: "" });
|
||||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
||||||
}
|
|
||||||
res.setHeader('X-Frame-Options', 'DENY');
|
|
||||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
||||||
|
|
||||||
return res.status(403).json({
|
|
||||||
status: "failed",
|
|
||||||
message: "User account is inactive",
|
|
||||||
data: ""
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check password
|
// Check password
|
||||||
const validPass = await bcrypt.compare(password, userData.password);
|
const validPass = await bcrypt.compare(password, userData.password);
|
||||||
if (!validPass) {
|
if (!validPass) {
|
||||||
if (isProd) {
|
return res.status(401).json({ status: "failed", message: "Invalid password" });
|
||||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
||||||
}
|
|
||||||
res.setHeader('X-Frame-Options', 'DENY');
|
|
||||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
||||||
|
|
||||||
return res.status(401).json({
|
|
||||||
status: "failed",
|
|
||||||
message: "Invalid password"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare token data
|
// Prepare token data
|
||||||
@ -118,37 +74,27 @@ exports.login = async (req, res) => {
|
|||||||
expiresIn: "6h",
|
expiresIn: "6h",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set token in HTTP-only cookie with secure settings
|
// Set token in HTTP-only cookie (IMPORTANT PART)
|
||||||
|
const isProd = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
res.cookie("auth_token", token, {
|
res.cookie("auth_token", token, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: isProd,
|
secure: isProd, // only true in production (HTTPS)
|
||||||
sameSite: isProd ? "none" : "lax",
|
sameSite: isProd ? "none" : "lax", // 'none' requires HTTPS, so use 'lax' locally
|
||||||
maxAge: 6 * 60 * 60 * 1000,
|
maxAge: 6 * 60 * 60 * 1000, // 6 hours
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set security headers for success response
|
|
||||||
if (isProd) {
|
|
||||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
||||||
}
|
|
||||||
res.setHeader('X-Frame-Options', 'DENY');
|
|
||||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
||||||
|
|
||||||
// Return minimal user info (WITHOUT password)
|
// Optionally return minimal user info (WITHOUT password)
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "success",
|
status: "success",
|
||||||
message: "Login successful",
|
message: "Login successful",
|
||||||
data: tokenData,
|
data: tokenData,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// return res.status(200).json({ status: "success", message: "Login successful", data: token });
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Set security headers for error response
|
|
||||||
const isProd = process.env.NODE_ENV === "production";
|
|
||||||
if (isProd) {
|
|
||||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
||||||
}
|
|
||||||
res.setHeader('X-Frame-Options', 'DENY');
|
|
||||||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
||||||
|
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
status: "failed",
|
status: "failed",
|
||||||
message: err.message,
|
message: err.message,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user