Contents

OAuth Header and Generating a Signature

For this examples we assume values for the respective tokens:

Every request to MKM's API need to include an Authorization header with an OAuth signature which looks like the following:

Authorization: OAuth realm="https://api.cardmarket.com/ws/v1.1/account",
                     oauth_consumer_key="bfaD9xOU0SXBhtBP",
                     oauth_token="lBY1xptUJ7ZJSK01x4fNwzw8kAe5b10Q",
                     oauth_nonce="53eb1f44909d6",
                     oauth_timestamp="1407917892",
                     oauth_signature_method="HMAC-SHA1",
                     oauth_version="1.0",
                     oauth_signature="DLGHHYV9OsbB/ARf73psEYaNWkI="

Attention: The realm parameter must be part of the Authorization header, although it is optional as per RFC.

As you see, you need to provide some (mandatory) parameters with the Authorization header:

The combination of oauth_timestamp and oauth_nonce must be unique for all your app's requests.

Authorization: OAuth realm="https://api.cardmarket.com/ws/v2.0/users/karmacrow/articles", 
                     oauth_consumer_key="bfaD9xOU0SXBhtBP", 
                     oauth_token="lBY1xptUJ7ZJSK01x4fNwzw8kAe5b10Q", 
                     oauth_nonce="59689e9cf4091", oauth_timestamp="1500028572", 
                     oauth_signature_method="HMAC-SHA1",
                     oauth_version="1.0", 
                     oauth_signature="r5g+nFelmTMffrrW+Y4se1u7Q/E="

1. Request Method and URI

Now on to generating the signature for the header. Generally you need to:

  1. Start you string with the request method which must be all uppercase.
  2. Append &.
  3. Append the Request URI which must be percent encoded.
  4. Append &.

Common Pitfall: If your request contains query string parameters (as in https://api.cardmarket.com/ws/v2.0/users/karmacrow/articles?start=0&maxResults=2), they must not be part of the value for the Authorization header's realm parameter, and must not be part of the request URI here while start building the base string for signature calculation.

// Attention: Don't include possible query parameters here!
$baseString = "GET&"
            . rawurlencode("https://api.cardmarket.com/ws/v1.1/account")
            . "&";

/*
 *  Results in:
 *  GET&https%3A%2F%2Fapi.cardmarket.com%2Fws%2Fv1.1%2Faccount&
 */

2. Parameters

You need to collect all parameters. Generally none of the MKM API's requests have designated parameters. But you need to include all parameters from the Authorization header except realm and oauth_signature.

Attention: With the API 2.0 we introduced query parameters, all of these need to be collected and used for the base string for signature calculation.

Common Pitfall: If your request contains query string parameters (as in https://api.cardmarket.com/ws/v2.0/users/karmacrow/articles?start=0&maxResults=2), they must be collected here in addition to all parameters from the Authorization header.

Percent encode all parameter names and values and sort them alphabetically:

Important: If the parameters are not sorted alphabetically, authorization will fail

  1. Concatenate each parameter name and its value with =
  2. Concatenate all parameter/value pairs using &
  3. Percent encode the parameter string and append it to the output string
$parameters = array();

// Attention: $encodedAndSortedParameters must include all possible query parameters!
foreach ($encodedAndSortedParameters as $name => $value)
{
    $parameters[] = $name . "=" . $value;
}
$parametersString = implode("&", $parameters);

/*
 *  Results in:
 *  oauth_consumer_key%3DbfaD9xOU0SXBhtBP%26oauth_nonce%3D53eb1f44909d6%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1407917892%26oauth_token%3DlBY1xptUJ7ZJSK01x4fNwzw8kAe5b10Q%26oauth_version%3D1.0
 */

$baseString .= rawurlencode($parametersString);

/*
 *  Results in:
 *  GET&https%3A%2F%2Fapi.cardmarket.com%2Fws%2Fv1.1%2Faccount&oauth_consumer_key%3DbfaD9xOU0SXBhtBP%26oauth_nonce%3D53eb1f44909d6%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1407917892%26oauth_token%3DlBY1xptUJ7ZJSK01x4fNwzw8kAe5b10Q%26oauth_version%3D1.0
 */

You now have the base string you need to encrypt.

Common Pitfall: If the request contains query parameters, they have to be part of this string and have been sorted alphabetically together with the rest of the parameters:

GET&https%3A%2F%2Fapi.cardmarket.com%2Fws%2Fv2.0%2Fusers%2Fkarmacrow%2Farticles&maxResults%3D2%26oauth_consumer_key%3DbfaD9xOU0SXBhtBP%26oauth_nonce%3D59689e9cf4091%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1500028572%26oauth_token%3DlBY1xptUJ7ZJSK01x4fNwzw8kAe5b10Q%26oauth_version%3D1.0%26start%3D0

3. Creating the signing key

The signing key to later encrypt the base string is the combination of your App Secret and the Access Token Secret (for the user).

  1. Percent encode the App Secret
  2. Append it by &
  3. Append it by the percent encoded Access Token Secret
$signingKey = rawurlencode($appSecret)
            . "&"
            . rawurlencode($accessTokenSecret);

/*
 *  Results in:
 *  pChvrpp6AEOEwxBIIUBOvWcRG3X9xL4Y&hc1wJAOX02pGGJK2uAv1ZOiwS7I9Tpoe
 */

Attention: There are use cases where you still don't have a Access Token Secret, or you simply don't need any.

You don't have an Access Token and Secret, but you have a Request Token

After successful login of your user at the /authenticate site you only have the Request Token to exchange it into Access Token and Secret. Your signing key doesn't include an Access Token Secret. In this case the signing key is only your percent encoded App Secret appended by the &. Only applicable for 3rd party apps!

You don't have both an Access Token and Secret

There are several public (non-protected) resources (mainly Marketplace requests). All these request are marked as public within this documentation. Also Widget Apps never have both an access token and secret.

In this case:

Attention: Dedicated apps always need both the Access Token and Secret, even for public resources.


Calculating the signature

At the end, the base string needs to be encrypted with the signature key and finally Base64 encoded.

$rawSignature = hash_hmac("sha1", $baseString, $signingKey, true);
$signature    = base64_encode($rawSignature);

/*
 *  Results in:
 *  163qUUcPtGFLxUzqeCIChErTbKU=
 */

Attention: Make sure the $rawSignature contains binary data (and not converted to a string). For that, the 4th parameter of the hash_hmac function must be set to true.