I do not understand why this is not working. I get this "ForbiddenError: invalid csrf token" message rendered when i click login, but I read that the {cookie: {httpOnly: true, secure: true}}
options must be enabled in the session in order to get a relatively secure app running (I use JWT and am a newbie in sessions and cookies)
Here are my files:
app.js
// ____ Imports ____
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const sessionConfig = require('./middleware/sessions');
const userProvider = require('./middleware/userProvider');
const connectDB = require('./services/db');
const csrf = require('csurf');
// ____ Middlewares ____
const app = express();
const csrfProtection = csrf();
app.set('view engine', 'ejs');
app.set('views', 'views');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(sessionConfig);
app.use(userProvider);
app.use(csrfProtection);
app.use((req, res, next) => {
res.locals.isAuth = req.session.isAuth;
res.locals.csrfToken = req.csrfToken();
next();
});
// ____ Routes ____
const adminRoutes = require('./routes/admin');
const shopRoutes = require('./routes/shop');
const authRoutes = require('./routes/auth');
const errorController = require('./controllers/error');
app.use('/admin', adminRoutes);
app.use(shopRoutes);
app.use(authRoutes);
app.use(errorController.get404);
// ____ Database & Server spin-up ____
const PORT = process.env.PORT || 3000; //deployment looks for process.env.PORT
connectDB(() => {
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
});
./middleware/sessions.js
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const config = require('config');
const store = new MongoDBStore({
uri: config.get('mongoURI'),
collection: 'sessions',
});
const sessionConfig = session({
secret: config.get('sessionSecret'),
resave: false,
saveUninitialized: false,
store: store,
cookie: { httpOnly: true, secure: true },
});
module.exports = sessionConfig;
./middleware/isAuth.js
module.exports = (req, res, next) => {
if (!req.session.isAuth) {
return res.redirect('/login');
}
next();
};
./middleware/userProvider.js
const User = require('../models/user');
module.exports = async (req, res, next) => {
try {
if (!req.session.user) {
return next();
}
let user = await User.findById(req.session.user._id);
req.user = user;
next();
} catch (error) {
console.log(error);
}
};
postLogin controller (passes through isAuth middleware)
exports.postLogin = async (req, res) => {
try {
let { email, password } = req.body;
let user = await User.findOne({ email: email });
let doMatch = await bcrypt.compare(password, user.password);
if (doMatch) {
console.log('Match!');
//use .save() method to make sure it redirects only when session is already created
req.session.user = user;
req.session.isAuth = true;
return req.session.save(() => {
res.redirect('/');
});
} else {
return res.redirect('/login');
}
} catch (error) {
console.log(error);
}
};
./views/auth/login.ejs
<%- include('../includes/head.ejs') %>
<link rel="stylesheet" href="/css/forms.css">
<link rel="stylesheet" href="/css/auth.css">
</head>
<body>
<%- include('../includes/navigation.ejs') %>
<main>
<form class="login-form" action="/login" method="POST">
<div class="form-control">
<label for="email">E-Mail</label>
<input type="email" name="email" id="email">
</div>
<div class="form-control">
<label for="password">Password</label>
<input type="password" name="password" id="password">
</div>
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button class="btn" type="submit">Login</button>
</form>
</main>
<%- include('../includes/end.ejs') %>
A session does get created in mongoDB, but no user or isAuth (shown in the document below as isLoggedin) is defined.