Authentification

Accès

Pour accéder à l’API Le Temps vous devez disposer d’une clé d’API.
Pour savoir comment obtenir une clé d’API rendez-vous sur cette page.

Une clé d’API est composée de deux éléments :

ÉlémentRôleConfidentiel
Identifiant d’accès (access_uuid)Identifie votre clé, il est transmis à chaque appelNon
Secret (secret)Sert à signer vos appels, il n’est jamais transmisOui

Le secret commence toujours par spyri_secret_. Conservez-le comme un mot de passe : ne le commitez pas dans un dépôt de code, ne l’envoyez pas par e-mail en clair, ne l’utilisez pas dans du code exécuté par un navigateur ou une application mobile.
Si vous pensez que votre secret a été exposé, contactez-nous pour qu’il soit régénéré.

Signer une requête

Chaque appel de l’API doit être signé avec votre secret (HMAC-SHA256).
La signature lie la requête à sa méthode, à son chemin, à ses paramètres et à son corps : une requête interceptée ne peut être ni modifiée, ni rejouée.

La signature est transmise dans un header Authorization unique, dont les paramètres sont séparés par des ; :

Authorization: HMAC-SHA256 access_uuid=VOTRE_ACCESS_UUID;time=TIMESTAMP;signature=SIGNATURE

Horodatage

TIMESTAMP est l’heure courante en timestamp Unix, en secondes (ex. 1762182000).

Chaîne à signer

Construisez une chaîne de quatre lignes séparées par un saut de ligne \n (sans saut de ligne final) :

MÉTHODE
CHEMIN
TIMESTAMP
EMPREINTE_DU_CORPS
  • MÉTHODE : la méthode HTTP en majuscules (GET, POST, PATCH…) ;
  • CHEMIN : le chemin de l’URL avec ses paramètres, exactement tel qu’il est envoyé, sans le nom de domaine (ex. /api/users?email=jeandupont%40gmail.com) ;
  • TIMESTAMP : la même valeur que celle du paramètre time du header ;
  • EMPREINTE_DU_CORPS : l’empreinte SHA-256 du corps brut de la requête, en hexadécimal minuscule. Pour une requête sans corps (un GET), c’est l’empreinte de la chaîne vide : e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.

Signature

SIGNATURE est le HMAC-SHA256 de cette chaîne, calculé avec votre secret, en hexadécimal minuscule.

⚠️
Le chemin est signé à l’octet près : signez la chaîne de paramètres telle que votre client HTTP l’envoie, encodage compris (%40 pour @, ordre des paramètres…). Le plus sûr est de construire vous-même l’URL encodée, puis de signer et d’envoyer cette même valeur.

Exemples

ACCESS_UUID="VOTRE_ACCESS_UUID"
SECRET="VOTRE_SECRET"
PATH_QUERY="/api/users?email=jeandupont%40gmail.com"
TIMESTAMP=$(date +%s)
BODY=""

BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf 'GET\n%s\n%s\n%s' "$PATH_QUERY" "$TIMESTAMP" "$BODY_HASH" \
  | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')

curl "https://www.letemps.ch$PATH_QUERY" \
  -H "Authorization: HMAC-SHA256 access_uuid=$ACCESS_UUID;time=$TIMESTAMP;signature=$SIGNATURE"
require "openssl"
require "net/http"
require "json"

access_uuid = "VOTRE_ACCESS_UUID"
secret = "VOTRE_SECRET"
path = "/api/users"
body = { user: { email: "jeandupont@gmail.com" } }.to_json
timestamp = Time.now.to_i.to_s

string_to_sign = ["POST", path, timestamp, OpenSSL::Digest::SHA256.hexdigest(body)].join("\n")
signature = OpenSSL::HMAC.hexdigest("SHA256", secret, string_to_sign)

request = Net::HTTP::Post.new(path)
request["Authorization"] = "HMAC-SHA256 access_uuid=#{access_uuid};time=#{timestamp};signature=#{signature}"
request["Content-Type"] = "application/json"
request.body = body
response = Net::HTTP.start("www.letemps.ch", 443, use_ssl: true) { |http| http.request(request) }

La gem spyri-api signe les requêtes pour vous depuis sa version 0.2.0 :

client = SpyriApi::Client.new(access_uuid: "VOTRE_ACCESS_UUID", secret: "VOTRE_SECRET", host: "https://www.letemps.ch/api")
SpyriApi::Users.new(client).search(email: "jeandupont@gmail.com")
import hashlib
import hmac
import json
import time

import requests

access_uuid = "VOTRE_ACCESS_UUID"
secret = "VOTRE_SECRET"
path = "/api/users"
body = json.dumps({"user": {"email": "jeandupont@gmail.com"}})
timestamp = str(int(time.time()))

string_to_sign = "\n".join(["POST", path, timestamp, hashlib.sha256(body.encode()).hexdigest()])
signature = hmac.new(secret.encode(), string_to_sign.encode(), hashlib.sha256).hexdigest()

