Skip to content

THU-506: Move integration secrets to local-only table#859

Open
raivieiraadriano92 wants to merge 4 commits into
mainfrom
raivieiraadriano92/thu-506-integration-secrets-local-only-table
Open

THU-506: Move integration secrets to local-only table#859
raivieiraadriano92 wants to merge 4 commits into
mainfrom
raivieiraadriano92/thu-506-integration-secrets-local-only-table

Conversation

@raivieiraadriano92
Copy link
Copy Markdown
Collaborator

@raivieiraadriano92 raivieiraadriano92 commented May 12, 2026

Summary

  • Creates integrations_secrets local-only table (PowerSync localOnly: true) for Google/Microsoft OAuth credentials and enabled flags — credentials never leave the device
  • Moves OAuth flow state (oauth_state, oauth_verifier, etc.) from synced settings to sessionStorage — PKCE verifiers and CSRF tokens are security-critical single-use values that should not sync
  • Moves integrations_google_is_enabled and integrations_microsoft_is_enabled to the local table alongside credentials — enabled flag is meaningless without local credentials on other devices
  • integrations_pro_is_enabled and integrations_do_not_ask_again remain in synced settings (not credential-dependent)
  • New src/dal/integrations.ts DAL module centralizes credential CRUD (uses SELECT-then-INSERT-or-UPDATE pattern for PowerSync view compatibility)

Test plan

  • Google OAuth connect flow → credentials saved to integrations_secrets, not settings table
  • Microsoft OAuth connect flow → same
  • Token refresh persists to local table
  • Disconnect deletes from local table, UI updates immediately
  • Toggle enabled updates local table
  • AI tools gated on enabled from local table
  • OAuth state uses sessionStorage (no oauth_* keys in settings table)
  • integrations_pro_is_enabled and integrations_do_not_ask_again still sync correctly
  • TypeScript compiles, lint passes, all tests pass

🤖 Generated with Claude Code


Note

Medium Risk
Medium risk because it changes how OAuth state and integration credentials/enabled flags are persisted, which can break connect/disconnect flows or tool availability if the new local-only table/sessionStorage state isn’t populated as expected.

Overview
Moves Google/Microsoft integration credentials and enabled flags out of synced settings and into a new local-only SQLite table (integrations_secrets) with a dedicated DAL (getIntegrationStatus, saveIntegrationCredentials, setIntegrationEnabled, deleteIntegrationCredentials).

Updates OAuth and deep-link handling to store PKCE/CSRF state in sessionStorage (src/lib/oauth-state.ts) instead of SQLite settings, and rewires UI/hooks/tool gating (useOAuthConnect, useIntegrationStatus, onboarding, integrations settings page, AI prompt/tool selection, token refresh) to read/write the new integration status source and invalidate the integrationStatus query after changes.

Refactors tests accordingly to seed/clear sessionStorage and the local-only integrations table.

Reviewed by Cursor Bugbot for commit a6aee2d. Bugbot is set up for automated code reviews on this repo. Configure here.

@raivieiraadriano92 raivieiraadriano92 self-assigned this May 12, 2026
@raivieiraadriano92 raivieiraadriano92 changed the title Raivieiraadriano92/thu 506 integration secrets local only table THU-506: Move integration secrets to local-only table May 12, 2026
@github-actions
Copy link
Copy Markdown

Semgrep Security Scan

No security issues found.

Comment thread src/lib/oauth-state.ts
export const setOAuthState = (update: Partial<OAuthState>): void => {
const current = getOAuthState()
const merged = { ...current, ...update }
sessionStorage.setItem(storageKey, JSON.stringify(merged))
@raivieiraadriano92 raivieiraadriano92 marked this pull request as ready for review May 12, 2026 17:48
@github-actions
Copy link
Copy Markdown

PR Metrics

Metric Value
Lines changed (prod code) +258 / -217
JS bundle size (gzipped) 🟢 1.02 MB → 1.02 MB (-4.6 KB, -0.4%)
Test coverage 🟢 71.27% → 71.40% (+0.1%)
Performance (preview) Preview not ready — Render deploy may have timed out
Accessibility
Best Practices
SEO

Updated Tue, 12 May 2026 17:49:24 GMT · run #1469

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a6aee2d. Configure here.


const db = getDb()
await updateSettings(db, { integrations_google_credentials: JSON.stringify(updated) })
await saveIntegrationCredentials(db, 'google', updated, true)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Token refresh hardcodes enabled flag to true

Medium Severity

Both ensureValidGoogleToken and the Microsoft ensureValidToken call saveIntegrationCredentials with enabled hardcoded to true. The old code only updated the credentials JSON via updateSettings without touching the enabled flag. Now, a token refresh will always force the integration back to enabled, which is a behavioral change that could silently re-enable an integration the user intentionally disabled.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a6aee2d. Configure here.

userEmail: mParsed?.profile?.email,
credentials: mParsed,
isEnabled: integrationStatusData?.microsoftEnabled ?? false,
isConnected: integrationStatusData?.microsoftConnected ?? false,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Connected integration no longer displays user email

Medium Severity

The Google and Microsoft integration objects no longer set userEmail or credentials. Previously these were populated by parsing the credentials JSON (which contained profile.email). The UI at line 197 conditionally renders integration.userEmail for connected accounts, so it now always falls back to showing "Google" or "Microsoft" instead of the user's email address.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a6aee2d. Configure here.

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.

2 participants