Skip to main content

Documentation Index

Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

Summary

The useGoogleDriveBackupReadiness hook is a pre-flight check that tells you whether the user’s linked Google account has the OAuth scopes required to back up MPC key shares to Google Drive. Use it to detect — and recover from — missing-scope situations before triggering an MPC reshare ceremony, so you don’t burn a ceremony on an upload that will fail. It pairs with useWalletBackup as the first layer of a two-layer defense: pre-flight readiness (this hook) catches the common cases; post-flight error inspection (via lastBackupError and isInsufficientGoogleDriveScopesError) handles legacy tokens captured before scope tracking was introduced. For more information on wallet backup, see the Google Drive backup documentation. The hook needs to be initialized within a child of DynamicContextProvider.

Functions

check

Reads the user’s stored Google OAuth token and compares its granted scopes against the scopes required for Google Drive backup.
This method is headless.
Returns: Promise<GoogleDriveBackupReadiness> — resolves with the new readiness state. The same object is also written to the hook’s state (so any of status, missingScopes, accessToken, error returned from the hook will reflect the latest result on the next render). Example:
const { check } = useGoogleDriveBackupReadiness();

const result = await check();
if (result.status === 'ready') {
  // Safe to call backupWallet(...)
}

requestAccess

Re-prompts the Google consent popup so the user can grant the missing scopes (or link a Google account if none is linked yet), refreshes the user, and re-runs check() against the new token. Use this after check() returns status: 'needs-access'.
This method opens a popup and uses Dynamic’s social linking flow.
Returns: Promise<GoogleDriveBackupReadiness> — resolves with the readiness state after the consent popup completes. Inspect status to see whether access was successfully granted. Example:
const { requestAccess } = useGoogleDriveBackupReadiness();

const result = await requestAccess();
if (result.status === 'ready') {
  // User granted the scopes; safe to call backupWallet(...)
} else if (result.status === 'needs-access') {
  // User cancelled or didn't grant the required scopes
}

reset

Clears the hook back to its initial 'idle' state. Useful when the user navigates away from the backup CTA and you want to discard cached readiness state. Returns: void

Properties

status

The current readiness status. Type: 'idle' | 'ready' | 'needs-access' | 'error'
StatusMeaning
'idle'The hook has not been checked yet, or reset() was called.
'ready'The stored token includes both required Google Drive scopes. Backup should succeed (barring transient errors).
'needs-access'Backup is unlikely to succeed without user intervention. Covers three sub-cases — see below. Call requestAccess() to recover.
'error'The underlying token fetch or relink rejected. Inspect error for the cause.
The 'needs-access' status covers three distinct sub-cases, distinguishable by missingScopes:
  1. No Google account linkedmissingScopes is populated with the full required set (GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES), so you can render copy directly without checking the empty case.
  2. Some scopes missingmissingScopes lists only the missing scopes.
  3. Legacy tokens (unknown scopes)missingScopes is an empty array. These are tokens captured before scope tracking shipped; requestAccess() forces a fresh token so the scopes get recorded.

isChecking

true while a check() or requestAccess() call is in flight. Type: boolean

missingScopes

Scopes the user still needs to grant. See the table above for how to interpret this in conjunction with status. Type: readonly string[]

accessToken

The user’s current Google OAuth access token, if one was successfully fetched. Present in 'ready' and most 'needs-access' results; absent when no Google account is linked or when the fetch errored. Type: string | undefined

error

The underlying error when status === 'error'. The token fetch and the relink/consent popup both surface here. Type: unknown

Usage

The recommended pattern is two-layer defense:
  • Layer 1 — pre-flight readiness: call check() when the user opens the “Backup to Google Drive” CTA. If status is 'needs-access', prompt the user and call requestAccess() to re-prompt the Google consent screen, then proceed with backupWallet(...).
  • Layer 2 — post-flight recovery UI: for legacy users where the stored scopes is empty, pre-flight cannot tell whether the upload will succeed. After the attempt, inspect lastBackupError from useWalletBackup during render — if it’s an insufficient-scopes error, surface a “Re-grant Drive access” button that calls requestAccess() and retries backupWallet(...) on click.
import {
  useGoogleDriveBackupReadiness,
  useWalletBackup,
  isInsufficientGoogleDriveScopesError,
  CloudBackupProvider,
} from '@dynamic-labs/sdk-react-core';

function BackupButton({ wallet }) {
  const { status, missingScopes, isChecking, check, requestAccess } =
    useGoogleDriveBackupReadiness();

  const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();

  // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when we
  // can already tell the upload will fail.
  const onBackup = async () => {
    let r = await check();
    if (r.status === 'needs-access') {
      r = await requestAccess();
      if (r.status !== 'ready') return; // user cancelled or relink failed
    }
    await backupWallet(wallet, CloudBackupProvider.GoogleDrive);
  };

  // Layer 2: post-flight recovery — driven by render-time inspection of
  // lastBackupError. No effect needed; React re-renders when state updates.
  const needsScopeRecovery =
    lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError);

  const onRecover = async () => {
    clearBackupError();
    const r = await requestAccess();
    if (r.status === 'ready') {
      await backupWallet(wallet, CloudBackupProvider.GoogleDrive);
    }
  };

  if (needsScopeRecovery) {
    return (
      <button onClick={onRecover} disabled={isChecking}>
        Re-grant Drive access and retry
      </button>
    );
  }

  return (
    <>
      {status === 'needs-access' && missingScopes.length > 0 && (
        <p>We need access to: {missingScopes.join(', ')}</p>
      )}
      <button onClick={onBackup} disabled={isChecking}>
        Back up to Google Drive
      </button>
    </>
  );
}
The same module also exports a few primitives for callers who want to build their own checks:
  • GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES — the readonly tuple of scopes required for backup.
  • findMissingGoogleDriveBackupScopes(grantedScopes) — given a list of granted scopes, returns the subset of required scopes that are missing.
  • hasAllGoogleDriveBackupScopes(grantedScopes) — convenience boolean.
  • isInsufficientGoogleDriveScopesError(error) — type guard for Google Drive backup failures caused by missing/insufficient OAuth scopes.

Learn More

Return Value

The hook returns an object with the following properties:
{
  status: 'idle' | 'ready' | 'needs-access' | 'error';
  isChecking: boolean;
  missingScopes: readonly string[];
  accessToken?: string;
  error?: unknown;
  check: () => Promise<GoogleDriveBackupReadiness>;
  requestAccess: () => Promise<GoogleDriveBackupReadiness>;
  reset: () => void;
}

Types

type GoogleDriveBackupReadinessStatus =
  | 'idle'
  | 'ready'
  | 'needs-access'
  | 'error';

type GoogleDriveBackupReadiness = {
  status: GoogleDriveBackupReadinessStatus;
  isChecking: boolean;
  missingScopes: readonly string[];
  accessToken?: string;
  error?: unknown;
};

type UseGoogleDriveBackupReadinessReturn = GoogleDriveBackupReadiness & {
  check: () => Promise<GoogleDriveBackupReadiness>;
  requestAccess: () => Promise<GoogleDriveBackupReadiness>;
  reset: () => void;
};