response = requests.post(
    "https://www.letemps.ch" + path,
    data=body,  # la chaîne signée telle quelle : json=... la sérialiserait à nouveau
    headers={
        "Authorization": f"HMAC-SHA256 access_uuid={access_uuid};time={timestamp};signature={signature}",
        "Content-Type": "application/json",
    },
)
const crypto = require("node:crypto");

const accessUuid = "VOTRE_ACCESS_UUID";
const secret = "VOTRE_SECRET";
const path = "/api/users";
const body = JSON.stringify({ user: { email: "jeandupont@gmail.com" } });
const timestamp = Math.floor(Date.now() / 1000).toString();

const bodyHash = crypto.createHash("sha256").update(body).digest("hex");
const stringToSign = ["POST", path, timestamp, bodyHash].join("\n");
const signature = crypto.createHmac("sha256", secret).update(stringToSign).digest("hex");

const response = await fetch(`https://www.letemps.ch${path}`, {
  method: "POST",
  body,
  headers: {
    Authorization: `HMAC-SHA256 access_uuid=${accessUuid};time=${timestamp};signature=${signature}`,
    "Content-Type": "application/json",
  },
});

Postman

Enregistrez votre identifiant d’accès et votre secret dans deux variables d’environnement, api_access_uuid et api_secret, puis ajoutez ce script dans Scripts → Pre-request, de préférence au niveau de la collection pour qu’il s’applique à toutes les requêtes :

const secret = pm.environment.get("api_secret");
const timestamp = Math.floor(Date.now() / 1000).toString();
const method = pm.request.method.toUpperCase();
// Variables résolues : on signe exactement ce que Postman envoie
const path = pm.variables.replaceIn(pm.request.url.getPathWithQuery());
const body = pm.request.body && pm.request.body.mode === "raw" ? pm.variables.replaceIn(pm.request.body.raw) : "";

const stringToSign = [method, path, timestamp, CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex)].join("\n");
const signature = CryptoJS.HmacSHA256(stringToSign, secret).toString(CryptoJS.enc.Hex);

pm.request.headers.upsert({
  key: "Authorization",
  value: `HMAC-SHA256 access_uuid=${pm.environment.get("api_access_uuid")};time=${timestamp};signature=${signature}`
});
  • Réglez l’onglet Authorization sur No Auth, sinon Postman écrase le header défini par le script.
  • Envoyez les corps de requête en raw JSON (Content-Type: application/json) : un corps form-data ou x-www-form-urlencoded serait signé comme vide et refusé.

Validité et rejeu

  • L’horodatage doit être à moins de 5 minutes de l’heure de nos serveurs, dans un sens comme dans l’autre : gardez l’horloge de votre serveur synchronisée (NTP).
  • Une signature n’est acceptée qu’une seule fois. Pour relancer une requête, signez-la à nouveau. Deux requêtes strictement identiques envoyées dans la même seconde ont la même signature : la seconde est refusée.

Erreurs d’authentification

Un appel qui ne peut pas être authentifié répond par une erreur 401 et indique un code :

{
    "code": "signature_invalid"
}
codeSignification
api_key_invalidHeader Authorization absent, ou identifiant d’accès manquant ou inconnu
signature_missingParamètre time ou signature absent du header
signature_expiredHorodatage mal formé ou à plus de 5 minutes de l’heure de nos serveurs
signature_invalidLa signature ne correspond pas : mauvais secret, ou méthode / chemin / paramètres / corps différents de ce qui a été signé
signature_replayedCette signature a déjà été utilisée
legacy_authentication_disabledClé transmise en clair (ancienne méthode, voir ci-dessous) alors que votre clé n’accepte que les requêtes signées

Ancienne méthode (dépréciée)

Jusqu’ici, la clé d’API était transmise en clair dans le header Authorization :

Authorization: Bearer VOTRE_CLE_D_API

Cette méthode reste acceptée temporairement pour les clés créées avant l’introduction des requêtes signées, mais elle sera désactivée clé par clé : une clé transmise en clair peut être interceptée et réutilisée indéfiniment.
Si vous l’utilisez encore, demandez-nous votre identifiant d’accès et votre secret, et passez aux requêtes signées. Les nouvelles clés n’acceptent que les requêtes signées.

Permissions

Chaque clé d’API est configurée pour vous permettre d’accéder à certaines méthodes.
Par exemple votre clé peut vous permettre d’accéder à la recherche d’utilisateur mais pas à l’affichage d’une facture.
De même votre clé peut permettre - ou non - d’ajouter / modifier du contenu.
Si vous essayez d’appeler une méthode à laquelle vous n’avez pas accès vous obtiendrez une erreur 403 avec le code api_key_unauthorized et un message vous expliquant quel est le droit manquant :

{
    "code": "api_key_unauthorized",
    "details": "Il vous manque les droits suivants : XXX"
}