feat(desktop): mirror the login tokens into the OS keychain (#105, step 1)
CI / Build & Quality Checks (pull_request) Successful in 1m46s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 8s
CI / Playwright smoke (e2e) (pull_request) Successful in 8m44s
CI / Build & Quality Checks (pull_request) Successful in 1m46s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 8s
CI / Playwright smoke (e2e) (pull_request) Successful in 8m44s
Step 1 of moving the desktop app's login out of the webview's plaintext localStorage: keep a verified copy of the tokens in the OS keychain (Windows Credential Manager, via cinny-desktop's new secure_session_* commands). The session is still read from localStorage exactly as before, so nothing about login changes and a keychain problem can't log anyone out. Step 2 (a later release, once this has run on real installs) switches reads to the keychain and drops the tokens from localStorage. - sessions.ts: onSessionPersisted — listeners told about every session write (login, token rotation) and removal (logout); a throwing listener can't break the write. - keychainMirror.ts: desktop only. Mirrors userId/deviceId/accessToken/ refreshToken (not the rest of the session); reads first and writes only when the copy differs, then verifies by reading back; serialized, 5 s timeouts; no session → clear (also covers a logout whose reload beat the clear). Every failure is a status, never an exception. A desktop build without the commands reads as "unsupported", so this can ship before the desktop side. - Settings → General (desktop): "Login in the system keychain" status. Tested: unit tests (fake keychain: store, no rewrite when current, rotation, clear, unsupported, missing commands, denied write, read-back mismatch, timeout); a simulated desktop app with a fake keychain, 14/14 (login mirrors only the secrets, Settings status, reload verifies without rewriting, logout clears, an existing session is mirrored after upgrade, Linux/denied show an honest status and stay logged in); the real Linux desktop binary (commands answer "unsupported", login unaffected, Settings says so). Unit 1295 pass, Playwright 20 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
91f82d60e3
commit
9b9f33b270
@@ -114,6 +114,7 @@ import { SequenceCardStyle } from '../styles.css';
|
||||
import { UpdateProgress, useTauriUpdater } from '../../../hooks/useTauriUpdater';
|
||||
import { describeUpdateError, manualDownloadUrl } from '../../../utils/updateErrors';
|
||||
import { isTauri as isTauriEnv, invokeTauri, tauriInvoke } from '../../../hooks/useTauri';
|
||||
import { useKeychainMirrorStatus } from '../../../state/keychainMirror';
|
||||
import { isSafeGlobalToggleKey } from '../../../hooks/useCallHotkeys';
|
||||
import { customWindowChromeAtom } from '../../../state/customWindowChrome';
|
||||
import { useDateFormatItems } from '../../../hooks/useDateFormat';
|
||||
@@ -238,6 +239,27 @@ function AutostartSetting() {
|
||||
);
|
||||
}
|
||||
|
||||
// [Gitea #105] Desktop: whether the login is also kept in the OS keychain.
|
||||
function KeychainMirrorSetting() {
|
||||
const status = useKeychainMirrorStatus();
|
||||
if (!isTauriEnv() || status.state === 'idle' || status.state === 'cleared') return null;
|
||||
let description: string;
|
||||
if (status.state === 'ok') {
|
||||
description =
|
||||
"A copy of your login is kept in the system keychain (Windows Credential Manager). A later update will keep it only there, out of the app's data folder.";
|
||||
} else if (status.state === 'unsupported') {
|
||||
description =
|
||||
"Not available on this system yet. Your login is saved in the app's data folder, as before.";
|
||||
} else {
|
||||
description = `Couldn't save a copy to the system keychain (${status.error}). You stay logged in; your login is saved in the app's data folder, as before.`;
|
||||
}
|
||||
return (
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Login in the system keychain" description={description} />
|
||||
</SequenceCard>
|
||||
);
|
||||
}
|
||||
|
||||
type ThemeSelectorProps = {
|
||||
themeNames: Record<string, string>;
|
||||
themes: Theme[];
|
||||
@@ -570,6 +592,7 @@ function Appearance() {
|
||||
|
||||
<DesktopChromeSetting />
|
||||
<AutostartSetting />
|
||||
<KeychainMirrorSetting />
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
|
||||
Reference in New Issue
Block a user