fix(ui): late Admin login reply could be misrouted after giving up

UITask::onRoomLoginResult() dispatches a login reply to whichever
screen is *currently* shown (curr == admin_screen ? AdminScreen :
MessagesScreen), not to whoever actually sent the request. Neither
giving up path (manual Cancel, or the timeout added in 23f43cac) told
MyMesh to stop tracking the request, so a reply that still arrived
after the user had navigated away landed on whatever screen they'd
moved to instead -- most likely MessagesScreen, which then persisted
its own unrelated _login_pw as the "confirmed" password for that
pubkey, silently corrupting the saved password even on a genuine
success.

Adds MyMesh::cancelUiPendingLogin(pub_key), pubkey-guarded so it's a
no-op if a newer request has since overwritten ui_pending_login, called
from both of AdminScreen's give-up paths. A late reply now simply
matches nothing and is dropped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-07-24 18:20:08 +02:00
parent 05609019b4
commit 5a5ebe9ff1
3 changed files with 24 additions and 1 deletions

View File

@@ -229,6 +229,19 @@ public:
return true;
}
// Called by a screen that gave up waiting on its own sendRoomLogin() (Cancel
// or a timeout) so a reply that arrives after doesn't get misrouted:
// UITask::onRoomLoginResult() dispatches by whichever screen is *currently*
// showing, not by who actually sent the request, so a late reply landing
// after the requester navigated away is delivered to whatever screen the
// user is on instead -- which then persists its own unrelated login state
// (e.g. a different screen's _login_pw) as if it were that reply's answer.
// Pubkey-guarded so this is a no-op if a newer request (this screen retrying,
// or a different screen entirely) has since overwritten ui_pending_login.
void cancelUiPendingLogin(const uint8_t* pub_key) {
if (ui_pending_login && memcmp(&ui_pending_login, pub_key, 4) == 0) ui_pending_login = 0;
}
// On-device UI logout: drops the local keep-alive tracking (mirrors the app's
// CMD_LOGOUT) and forgets the saved password, so the next room open prompts
// for credentials again instead of silently re-using them. No packet is sent

View File

@@ -436,6 +436,11 @@ public:
void poll() override {
if (_phase == LOGIN && _login_waiting && (int32_t)(millis() - _login_deadline_ms) >= 0) {
_login_waiting = false;
// Stop tracking this request on the MyMesh side too -- otherwise a reply
// that still arrives after we've given up gets misrouted to whatever
// screen the user is on by then (see cancelUiPendingLogin()'s comment),
// corrupting that screen's unrelated login state with our answer.
the_mesh.cancelUiPendingLogin(_target.id.pub_key);
// No response at all is ambiguous (could be a wrong/stale password, could
// just be out of range) -- but a saved password that's gone stale (the
// remote's password changed) is exactly this: silence, not a nack. Treat
@@ -515,7 +520,11 @@ public:
bool handleInput(char c) override {
if (_phase == LOGIN) {
if (_login_waiting) {
if (c == KEY_CANCEL) { _login_waiting = false; returnToOrigin(); }
if (c == KEY_CANCEL) {
_login_waiting = false;
the_mesh.cancelUiPendingLogin(_target.id.pub_key); // see cancelUiPendingLogin()'s comment
returnToOrigin();
}
return true;
}
auto r = kb().handleInput(c);