feat(desktop): keep the login tokens only in the OS keychain (#105 step 2)
CI / Build & Quality Checks (push) Skipped
CI / Secret scan (gitleaks) (push) Skipped
CI / Playwright smoke (e2e) (push) Skipped
CI / Build & Quality Checks (pull_request) Successful in 3m50s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 12s
CI / Playwright smoke (e2e) (pull_request) Successful in 20m30s
CI / Build & Quality Checks (push) Skipped
CI / Secret scan (gitleaks) (push) Skipped
CI / Playwright smoke (e2e) (push) Skipped
CI / Build & Quality Checks (pull_request) Successful in 3m50s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 12s
CI / Playwright smoke (e2e) (pull_request) Successful in 20m30s
On the desktop app with a working keychain (Windows Credential Manager), the tokens leave localStorage: it keeps a token-less shell blob (tokensIn: keychain) and the full session lives in memory, read from the keychain at boot before anything reads the session, so getFallbackSession stays synchronous. Web, Linux and older desktop builds are unchanged. - A new session (login, token refresh) is written to localStorage in full first; the mirror verifies the keychain copy and only then strips the tokens, so one durable store always holds the newest tokens. - Existing installs migrate on first boot the same way; a failing keychain write keeps the tokens in localStorage. - If a shell is there but the keychain entry is missing or unreadable, the app shows Retry / Sign in again instead of logging out, and the mirror is not started (it would clear the entry). Fixes #105 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4120ccdbcc
commit
5f3c5d370a
@@ -0,0 +1,69 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Button, Text, config } from 'folds';
|
||||
import { removeFallbackSession } from '../state/sessions';
|
||||
import type { KeychainBootResult } from '../state/keychainSession';
|
||||
import type { KeychainInvoke } from '../state/keychainMirror';
|
||||
|
||||
/**
|
||||
* [Gitea #105] Shown instead of the app when the desktop app's sign-in is kept
|
||||
* in the OS keychain but couldn't be read this time. Never a silent logout:
|
||||
* Retry boots again; signing in again is an explicit choice.
|
||||
*/
|
||||
export function KeychainUnavailable({
|
||||
boot,
|
||||
invoke,
|
||||
}: {
|
||||
boot: KeychainBootResult;
|
||||
invoke: KeychainInvoke | undefined;
|
||||
}) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const missing = boot.mode === 'missing';
|
||||
const signInAgain = async () => {
|
||||
setBusy(true);
|
||||
removeFallbackSession();
|
||||
await invoke?.('secure_session_clear').catch(() => undefined);
|
||||
window.location.reload();
|
||||
};
|
||||
return (
|
||||
<Box
|
||||
direction="Column"
|
||||
alignItems="Center"
|
||||
justifyContent="Center"
|
||||
gap="400"
|
||||
style={{ minHeight: '100vh', padding: config.space.S700, textAlign: 'center' }}
|
||||
>
|
||||
<Text size="H3">Couldn't read your sign-in</Text>
|
||||
<Text size="T300" style={{ maxWidth: 460 }}>
|
||||
{missing
|
||||
? 'Lotus Chat keeps your sign-in in Windows Credential Manager, but the entry is missing. If you removed it, sign in again.'
|
||||
: 'Lotus Chat keeps your sign-in in Windows Credential Manager and couldn’t read it just now. This is usually temporary.'}
|
||||
</Text>
|
||||
{boot.mode === 'error' && (
|
||||
<Text size="T200" priority="300">
|
||||
{boot.error}
|
||||
</Text>
|
||||
)}
|
||||
<Box gap="200">
|
||||
<Button
|
||||
variant="Primary"
|
||||
size="400"
|
||||
radii="300"
|
||||
disabled={busy}
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
<Text size="B400">Retry</Text>
|
||||
</Button>
|
||||
<Button
|
||||
variant="Secondary"
|
||||
fill="Soft"
|
||||
size="400"
|
||||
radii="300"
|
||||
disabled={busy}
|
||||
onClick={signInAgain}
|
||||
>
|
||||
<Text size="B400">Sign in again</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user