mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
feat: Rate limiter on requests and login attempts.
This commit is contained in:
49
server/src/services/Authentication/RateLimiter.ts
Normal file
49
server/src/services/Authentication/RateLimiter.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user