feat: Rate limiter on requests and login attempts.

This commit is contained in:
a.bouhuolia
2020-12-15 20:25:23 +02:00
parent 27483495cb
commit 6dd1229412
11 changed files with 170 additions and 4 deletions

View File

@@ -0,0 +1,49 @@
import { RateLimiterClusterMasterPM2, RateLimiterMemory, RateLimiterRedis, RateLimiterRes } from 'rate-limiter-flexible';
export default class RateLimiter {
rateLimiter: RateLimiterRedis;
/**
* Rate limiter redis constructor.
* @param {RateLimiterRedis} rateLimiter
*/
constructor(rateLimiter: RateLimiterMemory) {
this.rateLimiter = rateLimiter;
}
/**
*
* @return {boolean}
*/
public attempt(key: string, pointsToConsume = 1): Promise<RateLimiterRes> {
return this.rateLimiter.consume(key, pointsToConsume);
}
/**
* Increment the counter for a given key for a given decay time.
* @param {string} key -
*/
public hit(
key: string | number,
points: number,
secDuration: number,
): Promise<RateLimiterRes> {
return this.rateLimiter.penalty(key, points, secDuration);
}
/**
* Retrieve the rate limiter response of the given key.
* @param {string} key
*/
public get(key: string): Promise<RateLimiterRes> {
return this.rateLimiter.get(key);
}
/**
* Resets the rate limiter of the given key.
* @param key
*/
public reset(key: string): Promise<boolean> {
return this.rateLimiter.delete(key);
}
}

View File

@@ -50,18 +50,32 @@ export default class AuthenticationService implements IAuthenticationService {
* @param {string} password - Password.
* @return {Promise<{user: IUser, token: string}>}
*/
public async signIn(emailOrPhone: string, password: string): Promise<{user: ISystemUser, token: string, tenant: ITenant }> {
public async signIn(
emailOrPhone: string,
password: string
): Promise<{
user: ISystemUser,
token: string,
tenant: ITenant
}> {
this.logger.info('[login] Someone trying to login.', { emailOrPhone, password });
const { systemUserRepository } = this.sysRepositories;
const loginThrottler = Container.get('rateLimiter.login');
const user = await systemUserRepository.findByCrediential(emailOrPhone);
if (!user) {
await loginThrottler.hit(emailOrPhone);
this.logger.info('[login] invalid data');
throw new ServiceError('invalid_details');
}
this.logger.info('[login] check password validation.', { emailOrPhone, password });
if (!user.verifyPassword(password)) {
await loginThrottler.hit(emailOrPhone);
throw new ServiceError('invalid_password');
}
@@ -80,7 +94,7 @@ export default class AuthenticationService implements IAuthenticationService {
// Triggers `onLogin` event.
this.eventDispatcher.dispatch(events.auth.login, {
emailOrPhone, password,
emailOrPhone, password, user,
});
const tenant = await user.$relatedQuery('tenant');