0

I am trying to send vacancies to the Google Indexing API whenever we receive them from our vacancy provider. However, despite having set all the appropriate permissions, I keep receiving 403 status codes.

I have followed the "Prerequisites for the Indexing API" guide and created a Service account. In my case, I have 4 entries for the domain in Google Search Console, so I followed MarcQuay's answer and added the service account to all 4 entries.

I use the following code to implement the Google API Client and make my calls. The method sendGoogleRequest() is called for every vacancy that we receive.

// Google API setup
function sendGoogleRequest(array $aData, int $sStatus = 0)
{
    global $DOMAIN, $GOOGLE_AUTH_CONFIG, $GOOGLE_AUTH_SCOPE, $GOOGLE_API_ENDPOINT;

    $googleClient = new Google_Client();
    $googleClient->useApplicationDefaultCredentials();
    $googleClient->addScope([$GOOGLE_AUTH_SCOPE]);

    // Update Google Indexing API. This will notify Google that the URL should be crawled again.
    $httpClient = $googleClient->authorize();
    $endpoint = $GOOGLE_API_ENDPOINT;

    $sJobUrl = $DOMAIN . '/vacancies/' . $aData['url'];
    $sType = "";

    if (!empty($sStatus)) {
        switch ($sStatus) {
            case 1:
                $sType = "URL_UPDATED";
                break;
            case 2:
                $sType = "URL_DELETED";
                break;
        }
    }

    $content = "{
            \"url\": \"$sJobUrl\",
            \"type\": \"$sType\"
    }";

    $response = $httpClient->post($endpoint, ['body' => $content]);
    $status_code = $response->getStatusCode();
    return $status_code;
}

I have tried debugging it and it seems that '$credentials' is empty in $googleClient->authorize()

$authHandler = $this->getAuthHandler();

if ($credentials) {
  $callback = $this->config['token_callback'];
  $http = $authHandler->attachCredentials($http, $credentials, $callback);
} elseif ($token) {
  $http = $authHandler->attachToken($http, $token, (array) $scopes);
} elseif ($key = $this->config['developer_key']) {
  $http = $authHandler->attachKey($http, $key);
}

return $http;

However I have no idea what could be the cause of this.

Using this code returns a '403' for every call. After having browsed the internet for quite some time now, I can seem to find a definite answer, except for 'make sure the service account is an owner' but as previously stated, this is already the case.

I hope someone can help me out, since I have been stuck on this for longer than I'd like to admit.

If any more information is required I'll gladly update my answer.

EDIT: As per request, here is the full error message return by the $httpClient->post() call.

Response {#268 ▼
  -reasonPhrase: "Forbidden"
  -statusCode: 403
  -headers: array:11 [▼
    "Vary" => array:3 [▼
      0 => "X-Origin"
      1 => "Referer"
      2 => "Origin,Accept-Encoding"
    ]
    "Content-Type" => array:1 [▼
      0 => "application/json; charset=UTF-8"
    ]
    "Date" => array:1 [▼
      0 => "Tue, 24 Sep 2019 11:25:29 GMT"
    ]
    "Server" => array:1 [▼
      0 => "ESF"
    ]
    "Cache-Control" => array:1 [▼
      0 => "private"
    ]
    "X-XSS-Protection" => array:1 [▼
      0 => "0"
    ]
    "X-Frame-Options" => array:1 [▼
      0 => "SAMEORIGIN"
    ]
    "X-Content-Type-Options" => array:1 [▼
      0 => "nosniff"
    ]
    "Alt-Svc" => array:1 [▼
      0 => "quic=":443"; ma=2592000; v="46,43,39""
    ]
    "Accept-Ranges" => array:1 [▼
      0 => "none"
    ]
    "Transfer-Encoding" => array:1 [▼
      0 => "chunked"
    ]
  ]
  -headerNames: array:11 [▼
    "vary" => "Vary"
    "content-type" => "Content-Type"
    "date" => "Date"
    "server" => "Server"
    "cache-control" => "Cache-Control"
    "x-xss-protection" => "X-XSS-Protection"
    "x-frame-options" => "X-Frame-Options"
    "x-content-type-options" => "X-Content-Type-Options"
    "alt-svc" => "Alt-Svc"
    "accept-ranges" => "Accept-Ranges"
    "transfer-encoding" => "Transfer-Encoding"
  ]
  -protocol: "1.1"
  -stream: Stream {#256 ▼
    -stream: stream resource @123 ▼
      wrapper_type: "PHP"
      stream_type: "TEMP"
      mode: "w+b"
      unread_bytes: 0
      seekable: true
      uri: "php://temp"
      options: []
    }
    -size: null
    -seekable: true
    -readable: true
    -writable: true
    -uri: "php://temp"
    -customMetadata: []
  }
}
Kevin F.
  • 140
  • 1
  • 10
  • Please edit the question and include the full error message – Linda Lawton - DaImTo Sep 24 '19 at 10:16
  • @DaImTo I have updated my post to include the error message – Kevin F. Sep 24 '19 at 11:27
  • Forbidden more than likely means you dont have permission. – Linda Lawton - DaImTo Sep 24 '19 at 11:29
  • @DaImTo I have set the service account as Owner for all of my domain entries. I do not understand what else I need to set to give the service account the correct permissions – Kevin F. Sep 24 '19 at 11:31
  • well if it works for a while then stops i wonder if your access token is expiring. – Linda Lawton - DaImTo Sep 24 '19 at 11:35
  • The problem is, it doesn't work at all, and has never worked for me. I shouldn't have to set the token myself if I use the Google API Client, correct? I would think that it would also be capable of refreshing a token in that case. – Kevin F. Sep 24 '19 at 11:37
  • Here is one of my older anwsers on this https://stackoverflow.com/a/52178138/1841839 this might help to just plug in your scope https://github.com/LindaLawton/Google-APIs-PHP-Samples/blob/master/Samples/Drive%20API/v3/ServiceAccount.php – Linda Lawton - DaImTo Sep 24 '19 at 11:43
  • @DaImTo Sadly, neither link proves useful. I have copied your ServiceAccount file, replaced the scope and json file and called 'getServiceAccountClient()' instead of 'new Google_Client()'. I still only get 403 status codes as a result. – Kevin F. Sep 24 '19 at 12:35

1 Answers1

0

I ended up fixing this issue by using a Service account as AuthConfig

// Initialize Google_Client to submit vacancies to Indexing API
$googleClient = new Google_Client();
$googleClient->setAuthConfig($GOOGLE_AUTH_CONFIG);
$googleClient->addScope([$GOOGLE_AUTH_SCOPE]);
$googleClient->setAccessType("offline");

$googleClient is used for every call

function sendGoogleRequest(array $aData, int $sStatus = 0, Google_Client $google_Client) {
    global $DOMAIN;
    $sJobUrl = 'https://MY-DOMAIN.com/' . $aData['url'];
    $sType = "";

    if (!empty($sStatus)) {
        switch ($sStatus) {
            case 1:
                $sType = "URL_UPDATED";
                break;
            case 2:
                $sType = "URL_DELETED";
                break;
         }

     }

     $urlNotification = new Google_Service_Indexing_UrlNotification();
     $urlNotification->setType($sType);
     $urlNotification->setUrl($sJobUrl);

     $indexingService = new Google_Service_Indexing($google_Client);
     $response = $indexingService->urlNotifications->publish($urlNotification);

     return $response;
 }

This script is then called once per day and posts every URL to the Indexing API. To not exceed the limit of 250 requests per day, make sure you exclude vacancies that should no longer be indexed.

Kevin F.
  • 140
  • 1
  • 10