Skip to content

Move cloud OAuth token from localStorage to OS keychain #60

@chefsale

Description

@chefsale

Context

Raised multiple times in review on PR #52 (Bugbot: 3104888918, 3104928927, 3104931735, 3106546873, 3106571423). Deferred for a follow-up to avoid stretching #52 further.

Problem

cloudIdToken (a Google OAuth ID token, effectively a Bearer credential for the Cloud API) is persisted in plaintext via saveSettings()localStorage (apps/desktop/src/utils/settings.ts). Any JS running in the WebView (including injected proxy scripts in app windows loaded from HTTPS origins) can read it. On disk, Tauri's localStorage is unencrypted JSON.

Existing facility

The app already has OS-keychain storage wired up: secure_store_token / secure_get_token Tauri commands backed by the keyring crate. Used for the local node's access/refresh tokens. Cloud token just needs to be routed through the same path.

Scope

  • Remove cloudIdToken from Settings type and from getSettings / saveSettings in apps/desktop/src/utils/settings.ts.
  • Add a cloud_id_token key (or similar) to the keyring service used by secure_store_token.
  • Rewrite apps/desktop/src/utils/cloudAuth.ts:
    • setCloudCredentials / equivalent → call the secure store Tauri command (async).
    • getCloudIdToken → read from secure store.
    • disconnectCloud → clear the secure store entry.
  • Every caller of getCloudIdToken becomes async. Audit: apps/desktop/src/pages/Settings.tsx, Namespaces.tsx, Onboarding.tsx (and anywhere else).
  • Keep the expiry check (isTokenExpired) — don't remove that logic, just relocate the storage.

Migration

Read localStorage once on startup; if cloudIdToken is present, move it to the keyring and delete the localStorage entry. One-shot migration keeps existing users signed in across the upgrade.

Also worth checking

The decoded user profile (cloudUserEmail, cloudUserName, cloudUserPicture) currently lives next to the token in Settings. Those are less sensitive but derivable from the token — consider whether they need to move too, or stay in localStorage. My lean: move cloudIdToken only; keep the profile fields in localStorage so UI can hydrate without an async keychain call on every render.

Related

Blocks anything that'd touch token handling on desktop (e.g., planned multi-account or tauri-session refactors). Not urgent per se but it's the last security-debt item from PR #52 review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions