diff --git a/cmd/server/assets/login/login.html b/cmd/server/assets/login/login.html
index 94ea2f813..2e367cc72 100644
--- a/cmd/server/assets/login/login.html
+++ b/cmd/server/assets/login/login.html
@@ -138,6 +138,10 @@
flash.clear();
flash.error('Unsupported 2nd factor authentication type.');
}
+ } else if (error.code == 'auth/too-many-requests'){
+ flash.clear();
+ flash.error(err.message);
+ $submit.prop('disabled', false);
} else {
flash.clear();
flash.error("Sign-in failed. Please try again.");
diff --git a/internal/firebase/error.go b/internal/firebase/error.go
index b12495ffc..dcc4a425c 100644
--- a/internal/firebase/error.go
+++ b/internal/firebase/error.go
@@ -25,6 +25,7 @@ var (
ErrCredentialTooOld = &ErrorDetails{Err: "CREDENTIAL_TOO_OLD_LOGIN_AGAIN"}
ErrTokenExpired = &ErrorDetails{Err: "TOKEN_EXPIRED"}
ErrInvalidToken = &ErrorDetails{Err: "INVALID_ID_TOKEN"}
+ ErrTooManyAttempts = &ErrorDetails{Err: "TOO_MANY_ATTEMPTS_TRY_LATER"}
)
var _ error = (*ErrorDetails)(nil)
diff --git a/pkg/controller/login/reset_password.go b/pkg/controller/login/reset_password.go
index 9055eae98..c286435dc 100644
--- a/pkg/controller/login/reset_password.go
+++ b/pkg/controller/login/reset_password.go
@@ -57,6 +57,12 @@ func (c *Controller) HandleSubmitResetPassword() http.Handler {
}
if err := c.firebaseInternal.SendPasswordResetEmail(ctx, strings.TrimSpace(form.Email)); err != nil {
+ if errors.Is(err, firebase.ErrTooManyAttempts) {
+ flash.Error("Too many attempts have been made. Please wait and try again later.")
+ c.renderResetPassword(ctx, w, flash)
+ return
+ }
+
// Treat not-found like success so we don't leak details.
if !errors.Is(err, firebase.ErrEmailNotFound) {
flash.Error("Password reset failed.")