Skip to main content

Google Drive Salesforce Integration: Apex REST, OAuth 2.0 & Named Credentials Guide

In plain words: Integrating Google Drive with Salesforce allows your CRM to directly read, list, and upload files to Google Cloud storage. By authenticating via OAuth 2.0 and executing REST callouts in Apex, teams can offload heavy file storage from Salesforce, organize customer documents into Google Drive folders, and maintain real-time access right from record pages.

Salesforce orgs frequently hit data and file storage limits when storing heavy contracts, collateral, and customer uploads as native ContentDocument records. Integrating directly with Google Drive via the Google Drive REST API v3 offers a cost-effective, scalable solution. This guide walks through setting up OAuth credentials in Google Cloud, structuring robust Apex callout methods, and executing multipart file uploads safely.

1. Architecture Overview: OAuth 2.0 & Google Drive v3 REST API

Connecting Salesforce to Google Drive follows the standard OAuth 2.0 web application flow and Google Drive v3 endpoints:

  • Google Cloud Project & Consent: A project in Google Cloud Console with the Google Drive API enabled and OAuth 2.0 Client credentials generated.
  • Authentication Flow: Authorizes access to the https://www.googleapis.com/auth/drive scope and exchanges the temporary authorization code for long-lived refresh and access tokens.
  • Drive REST Endpoints: Uses https://www.googleapis.com/drive/v3/files for metadata queries and https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart for file uploads.
360 Google Drive Integration Card:
  • API Version: Google Drive REST API v3.
  • Auth Endpoint: https://accounts.google.com/o/oauth2/v2/auth
  • Token Endpoint: https://oauth2.googleapis.com/token
  • Recommended Architecture: Salesforce Named Credentials & External Credentials (eliminates manual refresh token handling in Apex).

2. Step-by-Step Setup: Google Cloud & Salesforce

Step 1: Configure Google Cloud Console
  1. Navigate to the Google Cloud Console and create a new project.
  2. Go to APIs & Services > Library, search for Google Drive API, and click Enable.
  3. Go to APIs & Services > Credentials, click Create Credentials > OAuth client ID, and select Web application.
  4. Add your Salesforce callback URL under Authorized redirect URIs (e.g., https://yourMyDomainName.my.salesforce.com/apex/GoogleDriveCallback or your Auth Provider callback URL).
  5. Copy your Client ID and Client Secret.
Step 2: Add Remote Site Settings in Salesforce
Navigate to Setup > Security > Remote Site Settings and whitelist:
  • https://accounts.google.com
  • https://oauth2.googleapis.com
  • https://www.googleapis.com

3. Programmatic Implementation in Apex

The following service class demonstrates OAuth code exchange, file listing, and constructing multipart requests for binary uploads.

public with sharing class GoogleDriveService {

    // Store securely in Protected Custom Metadata or use Named Credentials
    private static final String CLIENT_ID = 'YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com';
    private static final String CLIENT_SECRET = 'YOUR_GOOGLE_CLIENT_SECRET';
    private static final String REDIRECT_URI = 'https://yourinstance.my.salesforce.com/apex/GoogleDriveCallback';

    private static final String TOKEN_URL = 'https://oauth2.googleapis.com/token';
    private static final String DRIVE_API_URL = 'https://www.googleapis.com/drive/v3/files';
    private static final String UPLOAD_API_URL = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart';

    // 1. Build Authorization URL
    public static String getAuthorizationUrl() {
        return 'https://accounts.google.com/o/oauth2/v2/auth?' +
            'response_type=code' +
            '&client_id=' + EncodingUtil.urlEncode(CLIENT_ID, 'UTF-8') +
            '&redirect_uri=' + EncodingUtil.urlEncode(REDIRECT_URI, 'UTF-8') +
            '&scope=' + EncodingUtil.urlEncode('https://www.googleapis.com/auth/drive', 'UTF-8') +
            '&access_type=offline' +
            '&prompt=consent';
    }

    // 2. Exchange Auth Code for Access and Refresh Tokens
    public static Map<String, Object> exchangeCodeForTokens(String authCode) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(TOKEN_URL);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');

        String payload = 'code=' + EncodingUtil.urlEncode(authCode, 'UTF-8') +
            '&client_id=' + EncodingUtil.urlEncode(CLIENT_ID, 'UTF-8') +
            '&client_secret=' + EncodingUtil.urlEncode(CLIENT_SECRET, 'UTF-8') +
            '&redirect_uri=' + EncodingUtil.urlEncode(REDIRECT_URI, 'UTF-8') +
            '&grant_type=authorization_code';

        req.setBody(payload);

        HttpResponse res = new Http().send(req);
        if (res.getStatusCode() == 200) {
            return (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
        } else {
            throw new CalloutException('Token Exchange Failed: ' + res.getStatusCode() + ' ' + res.getBody());
        }
    }

    // 3. List Files from Google Drive
    public static String listFiles(String accessToken) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(DRIVE_API_URL + '?pageSize=20&fields=files(id,name,mimeType,size)');
        req.setMethod('GET');
        req.setHeader('Authorization', 'Bearer ' + accessToken);
        req.setHeader('Accept', 'application/json');

        HttpResponse res = new Http().send(req);
        if (res.getStatusCode() == 200) {
            return res.getBody();
        } else {
            throw new CalloutException('Failed to retrieve files: ' + res.getBody());
        }
    }

    // 4. Upload File (Multipart Body)
    public static String uploadFile(String fileName, String mimeType, Blob fileBody, String accessToken) {
        String boundary = '----------salesforce_boundary_' + String.valueOf(DateTime.now().getTime());
        String delimiter = '\r\n--' + boundary + '\r\n';
        String closeDelimiter = '\r\n--' + boundary + '--';

        // Part 1: Metadata JSON
        String metadataJson = '{"name": "' + String.escapeSingleQuotes(fileName) + '"}';
        String headerPayload = delimiter +
            'Content-Type: application/json; charset=UTF-8\r\n\r\n' +
            metadataJson +
            delimiter +
            'Content-Type: ' + mimeType + '\r\n' +
            'Content-Transfer-Encoding: base64\r\n\r\n';

        String encodedBody = EncodingUtil.base64Encode(fileBody);
        String fullPayload = headerPayload + encodedBody + closeDelimiter;

        HttpRequest req = new HttpRequest();
        req.setEndpoint(UPLOAD_API_URL);
        req.setMethod('POST');
        req.setHeader('Authorization', 'Bearer ' + accessToken);
        req.setHeader('Content-Type', 'multipart/related; boundary=' + boundary);
        req.setBody(fullPayload);

        HttpResponse res = new Http().send(req);
        if (res.getStatusCode() == 200 || res.getStatusCode() == 201) {
            return res.getBody();
        } else {
            throw new CalloutException('File Upload Failed: ' + res.getBody());
        }
    }
}

