Initial call to get product data from API is not working - cannot work out how to connect OAuth 2.0 to the call

I am trying to connect to the Google Shopping Products API to create some new product items. Before I do that more complex task, I am just trying to connect to get a list of the existing products.

I have setup a Service Account within Google API console and downloaded the json key file. I have saved the file to the server. I have then taken all of the examples from Google's documents and tried to piece them together.

$KEY_FILE_LOCATION - this is the location of the json service account key file.
$merchantid - this is the merchantid of my Google Merchant Centre


    $client->setApplicationName('Merchant Centre');
    echo "<br/><br/>client: ".json_encode($client);

    $url = "".$merchantid."/products";
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $data = curl_exec($ch);
    echo "<br/><br/>data: ".$data;

What I am having trouble with, is how to connect the authentication to the actual google shopping call. The connection to the content/v2/merchantid/products call returns this response: { "error": { "errors": [ { "domain": "global", "reason": "required", "message": "Login Required", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Login Required" } }

So how do I connect the oauth 2.0 authentication service account json to the actual call. I can't find anything in the documentation or online, about how you actually connect the two things together. I have integrations with other Google API's but none of the code I have provides a clear example of implementing this.

EDIT: Following the documentation through further I have managed to work out a flow that may work. I need to use JWT to obtain a token for the call to the API - the code below is being used to access the token, but it is still failing on the last section. The response from this call is { "error": "invalid_grant", "error_description": "Invalid JWT Signature." }. The signature section of the JWT is the only section that looks different to the example Google has given - my code outputs a signature of 43 characters whereas googles is significantly longer.

$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));

$iat = strtotime("now");
$exp = strtotime("+1 hour");
$currenttime = date("H:i:s");

$claimset = json_encode(['iss' => 'REDACTED', 
'scope' => '', 
'aud' => '', 
'exp' => $exp, 
'iat' => $iat]);
$base64UrlClaimSet = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claimset));
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlClaimSet, $privatekey, true);
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
$jwt = $base64UrlHeader . "." . $base64UrlClaimSet . "." . $base64UrlSignature;

$url = "";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=".$jwt);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$data = curl_exec($ch);
echo "<br/><br/>data: ".$data;

I am trying to follow the 'computing the signature' section of this

1 answer

  • answered 2019-07-17 08:45 eselskas

    401 means the request is unauthorized. You need to send an Authorization header with your request, it depends what kind of auth token the authorization requires (should be in the api docs). To set the header in PHP using cURL (for example bearer token):

    $header = array(
        'Authorization: Bearer <insert-token-here>'
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);