Aller au contenu

Authentification

Tous les endpoints /v2 exigent une signature de requête en HMAC-SHA256.

La clé API avec laquelle vous signez porte également des scopes (resource:action), le périmètre fin de la clé ; un endpoint répond 403 (code: INSUFFICIENT_SCOPE) si la clé n’a pas celui qu’il exige. Le catalogue des scopes se trouve dans la référence de l’API, sur Create an API key : c’est de la référence, et il évolue avec le produit.

Chaque requête doit comporter trois en-têtes :

En-tête Exemple
X-CowriePay-Key cpk_live_xxxxxxxxxxxxxxxxxxxx
X-CowriePay-Timestamp 1716825600 (horodatage Unix, en secondes)
X-CowriePay-Signature a1b2c3d4... (64 caractères hexadécimaux)

La signature est le HMAC-SHA256 d’une chaîne unique dont les parties sont jointes par des points littéraux (.) :

signing_string = TIMESTAMP + "." + METHOD + "." + PATH + "." + BODY_SHA256
signature = hex( HMAC_SHA256(secret, signing_string) )

Où :

  • TIMESTAMP, le même horodatage Unix (en secondes) que celui envoyé dans X-CowriePay-Timestamp
  • METHOD, la méthode HTTP en majuscules : GET, POST, PATCH, DELETE
  • PATH, la cible de la requête exactement telle qu’envoyée, préfixe /v2 et chaîne de requête compris. Par exemple /v2/wallets?page=2&status=ACTIVE
  • BODY_SHA256, le SHA-256 du corps brut de la requête, en hexadécimal minuscule. Pour une requête sans corps (GET, DELETE), utilisez le SHA-256 de la chaîne vide : e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

La signature est en hexadécimal minuscule et s’envoie telle quelle dans X-CowriePay-Signature, sans préfixe. Comme c’est le corps qui est haché, ni l’ordre des clés JSON ni les espaces n’ont d’importance : hachez exactement les octets que vous envoyez.

const crypto = require('crypto');
function sign(method, path, body, apiKeyId, apiSecret) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const bodyHash = crypto.createHash('sha256').update(body ?? '').digest('hex');
const signingString = [timestamp, method.toUpperCase(), path, bodyHash].join('.');
const signature = crypto.createHmac('sha256', apiSecret).update(signingString).digest('hex');
return {
'X-CowriePay-Key': apiKeyId,
'X-CowriePay-Timestamp': timestamp,
'X-CowriePay-Signature': signature,
};
}
// POST avec un corps : signez et envoyez LA MEME chaine.
const body = JSON.stringify({ chain: 'TRON', asset: 'USDT_TRON' });
const headers = sign('POST', '/v2/wallets', body, 'cpk_live_xxxxxxxxxxxxxxxxxxxx', 'your_secret');
// GET / DELETE, sans corps : passez '' (et incluez la chaine de requete dans le chemin) :
// sign('GET', '/v2/wallets?page=1&limit=20', '', 'cpk_live_xxx', 'your_secret');

Toute réponse d’erreur a la forme { "error": "<message>", "code": "<CODE_MACHINE>" }. Branchez-vous sur code, jamais sur le texte du message. Les codes propres à l’authentification :

code HTTP Cause Correctif
MISSING_AUTH_HEADERS 401 Un en-tête X-CowriePay-* obligatoire est absent Envoyez les trois en-têtes
TIMESTAMP_EXPIRED 401 Décalage d’horloge supérieur à 5 minutes Synchronisez l’horloge de votre serveur (NTP)
INVALID_SIGNATURE 401 Mauvais secret, ou chaîne de signature mal construite Journalisez la chaîne de signature exacte ; vérifiez que vous avez haché le corps brut et inclus /v2 ainsi que la chaîne de requête dans PATH
REPLAY_DETECTED 401 La même signature a été envoyée deux fois en moins de 5 minutes Utilisez un horodatage neuf à chaque requête
INVALID_API_KEY 401 Clé inconnue ou révoquée Vérifiez l’identifiant de la clé et qu’elle n’est pas révoquée
INSUFFICIENT_SCOPE 403 La clé n’a pas le scope requis Créez une clé portant le scope nécessaire