phpauthenticationcurloauthdiscogs-api

How do I authenticate a POST to the Discogs API with PHP / cURL


I've successfully used the example here https://gist.github.com/tonefolder/44191a29454f9059c7e6 to authenticate a user and to store the oauth_token and oauth_token_secret. I can then make authenticated GET requests using cURL.

I don't know how to make authenticated POST requests though.

I have tried using the $result['header'] from the signed oauthObject in this way:

$discogs_username = "'XXXX'";

$result = mysql_query("SELECT * FROM discogs WHERE username = $discogs_username;", $link);
if (mysql_num_rows($result)) {
    $discogs_details = mysql_fetch_array($result, MYSQL_ASSOC);
}


$signatures = array(
    'consumer_key'     => 'MyKey',
    'shared_secret'    => 'MySecret',
  'oauth_token'    => $discogs_details['oauth_token'],
  'oauth_token_secret'    => $discogs_details['oauth_token_secret']
);


$jsonquery = json_encode(array(
        'release_id' => '7608939',
        'price' => '18.00',
        'condition' => 'Mint',
        'sleeve_condition' => 'Mint',
        'status' => 'For Sale'
    )
);


$result = $oauthObject->sign(array(
        'path'      => $scope.'/marketplace/listings',
        'signatures'=> $signatures
    )
);


$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, 'MyDiscogsClient/0.1 +http://me.com');
curl_setopt($ch, CURLOPT_URL, $result['signed_url']);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json','Authorization:' . $result['header'])); 
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonquery);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = json_decode(curl_exec($ch), true);
curl_close($ch);

But I get '[message] => You must authenticate to access this resource.'

I'm not sure if i'm doing the CURLOPT_POSTFIELDS part correctly, but I can figure that out once i'm actually able to send an authenticated POST! Sorry if there is also loads more wrong with this - I don't use cURL that often!


Solution

  • Background

    After reproducing all your steps and fiddling around a bit, I found the following. As a side node, I presumed that the $scope variable just contains the API url, namely https://api.discogs.com.

    As it turns out, signing the OAuth request does also need to know the proper request method you are going to use. You can find that by var_dump()ing the $result you are getting from the sign() call:

    array(5) {
    /// ... snip ...
        'sbs' =>
      string(269) "GET&https%3A%2F%2Fapi.discogs.com%2Fmarketplace%2Flistings&oauth_consumer_key%XXXXX%26oauth_nonce%3DMOtWK%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1453937109%26oauth_token%XXXXX%26oauth_version%3D1.0"
    }
    

    So I digged a bit into the code of the "OAuthSimple" library that you are using, and it turns out you can just pass the method under an index called "action".

    Solution

    Based on the findings, you will have to change this:

    $result = $oauthObject->sign(
        array(
            'path' => $scope . '/marketplace/listings',
            'signatures' => $signatures,
        )
    );
    

    to this:

    $result = $oauthObject->sign(
        array(
            'path' => $scope . '/marketplace/listings',
            'signatures' => $signatures,
            'action' => 'POST',
        )
    );
    

    Side notes

    I actually tried that solution and was able to create a listing through the API. The only other change I had to make is the condition and sleeve_condition values Mint are not allowed by their API so you actually have to use Mint (M).

    Also, I got an error at first that said I have to complete my "seller settings". So you might want to catch that case in your application.

    Further thoughts

    As you mentioned in the comments to your original post, you presume that the suggested library just allows GET requests also. However, as you can see here, it just returns a Guzzle Client, so firing POST requests via this should not be a problem at all, and actually using this would probably save you a lot of pain. I did however not try that, so, just as thought.

    Hope that helps :)