From c81b95b8b9636f057ef22847f210a6d239174890 Mon Sep 17 00:00:00 2001 From: MarekZegare4 Date: Fri, 26 Jun 2026 11:34:08 +0200 Subject: [PATCH] feat(companion): persist app/USB-entered room passwords on device The on-device login path already saved room passwords to /room_pw; do the same for logins issued by the phone/USB app (CMD_SEND_LOGIN). The password is stashed when the login is sent and persisted once the server confirms, gated to ADV_TYPE_ROOM contacts; a failed login forgets any stale saved password, mirroring the on-device path. This lets the device post to a room standalone after a reboot without re-prompting. Co-Authored-By: Claude Opus 4.8 --- examples/companion_radio/MyMesh.cpp | 13 +++++++++++++ examples/companion_radio/MyMesh.h | 1 + release-notes.md | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index c36ade7c..44575f70 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -845,11 +845,13 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, pending_login = 0; int i = 0; + bool login_ok = false; if (memcmp(&data[4], "OK", 2) == 0) { // legacy Repeater login OK response out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; out_frame[i++] = 0; // legacy: is_admin = false memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix + login_ok = true; } else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; if (keep_alive_secs > 0) { @@ -863,12 +865,21 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, i += 4; // NEW: include server timestamp out_frame[i++] = data[7]; // NEW (v7): ACL permissions out_frame[i++] = data[12]; // FIRMWARE_VER_LEVEL + login_ok = true; } else { out_frame[i++] = PUSH_CODE_LOGIN_FAIL; out_frame[i++] = 0; // reserved memcpy(&out_frame[i], contact.id.pub_key, 6); i += 6; // pub_key_prefix } + // Persist app/USB-entered room passwords too, so the device can later post + // to that room standalone (after reboot, no phone) without re-prompting -- + // same store the on-device login path uses. Rooms only; a failed login + // forgets any stale saved password, mirroring onRoomLoginResult(). + if (contact.type == ADV_TYPE_ROOM) { + if (login_ok) saveRoomPassword(contact.id.pub_key, pending_login_pw); + else forgetRoomPassword(contact.id.pub_key); + } _serial->writeFrame(out_frame, i); } else if (ui_pending_login && memcmp(&ui_pending_login, contact.id.pub_key, 4) == 0) { // check for on-device UI login response ui_pending_login = 0; @@ -2185,6 +2196,8 @@ void MyMesh::handleCmdFrame(size_t len) { } else { clearPendingReqs(); memcpy(&pending_login, recipient->id.pub_key, 4); // match this to onContactResponse() + strncpy(pending_login_pw, password, sizeof(pending_login_pw) - 1); // saved on success if it's a room + pending_login_pw[sizeof(pending_login_pw) - 1] = 0; out_frame[0] = RESP_CODE_SENT; out_frame[1] = (result == MSG_SEND_SENT_FLOOD) ? 1 : 0; memcpy(&out_frame[2], &pending_login, 4); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index a6129b06..da9c5c82 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -320,6 +320,7 @@ private: NodePrefs _prefs; uint32_t pending_login; uint32_t ui_pending_login; // like pending_login, but triggered by on-device UI instead of BLE/USB app + char pending_login_pw[16]; // password of the in-flight app/USB login, persisted on success for ADV_TYPE_ROOM (see saveRoomPassword) uint32_t pending_status; uint32_t pending_telemetry, pending_discovery; // pending _TELEMETRY_REQ uint32_t pending_req; // pending _BINARY_REQ diff --git a/release-notes.md b/release-notes.md index 68f0ddbb..0ae0a7e5 100644 --- a/release-notes.md +++ b/release-notes.md @@ -10,7 +10,7 @@ - **Trail auto-pause** — recording **freezes on stops** (banking elapsed time and breaking the map line across the idle gap) and **resumes on movement** without ending the session; the home-screen blink keeps going while paused. - **Collapsible Tools** — tools are grouped into fold-in-place **Location / Comms / System** sections, the same model as Settings (Tools always opens folded to the section list), and the home carousel now uses **page-indicator icons** instead of dots. - **Waypoint coordinate editor** — add a waypoint by scroll-editing its latitude/longitude digit by digit. -- **On-device room login with saved passwords** — log in to a room server straight from the device (no phone app): pick the room and the password prompt appears automatically (a blank password works for open rooms), or re-login any time via the room's **context-menu "Login…"**. The password is **remembered across reboots**, so a room you've used before logs back in without retyping; a failed login (e.g. the server's password changed) forgets the stale password so the next attempt prompts again. Saved passwords are written with the same atomic, crash-safe persistence as contacts and channels. +- **On-device room login with saved passwords** — log in to a room server straight from the device (no phone app): pick the room and the password prompt appears automatically (a blank password works for open rooms), or re-login any time via the room's **context-menu "Login…"**. The password is **remembered across reboots**, so a room you've used before logs back in without retyping; a failed login (e.g. the server's password changed) forgets the stale password so the next attempt prompts again. Room passwords entered via the **phone app** are saved on the device too, so it can post to that room standalone after a reboot. Saved passwords are written with the same atomic, crash-safe persistence as contacts and channels. ### Fixes