I am encountering a CSRF token error when submitting a form with file uploads using multer in an Express.js application. I am using enctype="multipart/form-data" to handle the file input, but the CSRF token validation fails upon form submission. I have already added csrf() middleware in my server.js, but I am still receiving the error.
I tried implementing multer with enctype="multipart/form-data" for handling file uploads in my Express.js application. I also added the CSRF protection middleware (csrf()) for security. I expected the form to submit without errors, including the CSRF token being correctly processed. However, upon submission, I receive the error "Invalid CSRF token."
I’ve verified that the CSRF token is being correctly passed in the form (<%= csrfToken %>), but the CSRF validation is still failing.
codes
const express = require("express");
const app = express();
const dotenv = require("dotenv");
const expressLayouts = require("express-ejs-layouts");
const postRoutes = require("./routes/postRoutes");
const authRoutes = require("./routes/authRoutes");
const session = require("express-session");
const { isAuthenticated } = require("./middlewares/auth.middleware");
const pgSession = require("connect-pg-simple")(session);
const csrf = require("csurf");
const flash = require("connect-flash");
const multer = require("multer");
const path = require("path");
dotenv.config(); // Load environment variables
const port = process.env.PORT || 5000;
// Sessions
app.use(
session({
store: new pgSession({
conObject: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
port: process.env.DB_PORT,
},
tableName: "sessions",
}),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
})
);
// Flash messages
app.use(flash());
// Middleware to parse form data
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// Set up csrf protection middleware
const csrfProtection = csrf();
app.use(csrfProtection);
// Set variables in locals
app.use((req, res, next) => {
res.locals.isAuthenticated = req.session.userId ? true : false;
res.locals.csrfToken = req.csrfToken();
res.locals.alerts = req.flash();
next();
});
app.use(express.static("public")); // Serve static files
app.set("view engine", "ejs"); // Use EJS for templating
app.use(expressLayouts); // Enable layouts
app.set("layout", "layout");
// Multer setup
const storage = multer.diskStorage({
destination: "./public/uploads/",
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
const upload = multer({
storage: storage,
limits: { fileSize: 10000000 },
fileFilter: function (req, file, cb) {
const filetypes = /jpeg|png|jpg|gif/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb("You can only upload image files");
}
},
});
// Add post route with file upload
app.post("/posts/add", upload.single("imageUrl"), async (req, res) => {
const { title, content } = req.body;
const userId = req.session.userId;
const imageUrl = req.file ? `/uploads/${req.file.filename}` : null;
try {
await Posts.create({ title, content, imageUrl, userId });
res.redirect("/posts/me");
} catch (err) {
console.error(err);
req.flash("error", "Error adding post");
req.flash("oldTitle", title);
req.flash("oldContent", content);
req.flash("oldImageUrl", imageUrl || null);
res.redirect("/posts/add");
}
});
// Routes
app.use("/posts", isAuthenticated, postRoutes);
app.use("/auth", authRoutes);
app.listen(port, () =>
console.log(`Server is running on http://localhost:${port}`)
);