The DTP Sites web app development engine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

79 lines
2.0 KiB

// csrf-token.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0
'use strict';
const moment = require('moment');
const mongoose = require('mongoose');
const uuidv4 = require('uuid').v4;
const CsrfToken = mongoose.model('CsrfToken');
const { SiteService } = require('../../lib/site-lib');
class CsrfTokenService extends SiteService {
constructor (dtp) {
super(dtp, module.exports);
}
middleware (options) {
return async (req, res, next) => {
const requestToken = req.body[`csrf-token-${options.name}`];
if (!requestToken) {
return next(new Error('Must include valid CSRF token'));
}
const token = await CsrfToken.findOne({ token: requestToken });
if (!token) {
return next(new Error('CSRF request token is invalid'));
}
if (token.ip !== req.ip) {
return next(new Error('CSRF request token client mismatch'));
}
if (token.user) {
if (!req.user) {
return next(new Error('Must be logged in'));
}
if (!token.user.equals(req.user._id)) {
return next(new Error('CSRF request token user mismatch'));
}
}
this.log.info('claiming CSRF token', {
requestToken,
ip: req.ip,
});
await CsrfToken.updateOne(
{ _id: token._id },
{ $set: { claimed: new Date() } },
);
return next();
};
}
async create (req, options) {
options = Object.assign({
expiresMinutes: 30,
}, options);
const now = new Date();
let csrfToken = await CsrfToken.create({
created: now,
expires: moment(now).add(options.expiresMinutes, 'minute').toDate(),
user: req.user ? req.user._id : null,
ip: req.ip,
token: uuidv4(),
});
csrfToken = csrfToken.toObject();
csrfToken.name = `csrf-token-${options.name}`;
return csrfToken;
}
}
module.exports = {
slug: 'csrf-token',
name: 'csrfToken',
create: (dtp) => { return new CsrfTokenService(dtp); },
};