Opteo / google-ads-api

Google Ads API client library for Node.js
https://opteo.com
MIT License
270 stars 90 forks source link

Does this library support using service accounts? If so, how to get refresh token? #412

Closed unicornsoftwareinc closed 1 year ago

unicornsoftwareinc commented 2 years ago

Hi there, I'm trying to use a service account with this library. Is that possible?

I've gone through the process of setting up the service account, I have the client ID, secret, developer token. What I'm confused about now is how to get the refresh token to use with this library? From my understanding service accounts don't need refresh tokens in the first place...

Any insight here would be much appreciated.

Thanks

felixmosh commented 2 years ago

This lib doesn't supports connection via service account, therefore, I've generated a refresh token for the service account with the givven form

assaf commented 2 years ago

You can use a service account to generate an access token. It seems the trick is to impersonate a user who has access to the manager account (one you got the developer token from, I guess):

import jws from "jws";

const iss = // client_email from service account credentials
const secret = // private_key from service account credentials
const sub = // email of the user with Google Ads manager account

// Create a JWT token based on the service account credentials
const now = Math.floor(Date.now() / 1000);
const header = { alg: "RS256", typ: "JWT" };
const payload = {
  aud: "https://oauth2.googleapis.com/token",
  exp: now + 60,
  iat: now,
  iss,
  scope: "https://www.googleapis.com/auth/adwords",
  sub
};
const jwt = await jws.sign({ header, payload, secret });
const response = await fetch("https://oauth2.googleapis.com/token", {
  body: new URLSearchParams({
    grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
    assertion: jwt,
  }),
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  method: "POST",
});
const { access_token } = await response.json();
advance512 commented 2 years ago

@assaf How do you use this access token with the library?

advance512 commented 2 years ago

@felixmosh Did you mean an access token, like Assaf does here? I didn't think it was possible to generate a refresh token for a service account..

mkosik commented 2 years ago

@advance512 yes, Service account itself only gets an access token, which belongs to your app, not a user & renews it if necessary. When used directly for server-to-server interactions, app based access token is thus all you need. Please refer here: Using OAuth 2.0 to Access Google APIs Note: refresh token on the other hand is based on User OAuth flow & is given to apps requiring "offline access" - so they can e.g. refresh token outside of current user session.

But sometimes, Service account is also used to impersonate a user with the OAuth2 assertion flow (a.k.a. "delegated access" as also referred in the link above - "...your application can request delegated access to some resources."). More info on setting up delegated access: Control API access with domain-wide delegation

Some services' APIs allow both direct Service account access, as well as delegated access. BUT "Google Ads does not support using service accounts without impersonation." as mentioned in this article about using service account with Google Ads API: Google Ads API - Authorization - Service Accounts Note: From link above: "...using service accounts and assertion flow with Google OAuth2 requires you to have your own domain registered with Google Workspace." So delegated account access is really only usable for G Workspace users, who give you access on admin level for your service account, with rather broad access - to all users within the domain & thus is generally discouraged to use, unless you basically need impersonation as a feature.

Only allowing delegated access with service account in Google Ads API is by design, because of Google Ads / MCC accounts structure. In short, these are separate accounts in Google Ads ecosystem, not G Accounts. Users are invited & are only given access to them, so you have to impersonate a user (& only a user with G Account in Workspace domain will work), to gain access to all Ad accounts that user has access to.

So in this case, you will have to go trough OAuth2 assertion flow - sth like @assaf mentioned, but I'm not sure if you can use resulting access token directly with this lib, without modifying it. You can try to supply it instead of refresh token, but it probably wouldn't work. However, that shouldn't be that difficult to add by lib authors.