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

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.

2 participants