Login rate limiting
parent
2fd23f4615
commit
1289b56b2b
@ -0,0 +1,59 @@
|
||||
const {RateLimiterPostgres, RateLimiterUnion} = require('rate-limiter-flexible');
|
||||
const pg = require('../../db/pg')
|
||||
|
||||
const globalOptions = {
|
||||
storeClient: pg,
|
||||
tableName: 'sos.rate_limits',
|
||||
tableCreated: true
|
||||
}
|
||||
|
||||
function createRateLimit(maxTimes, duration, prefix) {
|
||||
return new RateLimiterPostgres({
|
||||
...globalOptions,
|
||||
points: maxTimes,
|
||||
inmemoryBlockOnConsumed: maxTimes,
|
||||
keyPrefix: prefix,
|
||||
duration
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {};
|
||||
|
||||
const loginRateLimits = [
|
||||
// Once per ten seconds
|
||||
createRateLimit(1, 10, 'logon1'),
|
||||
// Ten tries in five minutes
|
||||
createRateLimit(10, 5 * 60, 'logon2'),
|
||||
// One hundred in a day
|
||||
createRateLimit(100, 24 * 60 * 60, 'logon3')
|
||||
]
|
||||
|
||||
const loginRateLimitPool = new RateLimiterUnion(...loginRateLimits)
|
||||
|
||||
module.exports.loginRateLimit = async (req, res) => {
|
||||
try {
|
||||
await loginRateLimitPool.consume(req.body.email)
|
||||
return 'next';
|
||||
} catch (limits) {
|
||||
const wait = Math.max(...Object.values(limits).map(limit => limit?.msBeforeNext || 1));
|
||||
|
||||
res.status(429)
|
||||
res.set({
|
||||
'Retry-After': Math.ceil(wait / 1000)
|
||||
})
|
||||
|
||||
res.json({errors: [{
|
||||
param: 'email',
|
||||
msg: `Too many password attempts, please wait ${Math.ceil(wait / 1000)} seconds`
|
||||
},{
|
||||
param: 'password',
|
||||
msg: ' '
|
||||
}]})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.loginRateLimit.reset = email => Promise.all(
|
||||
loginRateLimits.map(rateLimit =>
|
||||
rateLimit.delete(email)
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue