This is my first time implementing CSRF and first time posting on Stack. I've struggled through the CSRF config, but finally got something that almost works.
If I open a bookmarked page in a fresh browser and submit a form, I'm seeing a 403 Invalid CSRF error: EBADCSRFTOKEN. In this case, the user auth is cookied so it does not challenge. I wonder if the session is expired? Subsequent posts work fine. Get requests are all fine. I'm stumped, time to put this challenge aside and ask for help as I've been at it for way too long, would appreciate any help.
Server.js does not reference csrf middleware but sets up the session
const express = require("express");
const path = require("path");
const favicon = require("serve-favicon");
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const flash = require("connect-flash");
const mongoose = require("mongoose");
const logger = require("morgan");
const expressSession = require("express-session");
const setCurrentUser = require("./app/controllers/setCurrentUser");
var session = {
secret: "XXXHIDDENXXX",
cookie: {},
resave: false,
saveUninitialized: false,
};
app.use(favicon(path.join(__dirname, "public/images", "favicon.png")));
app.use(logger("dev"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(flash());
app.use(expressSession(session));
app.use("/public", express.static("public"));
app.use(setCurrentUser);
app.use((req, res, next) => {
res.locals.user = req.user;
res.locals.success = req.flash("success");
res.locals.error = req.flash("error");
next();
});
Index.js
"use strict";
var express = require("express");
var router = express.Router();
const csrf = require("csurf");
const isLoggedIn = require("../controllers/auth");
var csrfProtection = csrf({ cookie: true });
router.use(csrf({ cookie: true }));
router.use((req, res, next) => {
// generate one CSRF token to every render page
res.locals.token = req.csrfToken();
next();
});
router.get("/settings", isLoggedIn, csrfProtection, function (req, res, next) {
...
});
router.post("/settings", isLoggedIn, csrfProtection, function (req, res, next) {
...
});
Interface, i'm using EJS, relevant code:
<head>
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="<%= locals.token %>">
</head>
<form method="POST" action="/settings">
<input type="hidden" name="_csrf" value="<%= locals.token %>">
<div class="form-group">
<label for="displayName">Name</label>
<input type="text" name="displayName" id="displayName" maxlength='30' data-parsley-maxlength='30' class="form-control" value="<%= user.displayName %>" required>
</div>
<button class="btn btn-primary">Submit</button>
</form>
The above code renders as expected:
<meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="qBdXd2ZQ-8vnbyw_IUivar_6UKp0qvhUf290">
<input type="hidden" name="_csrf" value="qBdXd2ZQ-8vnbyw_IUivar_6UKp0qvhUf290">
Token is sent via the form post in the post data as _csrf: qBdXd2ZQ-8vnbyw_IUivar_6UKp0qvhUf290
