Signature des requêtes

Chaque requête de la porte Brave est authentifiée par trois en-têtes. Le secret ne transite jamais sur le réseau : seule une empreinte HMAC est envoyée.

Les trois en-têtes

  • Name
    X-Api-Key
    Type
    string
    Description

    Votre clé publique (ex. VOTRE_CLE_API).

  • Name
    X-Timestamp
    Type
    integer
    Description

    Horodatage Unix de la requête, en secondes. Tolérance de ± 5 minutes.

  • Name
    X-Signature
    Type
    string
    Description

    HMAC-SHA256 hexadécimal (minuscules) du payload MÉTHODE \n CHEMIN \n TIMESTAMP \n SHA256(CORPS).

Calcul de la signature

signingKey  = sha256("sig-v2|" + secret)    # le secret EN CLAIR, jamais envoyé
payload     = MÉTHODE-en-majuscules + "\n"
            + chemin + "\n"                 # tel que reçu, query incluse
            + timestamp + "\n"
            + sha256(corps brut)            # vide = chaîne vide
signature   = HMAC-SHA256(payload, signingKey)
  • signingKey se dérive de votre secret en clair (relecture sécurité n°2) : l'empreinte sha256(secret) stockée en base ne permet PAS de recalculer la signature. Gardez le secret confidentiel.
  • chemin : le chemin tel que la requête est envoyée, query string incluse si présente. Un paramètre de query ajouté, retiré ou modifié après signature invalide la requête.
  • corps brut : l'empreinte porte sur le corps exactement tel qu'il part sur le réseau. Pour un GET sans corps, hashez la chaîne vide. Si vous modifiez le JSON après signature (même un espace), la signature ne vaut plus.
  • Le timestamp est en secondes (pas en millisecondes), base 10, sans padding.
  • La signature est en hexadécimal minuscules (la casse de l'en-tête n'a pas d'importance côté vérification, mais une signature déjà utilisée est refusée : anti-rejeu).
  • Une même signature ne peut pas être rejouée (anti-rejeu) : chaque requête doit porter un horodatage différent ou un corps différent.

Exemples de calcul

API_KEY="VOTRE_CLE_API"    # votre clé publique
SECRET="VOTRE_SECRET"      # votre secret — jamais envoyé sur le réseau
METHOD="GET"
PATH_="/v1/1000/ping"      # chemin tel qu'envoyé, query incluse
BODY=""                    # corps brut exact (vide pour un GET sans corps)

SIGNING_KEY=$(printf '%s' "sig-v2|${SECRET}" | shasum -a 256 | cut -d' ' -f1)
BODY_HASH=$(printf '%s' "$BODY" | shasum -a 256 | cut -d' ' -f1)
TIMESTAMP=$(date +%s)
PAYLOAD=$(printf '%s\n%s\n%s\n%s' \
  "$(printf '%s' "$METHOD" | tr '[:lower:]' '[:upper:]')" \
  "$PATH_" "$TIMESTAMP" "$BODY_HASH")
SIGNATURE=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$SIGNING_KEY" | sed 's/^.*= //')

curl -s "https://partners.bravebooking.net${PATH_}" \
  -H "X-Api-Key: $API_KEY" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Signature: $SIGNATURE" \
  -H "Accept: application/json"

Pièges fréquents

  • Corps modifié après signature — tout changement du JSON (même un espace) change sha256(corps) → 401. Signez le corps exactement tel qu'il sera envoyé.
  • Query string oubliée — path inclut la query telle que partie : signez /v1/1000/bookings?from=…&to=…, pas seulement /v1/1000/bookings.
  • Méthode en minuscules — la méthode est normalisée en majuscules dans le payload (get et GET donnent la même signature).
  • Millisecondes au lieu de secondes — le timestamp est rejeté comme « trop ancien » (401).
  • Horloge désynchronisée — synchronisez votre serveur (NTP) : la tolérance est de 5 minutes.
  • Rejeu d'une requête — chaque signature n'est acceptée qu'une fois : dans un script, espacez les appels d'1 s (sleep 1) ou faites varier le corps.
  • Ancien schéma v1 — sha256(apiKey + secretHash + timestamp) est définitivement rejeté (401).
  • Ancienne dérivation d'empreinte — sha256(sha256(secret) + "|brave-gateway-signing-v1") n'est plus acceptée pour les clés créées depuis la relecture n°2 : dérivez la clé de sha256("sig-v2|" + secret).

Erreurs d'authentification

CodeSignification
401 missing_credentialsEn-tête X-Api-Key, X-Timestamp ou X-Signature absent
401 invalid_signatureSignature ne correspondant pas à la clé, au chemin, à la méthode ou au corps
401 stale_timestampHorodatage hors de la fenêtre de 5 minutes
429Trop d'échecs d'authentification depuis cette IP — réessayez après Retry-After secondes
403 ip_not_allowedIP source non présente dans la liste autorisée
403 key_revokedClé révoquée, ou porte/format non activé pour cette clé

Le détail des erreurs est sur la page Erreurs.

Cette page vous a-t-elle été utile ?