Skip to content

feat(sync): Google Drive sync backend (all platforms, PKCE desktop)#473

Open
ericgriffin wants to merge 19 commits into
mainfrom
worktree-google-drive-sync
Open

feat(sync): Google Drive sync backend (all platforms, PKCE desktop)#473
ericgriffin wants to merge 19 commits into
mainfrom
worktree-google-drive-sync

Conversation

@ericgriffin

@ericgriffin ericgriffin commented Jul 4, 2026

Copy link
Copy Markdown
Member

Summary

Makes Google Drive a complete, fully functional cloud-sync backend on all five platforms (iOS, macOS, Android, Windows, Linux). The Drive v3 appDataFolder REST layer pre-existed but was hidden and desktop-incapable; this PR adds the auth machinery, re-exposes the settings tile, and configures OAuth per platform.

Built via an authenticator-seam design (Approach A): one GoogleDriveStorageProvider keeps all Drive REST logic, with a GoogleDriveAuthenticator seam (boundary is http.Client) backed by two implementations — GoogleSignInAuthenticator (iOS/macOS/Android, google_sign_in v7) and DesktopOAuthAuthenticator (Windows/Linux, loopback OAuth via googleapis_auth).

Design spec and implementation plan: docs/superpowers/specs/2026-07-02-google-drive-sync-backend-design.md, docs/superpowers/plans/2026-07-02-google-drive-sync-backend.md.

Changes

  • Auth seamGoogleDriveAuthenticator interface; google_sign_in logic extracted into GoogleSignInAuthenticator (with Android serverClientId); new DesktopOAuthAuthenticator doing PKCE loopback OAuth with refresh-token persistence in GoogleDriveTokenStore (keychain via FallbackSecureStorage, mirroring the S3 store).
  • Provider refactorGoogleDriveStorageProvider delegates auth to the seam; adds a one-shot 401 silent-re-auth retry and quota/offline error mapping; _findFile now rethrows auth errors instead of masking them as "not found" (which could fork a duplicate on token expiry).
  • Desktop uses PKCE, no client secret — Google lists client_secret as optional for Desktop-app clients, so the loopback token exchange authenticates with the PKCE code_verifier alone. Only public OAuth client IDs are committed; nothing for secret scanning to flag.
  • UI — restored the Google Drive tile in Cloud Sync settings (removed the googledrive → null coercion), with account-email subtitle, availability gating, and a cancellable browser-wait dialog on desktop. Three new l10n strings translated across all 11 locales.
  • AvailabilitygoogleDriveAvailableProvider / googleDriveAccountEmailProvider; GoogleDriveClientConfig.isSupportedOnThisPlatform is the single source of truth shared with the supportsGoogleDrive capability flag so they can't diverge.
  • Platform OAuth config — macOS GIDClientID + reversed URL scheme + Keychain Sharing entitlement; Android + Desktop OAuth clients registered (project 433819313354, shared appDataFolder).

