[Client] Report password-reset rate limits instead of a generic failure (#7677)

* [Client] Report password-reset rate limits instead of a generic failure

sigForgotPasswordError carried no response code, so rate limiting, a
disabled reset feature, an unknown user and a bad token all produced
"contact the server operator". Users who were only rate limited retried,
which kept them rate limited.

* [Client] Move the reset-password warning out of the response switch

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-10-28 09:19:42 +01:00 • committed by GitHub
parent a83d82b5e8
commit d89b502342
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 36 additions and 12 deletions

View file

@ -517,14 +517,38 @@ void ConnectionController::onForgotPasswordSuccess()
SettingsCache::instance().servers().setFPPlayerName("");
}
void ConnectionController::onForgotPasswordError()
void ConnectionController::onForgotPasswordError(int responseCode)
{
QMessageBox::warning(
dialogParent, tr("Reset Password"),
tr("Failed to reset user account password, please contact the server operator to reset your password."));
SettingsCache::instance().servers().setFPHostName("");
SettingsCache::instance().servers().setFPPort("");
SettingsCache::instance().servers().setFPPlayerName("");
// Rate limiting is retryable, so it must not clear the prefill.
bool keepPrefill = false;
QString message;
switch (static_cast<Response::ResponseCode>(responseCode)) {
case Response::RespTooManyRequests:
// Retryable: keep the host/port/user prefill so the user can try again. The window
// is server-configured (an hour by default), so do not promise a shorter wait.
keepPrefill = true;
message = tr("Too many password reset attempts from your address. Please wait before "
"requesting a new reset.");
break;
case Response::RespFunctionNotAllowed:
// Also returned for an unknown user, a banned user, and an expired token or
// challenge, so the message must not claim a single cause.
message = tr("The server did not accept the reset request. Check the user name, or contact "
"the server operator to reset your password.");
break;
default:
message = tr("Failed to reset user account password, please contact the server operator to reset your "
"password.");
break;
}
QMessageBox::warning(dialogParent, tr("Reset Password"), message);
if (!keepPrefill) {
SettingsCache::instance().servers().setFPHostName("");
SettingsCache::instance().servers().setFPPort("");
SettingsCache::instance().servers().setFPPlayerName("");
}
}
void ConnectionController::onPromptForgotPasswordReset()

View file

@ -87,7 +87,7 @@ private slots:
// Forgot-password flow
void onForgotPasswordSuccess();
void onForgotPasswordError();
void onForgotPasswordError(int responseCode);
void onPromptForgotPasswordReset();
void onPromptForgotPasswordChallenge();

View file

@ -837,7 +837,7 @@ void RemoteClient::requestForgotPasswordResponse(const Response &response)
emit sigPromptForForgotPasswordReset();
}
} else {
emit sigForgotPasswordError();
emit sigForgotPasswordError(response.response_code());
}
doDisconnectFromServer();
@ -873,7 +873,7 @@ void RemoteClient::submitForgotPasswordResetResponse(const Response &response)
derivedVerifier.clear();
}
} else {
emit sigForgotPasswordError();
emit sigForgotPasswordError(response.response_code());
}
doDisconnectFromServer();
@ -908,7 +908,7 @@ void RemoteClient::submitForgotPasswordChallengeResponse(const Response &respons
if (response.response_code() == Response::RespOk) {
emit sigPromptForForgotPasswordReset();
} else {
emit sigForgotPasswordError();
emit sigForgotPasswordError(response.response_code());
}
doDisconnectFromServer();

View file

@ -54,7 +54,7 @@ signals:
void notifyUserAboutUpdate();
void sigRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName);
void sigForgotPasswordSuccess();
void sigForgotPasswordError();
void sigForgotPasswordError(int responseCode);
void sigPromptForForgotPasswordReset();
void sigSubmitForgotPasswordResetToServer(const QString &hostname,
unsigned int port,