fix: stop keychain modal on every launch and fix grant-access loop

- Revert hydrate_extension_pairing_token to consult the DB's
  keychainAccessGranted flag instead of unconditionally skipping the
  keychain.  When the flag is true the backend tries the keychain; if
  macOS trusts the current binary (no code-signature change) the read
  succeeds silently and the modal is suppressed.  When the flag is
  false the keychain is skipped and the modal is shown exactly once.
- Fix the Grant Access button in the KeychainPermissionModal by
  writing the result (token, persistent flag) directly into the store
  instead of calling hydratePairingToken() again.  The re-hydration
  raced with Zustand's async persist middleware, which could read
  keychainAccessGranted=false from the DB and flip persistent back
  to false, re‑showing the modal in an endless loop.
- The scheduler defaults (enabled: false) were already correct in
  both the Rust default_settings() and the frontend Zustand store;
  no code change needed.
This commit is contained in:
NimBold
2026-06-25 02:38:40 +03:30
parent 0a647cd612
commit 09f103ea04
2 changed files with 10 additions and 8 deletions
+1 -6
View File
@@ -3435,12 +3435,7 @@ fn hydrate_extension_pairing_token(
app_state: tauri::State<'_, AppState>,
) -> Result<PairingTokenHydration, String> {
let mut connection = database.lock()?;
// Always skip the keychain during automatic hydration so the operating
// system never presents a credential-access prompt before the app's own
// modal is visible. The frontend will display the KeychainPermissionModal
// and only call grant_keychain_access — which touches the keychain — after
// the user explicitly clicks "Grant Access".
let skip_keychain = true;
let skip_keychain = !crate::db::is_keychain_access_granted(&connection).unwrap_or(false);
match crate::db::hydrate_pairing_token(&mut connection, skip_keychain) {
Ok((token, token_changed)) => {
if let Ok(mut pairing_token) = app_state.extension_pairing_token.write() {
+9 -2
View File
@@ -19,8 +19,15 @@ export const KeychainPermissionModal: React.FC = () => {
try {
const result = await invoke('grant_keychain_access');
if (result.persistent) {
useSettingsStore.setState({ keychainAccessGranted: true });
await useSettingsStore.getState().hydratePairingToken();
// Set all keychain-related state directly from the grant result
// instead of calling hydratePairingToken() again, which would
// re-read the DB before Zustand's persist middleware has written
// keychainAccessGranted and could flip persistent back to false.
useSettingsStore.setState({
keychainAccessGranted: true,
extensionPairingToken: result.token,
isPairingTokenPersistent: true
});
setShowKeychainModal(false);
} else {
setError(result.error || 'Failed to grant keychain access.');