mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-02 15:53:51 +00:00
Improve 2FA/PIN flow: prefill + clearer failure
This commit is contained in:
parent
62a49644a5
commit
2082ef1422
4 changed files with 68 additions and 9 deletions
|
|
@ -45,7 +45,9 @@ public:
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
void authenticate(const std::string& username, const std::string& password);
|
void authenticate(const std::string& username, const std::string& password);
|
||||||
|
void authenticate(const std::string& username, const std::string& password, const std::string& pin);
|
||||||
void authenticateWithHash(const std::string& username, const std::vector<uint8_t>& authHash);
|
void authenticateWithHash(const std::string& username, const std::vector<uint8_t>& authHash);
|
||||||
|
void authenticateWithHash(const std::string& username, const std::vector<uint8_t>& authHash, const std::string& pin);
|
||||||
// Optional: when the auth server requires a PIN (securityFlags & 0x01), call this to continue.
|
// Optional: when the auth server requires a PIN (securityFlags & 0x01), call this to continue.
|
||||||
// PIN must be 4-10 digits.
|
// PIN must be 4-10 digits.
|
||||||
void submitPin(const std::string& pin);
|
void submitPin(const std::string& pin);
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ private:
|
||||||
int expansionIndex = 0; // Index into expansion registry profiles
|
int expansionIndex = 0; // Index into expansion registry profiles
|
||||||
bool authenticating = false;
|
bool authenticating = false;
|
||||||
bool showPassword = false;
|
bool showPassword = false;
|
||||||
|
bool pinAutoSubmitted_ = false;
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
std::string statusMessage;
|
std::string statusMessage;
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,10 @@ void AuthHandler::requestRealmList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthHandler::authenticate(const std::string& user, const std::string& pass) {
|
void AuthHandler::authenticate(const std::string& user, const std::string& pass) {
|
||||||
|
authenticate(user, pass, std::string());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthHandler::authenticate(const std::string& user, const std::string& pass, const std::string& pin) {
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
LOG_ERROR("Cannot authenticate: not connected to auth server");
|
LOG_ERROR("Cannot authenticate: not connected to auth server");
|
||||||
fail("Not connected");
|
fail("Not connected");
|
||||||
|
|
@ -97,7 +101,7 @@ void AuthHandler::authenticate(const std::string& user, const std::string& pass)
|
||||||
|
|
||||||
username = user;
|
username = user;
|
||||||
password = pass;
|
password = pass;
|
||||||
pendingPin_.clear();
|
pendingPin_ = pin;
|
||||||
securityFlags_ = 0;
|
securityFlags_ = 0;
|
||||||
pinGridSeed_ = 0;
|
pinGridSeed_ = 0;
|
||||||
pinServerSalt_ = {};
|
pinServerSalt_ = {};
|
||||||
|
|
@ -111,6 +115,10 @@ void AuthHandler::authenticate(const std::string& user, const std::string& pass)
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthHandler::authenticateWithHash(const std::string& user, const std::vector<uint8_t>& authHash) {
|
void AuthHandler::authenticateWithHash(const std::string& user, const std::vector<uint8_t>& authHash) {
|
||||||
|
authenticateWithHash(user, authHash, std::string());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthHandler::authenticateWithHash(const std::string& user, const std::vector<uint8_t>& authHash, const std::string& pin) {
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
LOG_ERROR("Cannot authenticate: not connected to auth server");
|
LOG_ERROR("Cannot authenticate: not connected to auth server");
|
||||||
fail("Not connected");
|
fail("Not connected");
|
||||||
|
|
@ -127,7 +135,7 @@ void AuthHandler::authenticateWithHash(const std::string& user, const std::vecto
|
||||||
|
|
||||||
username = user;
|
username = user;
|
||||||
password.clear();
|
password.clear();
|
||||||
pendingPin_.clear();
|
pendingPin_ = pin;
|
||||||
securityFlags_ = 0;
|
securityFlags_ = 0;
|
||||||
pinGridSeed_ = 0;
|
pinGridSeed_ = 0;
|
||||||
pinServerSalt_ = {};
|
pinServerSalt_ = {};
|
||||||
|
|
@ -341,7 +349,21 @@ void AuthHandler::handlePacket(network::Packet& packet) {
|
||||||
if (state == AuthState::CHALLENGE_SENT) {
|
if (state == AuthState::CHALLENGE_SENT) {
|
||||||
handleLogonChallengeResponse(packet);
|
handleLogonChallengeResponse(packet);
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING("Unexpected LOGON_CHALLENGE response in state: ", (int)state);
|
// Some servers send a short LOGON_CHALLENGE failure packet if auth times out while we wait for 2FA/PIN.
|
||||||
|
LogonChallengeResponse response;
|
||||||
|
if (LogonChallengeResponseParser::parse(packet, response) && !response.isSuccess()) {
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "Server cancelled authentication";
|
||||||
|
if (state == AuthState::PIN_REQUIRED) {
|
||||||
|
ss << " while waiting for 2FA/PIN code";
|
||||||
|
}
|
||||||
|
ss << ": " << getAuthResultString(response.result)
|
||||||
|
<< " (code 0x" << std::hex << std::setw(2) << std::setfill('0')
|
||||||
|
<< static_cast<unsigned>(response.result) << std::dec << ")";
|
||||||
|
fail(ss.str());
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("Unexpected LOGON_CHALLENGE response in state: ", (int)state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,18 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
|
||||||
// Checkbox state changed
|
// Checkbox state changed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optional 2FA / PIN field (some servers require this; e.g. Turtle WoW uses Google Authenticator).
|
||||||
|
// Keep it visible pre-connect so we can send LOGON_PROOF immediately after the SRP challenge.
|
||||||
|
{
|
||||||
|
ImGuiInputTextFlags pinFlags = ImGuiInputTextFlags_Password;
|
||||||
|
if (authHandler.getState() == auth::AuthState::PIN_REQUIRED) {
|
||||||
|
pinFlags |= ImGuiInputTextFlags_EnterReturnsTrue;
|
||||||
|
}
|
||||||
|
ImGui::InputText("2FA / PIN", pinCode, sizeof(pinCode), pinFlags);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextDisabled("(optional)");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
@ -356,6 +368,7 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
|
||||||
if (authenticating) {
|
if (authenticating) {
|
||||||
auto state = authHandler.getState();
|
auto state = authHandler.getState();
|
||||||
if (state != auth::AuthState::PIN_REQUIRED) {
|
if (state != auth::AuthState::PIN_REQUIRED) {
|
||||||
|
pinAutoSubmitted_ = false;
|
||||||
authTimer += ImGui::GetIO().DeltaTime;
|
authTimer += ImGui::GetIO().DeltaTime;
|
||||||
|
|
||||||
// Show progress with elapsed time
|
// Show progress with elapsed time
|
||||||
|
|
@ -363,13 +376,28 @@ void AuthScreen::render(auth::AuthHandler& authHandler) {
|
||||||
snprintf(progressBuf, sizeof(progressBuf), "Authenticating... (%.0fs)", authTimer);
|
snprintf(progressBuf, sizeof(progressBuf), "Authenticating... (%.0fs)", authTimer);
|
||||||
ImGui::Text("%s", progressBuf);
|
ImGui::Text("%s", progressBuf);
|
||||||
} else {
|
} else {
|
||||||
ImGui::TextWrapped("This server requires a PIN. Enter your PIN to continue.");
|
ImGui::TextWrapped("This server requires a 2FA / PIN code. Enter it and submit quickly (the server may time out).");
|
||||||
ImGui::InputText("PIN", pinCode, sizeof(pinCode), ImGuiInputTextFlags_Password);
|
|
||||||
|
// If the user already typed a code before clicking Connect, submit immediately once.
|
||||||
|
if (!pinAutoSubmitted_) {
|
||||||
|
bool digitsOnly = true;
|
||||||
|
size_t len = std::strlen(pinCode);
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
if (pinCode[i] < '0' || pinCode[i] > '9') { digitsOnly = false; break; }
|
||||||
|
}
|
||||||
|
if (digitsOnly && len >= 4 && len <= 10) {
|
||||||
|
authHandler.submitPin(pinCode);
|
||||||
|
pinCode[0] = '\0';
|
||||||
|
pinAutoSubmitted_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Submit PIN")) {
|
if (ImGui::Button("Submit 2FA/PIN")) {
|
||||||
authHandler.submitPin(pinCode);
|
authHandler.submitPin(pinCode);
|
||||||
// Don't keep the PIN around longer than needed.
|
// Don't keep the code around longer than needed.
|
||||||
pinCode[0] = '\0';
|
pinCode[0] = '\0';
|
||||||
|
pinAutoSubmitted_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -489,18 +517,24 @@ void AuthScreen::attemptAuth(auth::AuthHandler& authHandler) {
|
||||||
authenticating = true;
|
authenticating = true;
|
||||||
authTimer = 0.0f;
|
authTimer = 0.0f;
|
||||||
setStatus("Connected, authenticating...", false);
|
setStatus("Connected, authenticating...", false);
|
||||||
|
pinAutoSubmitted_ = false;
|
||||||
|
|
||||||
// Save login info for next session
|
// Save login info for next session
|
||||||
saveLoginInfo(false);
|
saveLoginInfo(false);
|
||||||
|
|
||||||
|
const std::string pinStr = trimAscii(pinCode);
|
||||||
|
|
||||||
// Send authentication credentials
|
// Send authentication credentials
|
||||||
if (useHash) {
|
if (useHash) {
|
||||||
auto hashBytes = hexDecode(savedPasswordHash);
|
auto hashBytes = hexDecode(savedPasswordHash);
|
||||||
authHandler.authenticateWithHash(username, hashBytes);
|
authHandler.authenticateWithHash(username, hashBytes, pinStr);
|
||||||
} else {
|
} else {
|
||||||
usingStoredHash = false;
|
usingStoredHash = false;
|
||||||
authHandler.authenticate(username, password);
|
authHandler.authenticate(username, password, pinStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't keep the code around longer than needed.
|
||||||
|
pinCode[0] = '\0';
|
||||||
} else {
|
} else {
|
||||||
std::stringstream errSs;
|
std::stringstream errSs;
|
||||||
errSs << "Failed to connect to " << hostname << ":" << port
|
errSs << "Failed to connect to " << hostname << ":" << port
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue