Google Seach Console API authorization issue with service account

333 views Asked by At

I'm trying to fetch data from Google search console (GSC) through http request.

I'm using google app Maker with javascript.

For my purpose I'm using a service account, all the scopes are already set up for the account.

I've copied the code provided by @Morfinismo.

/*********** SERVICE ACCOUNT CONFIGURATION USING THE OAUTH LIBRARY ***********
** All of the values are obtained from the .json file that is downloaded at
** the time of the service account creation
** Ref: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
** Ref: https://github.com/googlesamples/apps-script-oauth2
*/


var accessData = {
  "private_key" : "-----BEGIN PRIVATE KEY-----THE KEY-----END PRIVATE KEY-----\n",
  "client_email" : "searchconsolebot@project-id-xxxxxxxxxxxxxxx.iam.gserviceaccount.com",
  "user_email" : "[email protected]" // Why do we need a user mail ?
};


var scopes = ["https://www.googleapis.com/auth/webmasters", "https://www.googleapis.com/auth/webmasters.readonly"]; //GSC api scope
scopes = scopes.join(" "); //join all scopes into a space separated string

function getOAuthService(user) {
  console.log("je passe par getOAuthService");
  user = user || accessData.user_email;
  console.log("user: " + user);
  return OAuth2.createService("GSC_Service_Account")
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setPrivateKey(accessData.private_key)
    .setIssuer(accessData.client_email)
    .setSubject(user)
    .setPropertyStore(PropertiesService.getScriptProperties())
    .setCache(CacheService.getUserCache())
    .setParam('access_type', 'offline')
    .setScope(scopes);
}


function reset(user) {
  var service = getOAuthService(user);
  console.log("service: " + service);
  service.reset();
  return service;
}


function getToken(userEmail){
  var totoken = reset(userEmail).getAccessToken();
  console.log(totoken);
  return reset(userEmail).getAccessToken();
}


function getGCSUrlData(urlGiven){
  var token = getToken();
  if(token){
    var reqBody = {
      startDate: "2019-01-01",
      endDate: "2020-01-23"
    };
    var options = {
      method : 'POST',           
      headers : {
        Authorization : 'Bearer ' + token,
      },
      contentType: 'application/json',
      payload: JSON.stringify(reqBody),
      muteHttpExceptions: true,
    };
    var url = "https://www.googleapis.com/webmasters/v3/sites/" + encodeURIComponent(urlGiven) + "/searchAnalytics/query";
    var response = UrlFetchApp.fetch(url, options);
    console.log(response);
  }
}

Using the OAuth library seems really great but it does return me an error

Error: Access not granted or expired. at getToken (Service_Account_Config:46)

Also I noticed that getToken() method requires a param but when calling it we don't give any param is it normal ? And why do we need a user_email since we are using a service account ? Which email should I enter for the user_email then ?

I would really appreciate some help about this issue and any advice to understand this kind of issue.

Thanks a lot, Jacky

1

There are 1 answers

5
Morfinismo On

Integration with a service account is very easy when using the OAuth2 Library for AppsScript. Here are the steps:

1.) In a server side script add the following:

/*********** SERVICE ACCOUNT CONFIGURATION USING THE OAUTH LIBRARY ***********
** All of the values are obtained from the .json file that is downloaded at
** the time of the service account creation
** Ref: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
** Ref: https://github.com/googlesamples/apps-script-oauth2
*/

var accessData= {
  "private_key": "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----\n",
  "client_email": "[email protected]",
  "user_email": "[email protected]"
};

var scopes = ["https://www.googleapis.com/auth/webmasters", "https://www.googleapis.com/auth/webmasters.readonly"]; //drive api scope
scopes = scopes.join(" "); //join all scopes into a space separated string

function getOAuthService(user) {
  user = user || accessData.user_email;
  return OAuth2.createService("Service Account")
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setPrivateKey(accessData.private_key)
    .setIssuer(accessData.client_email)
    .setSubject(user)
    .setPropertyStore(PropertiesService.getScriptProperties())
    .setCache(CacheService.getUserCache())
    .setParam('access_type', 'offline')
    .setScope(scopes);
}


function reset(user) {
  var service = getOAuthService(user);
  service.reset();
  return service;
}


function getToken(userEmail){
  return reset(userEmail).getAccessToken();
}

Then you can simplye call the service you need by doing the following:

function getGCSUrlData(urlGiven){
  var token = getToken();
  if(token){
    var reqBody = {
      startDate: "2020-01-01",
      endDate: "2020-01-23"
    };
    var options = {
      method : 'POST',           
      headers : {
        Authorization : 'Bearer ' + token,
      },
      contentType: 'application/json',
      payload: JSON.stringify(reqBody),
      muteHttpExceptions: true,
    };
    var url = "https://www.googleapis.com/webmasters/v3/sites/" + encodeURIComponent(urlGiven) + "/searchAnalytics/query";
    var response = UrlFetchApp.fetch(url, options);
    console.log(response);
  }
}