0
votes

Im building an app with google ads api using a service account auth flow (server to server).

The problem: the auth part is not working... I keep getting a 401 error.

Sample of the request:

const request = require('request');

....

request({
  'method': 'GET',
  'url': 'https://googleads.googleapis.com/v6/customers/XXXXXXXX',
  'headers': {
    'Authorization': 'Bearer XXXXXXXX',
    'developer-token': 'XXXXXXXX',
    'Content-Type': 'application/json'
  }
})

...

or

curl --location --request GET 'https://googleads.googleapis.com/v6/customers/XXXXXXXX' \
--header 'Authorization: Bearer XXXXXXXX' \
--header 'developer-token: XXXXXXXX' \
--header 'Content-Type: application/json'

[ERROR] Response from google ads api:

{
        "code": 401,
        "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
        "status": "UNAUTHENTICATED"
    }

I cant seem to find out why Im getting this authentication error.

What I have tried:

  1. Following this guide step by step -> https://developers.google.com/google-ads/api/docs/oauth/service-accounts
  2. Using the python library -> https://github.com/googleads/googleads-python-lib
  3. Using the google ads API with REST (no library)
  4. On localhost server
  5. On the production server (the www.g-suite.com domain linked to the account)

Whatever method or environment I try, it results in the same error.

Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential

Steps I have implemented:

  • Created a google ads manager account
  • Generated a valid developer token for that account
  • Created project in Google cloud platform
  • Opened a service account inside the project
  • Created a private key for the service account
  • Granting impersonation abilities in the G Suite domain for this scope: https://www.googleapis.com/auth/adwords
  • Enabled domain-wide delegation on the service account
  • Generated the access token with the key json file.

Generating the access token:

const { google } = require('googleapis');


const getAccessToken = async () => {
    const SCOPES = ['https://www.googleapis.com/auth/adwords'];

    const authClient = new google.auth.GoogleAuth({
        keyFile: './pathtokeyfile.json',
        scopes: SCOPES,
    });

    const token = await authClient.getAccessToken();

    return token;
};

I am surely missing something, but am not sure what it is..

Will be super grateful if someone can share a solution!! thanks champs ????

1
Show how you are creating the OAuth Access Token (the code). The token you are using is not valid. Did you also configure Domain Wide Delegation?John Hanley
Yes, I ticked on the domain-wide delegation on the service account as instructed in the tutorials. I created a separate answer for the access token codeRaymen
Delete your answer. Put that content in your question. To use a service account, you must also use impersonation. Your code does not impersonate a user. I do not code in node.js often (Python, c#, Go) otherwise I would post example code to show you how.John Hanley
Thanks for the help mate. Yep looks like I missed the part of impersonating a user.. the tutorials are very unclear. I am cool with python, if you have an example Ill be happy to see itRaymen
I have answered another question with examples in Python. A SO search should find it. My website also has articles on impersonation. jhanley.comJohn Hanley

1 Answers

0
votes

** NOT_ADS_USER SOLVED *** !!!

Now it's all working.

Turns out I was not creating the token precisely the correct way.

Here's a sample of the code I use to generate the correct access token in NodeJs:

    const private_key = require('./key.json').private_key;
    const token = jwt.sign(
    {
    iss:
    SERVICE_ACCOUNT_EMAIL,
    sub: GOOGLE_MMC_MANAGER_EMAIL,
    scope: 'https://www.googleapis.com/auth/adwords',
    aud: 'https://oauth2.googleapis.com/token',
    exp: Math.round(Date.now() / 1000) + 3600,
    iat: Math.round(Date.now() / 1000),
    },
    private_key,
    { algorithm: 'RS256' },
    );
    
    // console.log(token);
    
    try {
    const response = await http.post('https://oauth2.googleapis.com/token', {
    grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
    assertion: token,
    });
    const accesstoken = response.data;
    console.log(accesstoken);
    } catch (error) {
    console.log(error.data);
    }
    };

I was missing this part in signing the token: sub: GOOGLE_MMC_MANAGER_EMAIL,

GOOGLE_MMC_MANAGER_EMAIL is the email that you created the MMC account with.