Test Plan

  • flutter test passes (9,624 passed, 14 platform-gated skips)
  • flutter analyze passes (whole project, clean)
  • Manual testing on: iOS, macOS, Android, Windows, Linux — per docs/superpowers/specs/2026-07-02-google-drive-sync-manual-test-checklist.md (real-device OAuth can't run on CI). This is the remaining pre-merge gate.

Two desktop-dialog widget tests are skipped on macOS and execute for the first time on Linux CI (inverse of the existing Apple-only tile-tap pattern) — worth watching the first CI run.

Merge note

A recent Dropbox sync provider (PR #461) touches the same files (cloud_sync_page.dart, sync_providers.dart, storage_providers.dart, the CloudProviderType enum, ARB files). If a dropbox enum value has landed on main, the exhaustive switch in cloudProviderInstanceFor won't compile until the merge adds the dropbox case — the analyzer catches it immediately; a mechanical merge fix, not a runtime hazard. ARB/tile changes merge additively.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a complete Google Drive cloud-sync backend across all supported platforms by introducing a Drive-authentication seam (mobile/macOS via google_sign_in, Windows/Linux via PKCE loopback OAuth), restoring the Google Drive settings tile with availability/email subtitle, and wiring platform OAuth configuration + localized UI strings.

Changes:

  • Introduces GoogleDriveAuthenticator with GoogleSignInAuthenticator (iOS/macOS/Android) and DesktopOAuthAuthenticator (Windows/Linux), plus a keychain-backed GoogleDriveTokenStore.
  • Refactors GoogleDriveStorageProvider to delegate auth via the seam and adds Drive error mapping + a one-shot 401 re-auth retry.
  • Restores the Google Drive Cloud Sync settings tile (availability-gated, email subtitle, desktop browser-wait dialog) and adds/updates unit + widget tests and l10n.

Reviewed changes

Copilot reviewed 43 out of 43 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/features/settings/presentation/providers/google_drive_ui_providers_test.dart Adds provider-level tests for Drive availability + email subtitle recomputation behavior.
test/features/settings/presentation/pages/cloud_sync_page_test.dart Updates Cloud Sync page widget tests for the restored Google Drive tile and desktop dialog flows.
test/core/services/cloud_storage/google_drive/google_drive_token_store_test.dart Adds unit tests for Drive desktop credential persistence behavior.
test/core/services/cloud_storage/google_drive/desktop_oauth_authenticator_test.dart Adds unit tests for desktop loopback OAuth behavior and token persistence/revocation.
test/core/services/cloud_storage/google_drive_storage_provider_test.dart Adds unit tests for Drive provider CRUD + 401 retry + quota mapping.
macos/Runner/Release.entitlements Adds Keychain Sharing access group required for Google Sign-In on macOS.
macos/Runner/DebugProfile.entitlements Adds Keychain Sharing access group required for Google Sign-In on macOS (debug).
macos/Runner/Info.plist Adds macOS GIDClientID + URL scheme for Google Sign-In callback routing.
lib/l10n/arb/app_en.arb Adds new Google Drive desktop availability + browser-wait dialog strings (EN).
lib/l10n/arb/app_ar.arb Adds translations for new Google Drive desktop/browser-wait strings (AR).
lib/l10n/arb/app_de.arb Adds translations for new Google Drive desktop/browser-wait strings (DE).
lib/l10n/arb/app_es.arb Adds translations for new Google Drive desktop/browser-wait strings (ES).
lib/l10n/arb/app_fr.arb Adds translations for new Google Drive desktop/browser-wait strings (FR).
lib/l10n/arb/app_he.arb Adds translations for new Google Drive desktop/browser-wait strings (HE).
lib/l10n/arb/app_hu.arb Adds translations for new Google Drive desktop/browser-wait strings (HU).
lib/l10n/arb/app_it.arb Adds translations for new Google Drive desktop/browser-wait strings (IT).
lib/l10n/arb/app_nl.arb Adds translations for new Google Drive desktop/browser-wait strings (NL).
lib/l10n/arb/app_pt.arb Adds translations for new Google Drive desktop/browser-wait strings (PT).
lib/l10n/arb/app_zh.arb Adds translations for new Google Drive desktop/browser-wait strings (ZH).
lib/l10n/arb/app_localizations.dart Regenerates localization interface with new Drive strings.
lib/l10n/arb/app_localizations_en.dart Regenerates EN localization implementation.
lib/l10n/arb/app_localizations_ar.dart Regenerates AR localization implementation.
lib/l10n/arb/app_localizations_de.dart Regenerates DE localization implementation.
lib/l10n/arb/app_localizations_es.dart Regenerates ES localization implementation.
lib/l10n/arb/app_localizations_fr.dart Regenerates FR localization implementation.
lib/l10n/arb/app_localizations_he.dart Regenerates HE localization implementation.
lib/l10n/arb/app_localizations_hu.dart Regenerates HU localization implementation.
lib/l10n/arb/app_localizations_it.dart Regenerates IT localization implementation.
lib/l10n/arb/app_localizations_nl.dart Regenerates NL localization implementation.
lib/l10n/arb/app_localizations_pt.dart Regenerates PT localization implementation.
lib/l10n/arb/app_localizations_zh.dart Regenerates ZH localization implementation.
lib/features/settings/presentation/providers/sync_providers.dart Adds googleDriveAvailableProvider + googleDriveAccountEmailProvider for UI.
lib/features/settings/presentation/providers/storage_providers.dart Wires supportsGoogleDrive to GoogleDriveClientConfig.isSupportedOnThisPlatform.
lib/features/settings/presentation/pages/cloud_sync_page.dart Restores the Drive tile + adds desktop browser-wait dialog during auth.
lib/core/services/cloud_storage/google_drive/google_sign_in_authenticator.dart Extracts mobile/macOS Google Sign-In auth implementation behind the seam.
lib/core/services/cloud_storage/google_drive/google_drive_token_store.dart Adds JSON-blob credential persistence for desktop OAuth creds.
lib/core/services/cloud_storage/google_drive/google_drive_client_config.dart Adds committed OAuth client IDs and platform/build support gating.
lib/core/services/cloud_storage/google_drive/google_drive_authenticator.dart Defines the authenticator seam interface used by the provider.
lib/core/services/cloud_storage/google_drive/desktop_oauth_authenticator.dart Implements Windows/Linux PKCE loopback OAuth + revocation + persistence.
lib/core/services/cloud_storage/google_drive_storage_provider.dart Refactors Drive provider to use the seam + adds 401 retry and error mapping.
docs/superpowers/specs/2026-07-02-google-drive-sync-manual-test-checklist.md Adds a manual device test checklist for the new backend.
docs/superpowers/specs/2026-07-02-google-drive-sync-backend-design.md Adds the design spec describing architecture, auth flows, and acceptance gates.
docs/superpowers/plans/2026-07-02-google-drive-sync-backend.md Adds the implementation plan for the backend work.

Comment thread lib/features/settings/presentation/pages/cloud_sync_page.dart Outdated
Comment thread lib/features/settings/presentation/pages/cloud_sync_page.dart Outdated
Comment thread lib/features/settings/presentation/pages/cloud_sync_page.dart Outdated
Comment thread lib/core/services/cloud_storage/google_drive/desktop_oauth_authenticator.dart Outdated
@ericgriffin ericgriffin self-assigned this Jul 4, 2026
…-sync

# Conflicts:
#	lib/features/settings/presentation/pages/cloud_sync_page.dart
…w gating

- URL-encode the refresh token in the desktop revocation request body
- restructure the browser-wait dialog around one synchronously-checked
  guard so auth-completion and Cancel can never race to pop the wrong route
- disable Sync Now for a persisted Google Drive selection when Drive is
  unavailable on the current build
@ericgriffin

Copy link
Copy Markdown
Member Author

Addressed the review feedback and resolved the merge conflict with main.

Merge with main (Dropbox provider #461): the only real conflict was a duplicate import in cloud_sync_page.dart (kept both); the CloudProviderType switch and provider tiles auto-merged with all four providers. Regenerated Drift/mockito codegen for main's new tables (checklists, buddy roles, trips, consolidation). Full suite green: 10,142 passed / 14 platform-gated skips, flutter analyze clean.

Copilot comments (all in 1d5ba305e5f):

  • Revocation token not URL-encoded — fixed with Uri.encodeQueryComponent; added a test that a 1//…-style token is percent-encoded in the body.
  • Cancel-vs-completion dialog race (both dialog comments) — restructured _authenticateWithBrowserWait so auth-completion and Cancel both route through a single closeDialog guard that is checked-and-set synchronously (no read-after-await), so exactly one pop happens and it can only target the dialog route.
  • hasProvider ignored Drive availability — Sync Now is now gated on googleDriveAvailable for a persisted Google Drive selection; added a widget test covering the unavailable-build case.

Desktop OAuth was also changed to PKCE with no committed client secret (Google lists client_secret as optional for Desktop-app clients), so nothing sensitive is in the repo. Real-device sign-in across the five platforms remains the pre-merge gate per the committed manual checklist.

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

@jamespet77

Copy link
Copy Markdown
Contributor

what is this change waiting on? I pulled the change and merged it with main and it appeared to be working after a few small updates because the working tree has changed so much. I only ask because I would prefer using google drive.

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

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

3 participants