112 lines
3.2 KiB
JavaScript
112 lines
3.2 KiB
JavaScript
const debug = require('debug')('server-connect:secure');
|
|
const config = require('./config');
|
|
|
|
module.exports = function (app) {
|
|
const { randomBytes } = require('crypto');
|
|
const generateToken = (req, overwrite) => {
|
|
const sessionToken = req.session.csrfToken;
|
|
|
|
if (!overwrite && typeof sessionToken === 'string') {
|
|
return sessionToken;
|
|
}
|
|
|
|
const token = randomBytes(32).toString('hex');
|
|
req.session.csrfToken = token;
|
|
|
|
return token;
|
|
}
|
|
const getTokenFromRequest = (req) => req.headers['x-csrf-token'] || req.body.CSRFToken;
|
|
const isValidToken = (req) => {
|
|
const token = getTokenFromRequest(req);
|
|
const sessionToken = req.session.csrfToken;
|
|
return typeof token === 'string' && typeof sessionToken === 'string' && token === sessionToken;
|
|
}
|
|
|
|
app.get('/__csrf', (req, res) => {
|
|
res.json({ csrfToken: generateToken(req, true) });
|
|
});
|
|
|
|
app.use((req, res, next) => {
|
|
req.csrfToken = (overwrite) => generateToken(req, overwrite);
|
|
req.validateCSRF = () => isValidToken(req);
|
|
next();
|
|
});
|
|
|
|
debug('CSRF Protection initialized');
|
|
|
|
if (config.rateLimit?.enabled) {
|
|
const options = config.rateLimit;
|
|
const { RateLimiterMemory, RateLimiterRedis } = require('rate-limiter-flexible');
|
|
|
|
if (global.redisClient) {
|
|
app.rateLimiter = new RateLimiterRedis({
|
|
duration: options.duration,
|
|
points: options.points,
|
|
storeClient: global.redisClient,
|
|
});
|
|
if (options.private?.provider) {
|
|
app.privateRateLimiter = new RateLimiterRedis({
|
|
duration: options.private.duration,
|
|
points: options.private.points,
|
|
storeClient: global.redisClient,
|
|
});
|
|
}
|
|
} else {
|
|
app.rateLimiter = new RateLimiterMemory({
|
|
duration: config.rateLimit.duration,
|
|
points: config.rateLimit.points,
|
|
});
|
|
if (options.private?.provider) {
|
|
app.privateRateLimiter = new RateLimiterMemory({
|
|
duration: options.private.duration,
|
|
points: options.private.points,
|
|
});
|
|
}
|
|
}
|
|
|
|
debug('Ratelimit initialized', options);
|
|
}
|
|
|
|
if (config.passport) {
|
|
const passport = require('passport');
|
|
const ServerConnectStrategy = require('../auth/passport');
|
|
|
|
passport.use(new ServerConnectStrategy({ provider: 'security' }));
|
|
|
|
app.use(passport.initialize());
|
|
app.use(passport.session());
|
|
app.use(passport.authenticate('server-connect'));
|
|
|
|
debug('Passport initialized', passport.strategies);
|
|
|
|
app.use((req, res, next) => {
|
|
debug('auth', req.isAuthenticated());
|
|
debug('Session', req.session);
|
|
if (req.user) {
|
|
debug('User', req.user);
|
|
}
|
|
next();
|
|
});
|
|
|
|
app.use('/api/secure', restrict());
|
|
}
|
|
};
|
|
|
|
// restrict middleware
|
|
function restrict (options = {}) {
|
|
return async function (req, res, next) {
|
|
if (req.isAuthenticated()) {
|
|
return next();
|
|
}
|
|
|
|
if (req.is('json')) {
|
|
return res.status(401).json({ error: 'Unauthorized' });
|
|
}
|
|
|
|
if (options.redirect) {
|
|
return res.redirect(options.redirect);
|
|
}
|
|
|
|
res.status(401).send('Unauthorized');
|
|
};
|
|
} |