11import * as http from "http"
2+ import * as limiter from "limiter"
23import * as querystring from "querystring"
34import { HttpCode , HttpError } from "../../common/http"
45import { AuthType , HttpProvider , HttpResponse , Route } from "../http"
@@ -48,6 +49,8 @@ export class LoginHttpProvider extends HttpProvider {
4849 return this . replaceTemplates ( route , response )
4950 }
5051
52+ private readonly limiter = new RateLimiter ( )
53+
5154 /**
5255 * Try logging in. On failure, show the login page with an error.
5356 */
@@ -59,6 +62,10 @@ export class LoginHttpProvider extends HttpProvider {
5962 }
6063
6164 try {
65+ if ( ! this . limiter . try ( ) ) {
66+ throw new Error ( "Login rate limited!" )
67+ }
68+
6269 const data = await this . getData ( request )
6370 const payload = data ? querystring . parse ( data ) : { }
6471 return await this . login ( payload , route , request )
@@ -108,3 +115,17 @@ export class LoginHttpProvider extends HttpProvider {
108115 throw new Error ( "Missing password" )
109116 }
110117}
118+
119+ // RateLimiter wraps around the limiter library for logins.
120+ // It allows 2 logins every minute and 12 logins every hour.
121+ class RateLimiter {
122+ private readonly minuteLimiter = new limiter . RateLimiter ( 2 , "minute" )
123+ private readonly hourLimiter = new limiter . RateLimiter ( 12 , "hour" )
124+
125+ public try ( ) : boolean {
126+ if ( this . minuteLimiter . tryRemoveTokens ( 1 ) ) {
127+ return true
128+ }
129+ return this . hourLimiter . tryRemoveTokens ( 1 )
130+ }
131+ }
0 commit comments