Search code examples
phpopensslphpseclibwalmart-api

Walmart.io authentication issue - Could not authenticate in-request, auth signature


I am trying to link up with Walmart.io API to get some data from their resources. But I am stuck up in the first phase.

According to Walmart.io Quick Start Doc (https://walmart.io/docs/affiliate/quick-start-guide) I am supposed to follow following steps:

  1. Create an account with Walmart.io
  2. Create an application for Web Application
  3. Generate a certificate ( According to their guide there should be some feature to autogenerate the certificate, but I didn't find it)
  4. Upload public key to the application
  5. We will get consumer id and key version using which along with private key, we can make a request. We need to add additional headers that includes Signature and Timestamp too.

So, I did everything, but it still isn't working.

I am using Open SSL to generate private and public key as suggested by them: https://walmart.io/key-tutorial I tried avoiding -des3 so that it doesn't ask me for passphrase too, but it didn't work either.

Here is the script I tried with

curl --location --request GET 'https://developer.api.walmart.com/api-proxy/service/affil/product/v2/taxonomy' \
--header 'WM_SEC.KEY_VERSION: 2' \
--header 'WM_CONSUMER.ID: <Consumer_ID>' \
--header 'WM_CONSUMER.INTIMESTAMP: 1594389945813' \
--header 'WM_SEC.AUTH_SIGNATURE: W5PEHIew3LsnATk0zxJddeo416YEpMIjvk1b7lW9VMIZFx55erc/5df/FK9UtS5i48q057oASo0AX3SDd2hx+QSeyiX3FtLAgAgiZnGqQ6nJndySWgL5ih/GaUTXIC6dd048GFEZlC6axXdGoTWNzX9P0n/2DwLF9EtvMjdvjB1kum0z2xKz/lQGlvnjVkGK9sZdSUa5rfgxKSPi7ix+LRIJWYwt6mTKUlGz2vP1YjGcZ7gVwAs9o8iFC//0rHUWFwaEGrT0aZJtS7fvSFtKj5NRfemX4fwRO4cgBRxPWy9MRooQwXPmKxRP75PxHKTerv8X6HvRo0GdGut+2Krqxg==' \

And the response I get is

{
    "details": {
        "Description": "Could not authenticate in-request, auth signature :  Signature verification failed: affil-product, version: 2.0.0, env: prod",
        "wm_svc.version": "2.0.0",
        "wm_svc.name": "affil-product",
        "wm_svc.env": "prod"
    }
}

Hope someone gives me some insight into this problem.

Thanks in advance


Solution

  • Turns out it was issue with generated Signature (That explains why it worked after I changed the script.

    Thus here is the script that worked fine:

    <?php
    
    use GuzzleHttp\Psr7;
    use GuzzleHttp\Exception\RequestException;
    
    class Walmart{
    
        private $host;
    
        private $consumer_id;
    
        private $private_key_file;
    
        private $headers;
    
        private $sec_key_version;
    
        private $client;
    
        private $options;
    
        public function __construct($config){
            $this->host             = $config['host'];
            $this->consumer_id      = $config['consumer_id'];
            $this->private_key_file = $config['private_key_file'];
            $this->sec_key_version  = $config['sec_key_version'];
    
            $this->options = array();
            
            $this->client           = new GuzzleHttp\Client();
        }
        
        public function lookup_product($publisher_id='', $ids='', $upc='', $format='json'){
            $this->load_options();
    
            $url_params = array(
                'format' => $format,
            );
    
            if($publisher_id){
                $url_params['publisher_id'] = $publisher_id;
            }
    
            if($ids){
                $url_params['ids'] = $ids;
            }
    
            if($upc){
                $url_params['upc'] = $upc;
            }
    
            $query = http_build_query($url_params);
    
            $url = $this->host . '/product/v2/items?'.$query;
            try {
                $res = $this->client->request('GET', $url, $this->options);
                $body = $res->getBody();
                if($res->getStatusCode() == 200){
                    return $this->response(false, json_decode($body, true));
                }else{
                    return $this->response(array(
                        'title' => 'Unable to get products',
                        'stack' => $body,
                    ));
                }
            } catch (RequestException $e) {
                $err = Psr7\str($e->getRequest());
    
                if ($e->hasResponse()) {
                    $err .= Psr7\str($e->getResponse());
                }
    
                return $this->response(array(
                    'title' => 'Unable to get products',
                    'stack' => $err,
                ));
            }
        }
    
        private function load_options(){
            $timestamp = time()*1000;
            $this->options = array(
                'debug' => (defined("DEBUG") && DEBUG) ? true: false,
                'headers' => array(
                    'WM_SEC.KEY_VERSION'        => $this->sec_key_version,
                    'WM_CONSUMER.ID'            => $this->consumer_id,
                    'WM_CONSUMER.INTIMESTAMP'   => $timestamp,
                    'WM_SEC.AUTH_SIGNATURE'     => $this->get_signature($timestamp),
                )
            );
        }
    
        private function get_signature($timestamp){
    
            $message = $this->consumer_id."\n".$timestamp."\n".$this->sec_key_version."\n";
    
            $pkeyid = openssl_pkey_get_private("file://".$this->private_key_file);
    
            openssl_sign($message, $signature, $pkeyid, OPENSSL_ALGO_SHA256);
    
            $signature = base64_encode($signature);
    
            openssl_free_key($pkeyid);
    
            return $signature;
        }
    
        private function response($err, $data=false){
            return array(
                'error' => $err,
                'data' => $data,
            );
        }
    }
    

    Note: It uses guzzlehttp/guzzle library for HTTP Request