feat: secure_session commands, login tokens in the OS keychain (cinny #105, step 1)

Commands for the web client to keep a copy of the login tokens in the OS
keychain: secure_session_supported / _set / _get / _clear.

- Windows: Credential Manager via the keyring crate (3.6, windows-native),
  entry "session" in service "Lotus Chat". Only the secrets are stored
  (userId, deviceId, accessToken, refreshToken); the serialized value is
  capped at 1200 chars (Windows' limit is 2560 bytes).
- Other platforms: supported = false and the other commands answer "not
  supported on this platform" (Linux Secret Service can prompt to unlock a
  wallet at startup; that needs its own testing). No new Linux dependency:
  without a platform feature the crate only has its mock store.
- Keychain calls run on the blocking pool, off the main thread.

Step 1 is a mirror only (the web client still reads its session from
localStorage); see the cinny PR.

Tests: round trip + clear, clearing an empty keychain, incomplete and
oversized sessions rejected with nothing written, the JSON shape the web
client sends, a realistic OIDC session fits (keyring's mock store). Linux
release build: commands answer as designed and login is unaffected.
Windows: type-checked only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
Lotus CI
2026-09-29 00:25:01 -04:00
co-authored by Claude Opus 5.5
parent 884877a55b
commit 032b6e04e7
4 changed files with 204 additions and 0 deletions
+5
View File
@@ -13,6 +13,7 @@ use tauri_plugin_opener::OpenerExt;
pub mod gpu_workarounds;
mod native;
mod secure_session;
/// Bring the main window to the foreground from the tray / a hidden /
/// minimized state. Shared by the tray, single-instance, and deep-link paths.
@@ -952,6 +953,10 @@ pub fn run() {
native::focus_assist::get_focus_assist,
native::hotkeys::global_hotkeys_supported,
native::hotkeys::set_global_hotkeys,
secure_session::secure_session_supported,
secure_session::secure_session_set,
secure_session::secure_session_get,
secure_session::secure_session_clear,
])
.plugin(tauri_plugin_localhost::Builder::new(port).build())
.plugin(