4. Modern Best Practice: Named Credentials vs. Manual Apex Auth

Why Use Named Credentials Instead of Custom Token Storage?
Writing manual token exchange code requires building custom token-refresh schedulers and storing client secrets in custom settings. By creating an Auth. Provider and a Named Credential in Salesforce Setup:
  • Salesforce automatically manages token refresh lifecycles in the background.
  • Apex code simplifies to a clean endpoint call: req.setEndpoint('callout:Google_Drive_Named_Cred/drive/v3/files'); with zero header construction.
  • Secrets remain encrypted in platform infrastructure, satisfying enterprise compliance standards.

5. Common Traps & Implementation Best Practices

Apex Heap Size Trap: Uploading Large Files Directly in Synchronous Transactions
Apex synchronous transactions enforce a strict 6 MB heap size limit (12 MB for asynchronous). Converting large binary blobs into multipart strings increases memory consumption rapidly. For files larger than 4–5 MB, utilize asynchronous Apex (Queueable / Batchable) or implement Google Drive's Resumable Upload API protocol.
Core Rule: Use OAuth 2.0 with the access_type=offline parameter to obtain a long-lived refresh token, structure multipart payloads with distinct boundary delimiters, and prefer Salesforce Named Credentials for production environments.
  • Include access_type=offline: Google only returns a refresh_token on the initial authorization request when access_type=offline and prompt=consent are specified.
  • Handle Token Expiry Gracefully: If making raw HTTP callouts without Named Credentials, check for 401 Unauthorized responses and trigger a refresh token callout before retrying the operation.
  • Mock Callouts in Test Classes: Always implement HttpCalloutMock classes to simulate JSON metadata responses and multipart upload receipts for unit test validation.

Summary

Integrating Google Drive with Salesforce streamlines document management, avoids platform storage limit bottlenecks, and provides unified file access for CRM users. By leveraging the Google Drive v3 REST API, configuring secure OAuth 2.0 authentication, and adopting Named Credentials, development teams can build scalable, high-performance file management solutions.