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.
78 lines
2.0 KiB
78 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,
|
|
});
|
|
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); },
|
|
};
|