Skip to content

Move GitHub access token from SQLite to OS credential store#11

Open
Ghvstcode wants to merge 3 commits intomainfrom
sustn/move-github-access-token-from-sqlite-to-os-credential-store
Open

Move GitHub access token from SQLite to OS credential store#11
Ghvstcode wants to merge 3 commits intomainfrom
sustn/move-github-access-token-from-sqlite-to-os-credential-store

Conversation

@Ghvstcode
Copy link
Copy Markdown
Owner

Summary

The GitHub access token is stored in plaintext in the local SQLite database (src/core/db/auth.ts:61github_access_token TEXT NOT NULL column). Anyone or any process with file system access to the user's app data directory can read the token, which grants repo read:user user:email scopes — full read/write access to all the user's repositories.

The keyring crate is already listed as a dependency in the project (per memory notes), but it's not being used for token storage. The fix is to migrate token storage to the OS credential manager (macOS Keychain via keyring crate on the Rust side) and expose get/set/clear commands via Tauri IPC. The frontend saveAuth function in src/core/db/auth.ts would store everything except the token in SQLite and call a Tauri command to persist the token in Keychain. getAuth would similarly fetch the token from Keychain.

This is a security-critical issue because the token has repo scope, meaning a leaked token grants full push access to every repository the user has connected. The SQLite database file is unencrypted and sits in a predictable app data directory.

Files involved: src/core/db/auth.ts (frontend auth CRUD), src-tauri/src/auth.rs (currently only generates UUIDs — needs get/set/clear token commands), src-tauri/src/migrations.rs (migration to drop the token column), src-tauri/src/lib.rs (register new commands). The keyring crate is already available.

Branch: sustn/move-github-access-token-from-sqlite-to-os-credential-store

…al store

The GitHub access token was stored in plaintext in the local SQLite
database, which is unencrypted and sits in a predictable app data
directory. Anyone with filesystem access could read the token, which
grants full repo read/write access to all connected repositories.

This commit migrates token storage to the OS credential manager
(macOS Keychain) via the `keyring` crate and exposes get/set/clear
commands over Tauri IPC. The frontend auth module now persists
everything except the token in SQLite and delegates token storage
to the native credential store.

- Add `keyring` v3 dependency to Cargo.toml
- Add set_github_token, get_github_token, clear_github_token Tauri
  commands in auth.rs using keyring::Entry
- Add migrate_token_to_keyring() pre-migration that moves any
  existing token from SQLite to keyring before the column is dropped
- Add SQL migration 13 to drop github_access_token column from auth
- Update frontend auth.ts: saveAuth stores token via IPC, getAuth
  retrieves it from keyring, clearAuth removes it from both stores
- Register new commands in lib.rs invoke_handler

SUSTN-Task: 21c49249-78ac-49b0-81fe-f1e573f12520
The keyring v3 crate was added without platform-specific features
(apple-native, windows-native, linux-native), causing it to fall back
to an in-memory mock credential store on all platforms. This meant
tokens stored via keyring were lost on every app restart.

Combined with migration 13 (which drops the github_access_token column
from SQLite), this caused permanent token loss: the pre-migration code
copied the token to the mock store, the migration deleted the SQLite
column, and on next launch the mock store was empty with no way to
recover the token.

Enabling the platform features activates the real OS credential
backends:
- macOS: Keychain via security-framework
- Windows: Credential Manager via windows-sys
- Linux: kernel keyutils

SUSTN-Task: 21c49249-78ac-49b0-81fe-f1e573f12520
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sustn Ready Ready Preview, Comment Mar 29, 2026 7:02am

}

/// Migrate an existing GitHub access token from the SQLite `auth` table
/// into the OS credential store. Must run **before** migration 13 drops
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

migration 13 is a little too vague - could you clarify what you mean?

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
// Migrate any existing GitHub token from SQLite → OS credential store
// before the SQL plugin applies migration 13 (which drops the column).
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - migration 13 is vague

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback!

if (rows.length === 0) return undefined;
return rowToRecord(rows[0]);

const token = await invoke<string | null>("get_github_token");
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback!

return rowToRecord(rows[0]);

const token = await invoke<string | null>("get_github_token");
if (!token) return undefined;
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really wanna be returning undefined?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback!

Replace the vague "migration 13" reference with an explicit description
of what the migration does (DROP COLUMN github_access_token), so readers
don't have to cross-reference migrations.rs to understand the ordering
constraint.

SUSTN-Task: 21c49249-78ac-49b0-81fe-f1e573f12520
@Ghvstcode
Copy link
Copy Markdown
Owner Author

I've addressed the review feedback:

Clarified the vague 'migration 13' reference in auth.rs doc comment to explicitly describe what the migration does (DROP COLUMN github_access_token), so the ordering constraint is understandable without cross-referencing migrations.rs.

Commit: 5e09220

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant