google-ads-api icon indicating copy to clipboard operation
google-ads-api copied to clipboard

Add Service Account Authentication Support

Open rogiervandenberg opened this issue 4 months ago โ€ข 3 comments

This PR adds support for Google Service Account authentication to the Google Ads API library, enabling server-to-server authentication without requiring OAuth2 refresh tokens.

๐ŸŽฏ Problem Solved

Closes #493, #83

The library previously only supported OAuth2 authentication which requires:

  • Client ID and Client Secret
  • Refresh tokens for each customer
  • Manual token management and renewal

This approach is problematic for server-to-server applications where:

  • You don't want to store user refresh tokens
  • You need automated, unattended access to Google Ads
  • You want to use Google Cloud service accounts for authentication

โœจ Features Added

Service Account Authentication

  • Full support for Google Cloud service account authentication
  • No refresh tokens required
  • Compatible with existing OAuth2 authentication (non-breaking change)
  • Works with both gRPC and REST API calls
  • Type-safe implementation with TypeScript support

Clean API Design

  • Simple, intuitive API that follows Google Auth Library patterns
  • Maintains backward compatibility with existing OAuth2 code
  • Proper error handling for authentication failures

๐Ÿ’ป Usage

Before (OAuth2 only)

const client = new GoogleAdsApi({
  client_id: "CLIENT_ID",
  client_secret: "CLIENT_SECRET",
  developer_token: "DEVELOPER_TOKEN",
});

const customer = client.Customer({
  customer_id: "CUSTOMER_ID",
  refresh_token: "REFRESH_TOKEN", // Required!
});

After (With Service Account Support)

import { GoogleAdsApi } from 'google-ads-api';
import { auth, JWT } from 'google-auth-library';

// Create JWT auth client from service account key
const authClient = auth.fromJSON(serviceAccountKey) as JWT;
authClient.scopes = ["https://www.googleapis.com/auth/adwords"];
await authClient.authorize();

// Create GoogleAdsApi client with service account auth
const client = new GoogleAdsApi({
  auth_client: authClient,
  developer_token: "DEVELOPER_TOKEN",
});

// No refresh_token needed!
const customer = client.Customer({
  customer_id: "CUSTOMER_ID",
});

๐Ÿ”ง Implementation Details

Core Changes

  • Enhanced ClientOptions: Added ServiceAccountClientOptions type alongside existing OAuth2ClientOptions
  • Updated Service class: Modified authentication logic to handle both OAuth2 and service account flows
  • Type safety: Added proper TypeScript interfaces and type guards
  • Backward compatibility: All existing OAuth2 code continues to work unchanged

Authentication Flow

  • gRPC calls: Service account auth client is used with grpc.credentials.createFromGoogleCredential()
  • REST calls: Access tokens are obtained directly from the service account auth client
  • Token caching: Maintains existing access token caching for performance

๐Ÿ“š Documentation

  • Added comprehensive documentation in SERVICE_ACCOUNT_AUTH.md
  • Included practical examples showing service account setup and usage
  • Updated type definitions with proper interfaces

๐Ÿงช Testing

  • Added comprehensive test suite for service account authentication
  • Tests verify both OAuth2 and service account authentication paths
  • Type guards properly distinguish between authentication methods
  • All existing tests continue to pass

Breaking Changes: None - this is fully backward compatible, new service account authentication is opt-in and both methods can coexist.

Dependencies: Uses existing google-auth-library dependency (updated to compatible version)

rogiervandenberg avatar Sep 25 '25 12:09 rogiervandenberg

@rogiervandenberg I wanted to try it and i installed it via npm i "https://github.com/rogiervandenberg/google-ads-api/tarball/add-service-account-support" but that doesn't seem to make the build.

Running npm run build was met with alot of error TS1056: Accessors are only available when targeting ECMAScript 5 and higher explosions.

Do you you know how could i properly install it as a package while it's not merged? Edit: seems like: npm install rogiervandenberg/google-ads-api#add-service-account-support does the job!

psociety avatar Oct 06 '25 14:10 psociety

Hello, just wanted to leave here this issue I've encountered while trying to make this PR work: https://github.com/grpc/grpc-node/issues/2993 Reverting google-auth-library to 9.15.1 fixes it. It may be related to how headers are passed down the line: https://github.com/rogiervandenberg/google-ads-api/blob/3c8771ffd72f63347cd51fd69471149b6ec9e764/src/client.ts#L87

miwelc avatar Oct 10 '25 16:10 miwelc

I've updated the PR to update to google-auth-library v10 and fix the way headers are passed down.

Fixed CREDENTIALS_MISSING error by switching to grpc.credentials.createFromMetadataGenerator. This resolves the incompatibility between google-auth-library v10 and the previous gRPC credential method.

rogiervandenberg avatar Nov 19 '25 13:11 rogiervandenberg