S'authentifier
L'API utilise le standard OAuth 2.0, dans sa forme la plus simple (« client credentials ») : vous échangez vos deux clés contre un jeton, puis vous envoyez ce jeton avec chaque requête. La plupart des outils (Postman, les bibliothèques Python, Node, PHP) savent le faire seuls.
Le principe en quatre lignes#
- Vous envoyez
client_id(votre clé d'accès) etclient_secret(votre code de sécurité) àPOST /auth/token. - Vous recevez un
access_tokenvalableexpires_insecondes (900, soit 15 minutes). - Vous l'envoyez dans l'en-tête
Authorization: Bearer <jeton>de chaque requête. - Quand il expire, vous en redemandez un.
curl -X POST "https://votre-entreprise.evyooht.com/api/v1/auth/token" \
-d "grant_type=client_credentials" \
-d "client_id=evy_ak_votre_cle" \
-d "client_secret=evy_sk_votre_code"$body = @{
grant_type = "client_credentials"
client_id = "evy_ak_votre_cle"
client_secret = "evy_sk_votre_code"
}
Invoke-RestMethod -Method Post -Uri "https://votre-entreprise.evyooht.com/api/v1/auth/token" -Body $bodyconst response = await fetch("https://votre-entreprise.evyooht.com/api/v1/auth/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "evy_ak_votre_cle",
client_secret: "evy_sk_votre_code",
}),
});
const data = await response.json();
console.log(data);$ch = curl_init("https://votre-entreprise.evyooht.com/api/v1/auth/token");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
"grant_type" => "client_credentials",
"client_id" => "evy_ak_votre_cle",
"client_secret" => "evy_sk_votre_code",
]),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);import requests
response = requests.post(
"https://votre-entreprise.evyooht.com/api/v1/auth/token",
data={"grant_type": "client_credentials", "client_id": "evy_ak_votre_cle", "client_secret": "evy_sk_votre_code"},
)
print(response.json())Demander moins de permissions#
Par défaut, le jeton reçoit toutes les permissions de votre clé. Vous pouvez en demander moins avec le champ facultatif scope, par exemple pour qu'un composant de votre système ne puisse lire que les produits :
curl -X POST "https://votre-entreprise.evyooht.com/api/v1/auth/token" \
-d "grant_type=client_credentials" \
-d "client_id=evy_ak_votre_cle" \
-d "client_secret=evy_sk_votre_code" \
-d "scope=products:read"$body = @{
grant_type = "client_credentials"
client_id = "evy_ak_votre_cle"
client_secret = "evy_sk_votre_code"
scope = "products:read"
}
Invoke-RestMethod -Method Post -Uri "https://votre-entreprise.evyooht.com/api/v1/auth/token" -Body $bodyconst response = await fetch("https://votre-entreprise.evyooht.com/api/v1/auth/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "evy_ak_votre_cle",
client_secret: "evy_sk_votre_code",
scope: "products:read",
}),
});
const data = await response.json();
console.log(data);$ch = curl_init("https://votre-entreprise.evyooht.com/api/v1/auth/token");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
"grant_type" => "client_credentials",
"client_id" => "evy_ak_votre_cle",
"client_secret" => "evy_sk_votre_code",
"scope" => "products:read",
]),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);import requests
response = requests.post(
"https://votre-entreprise.evyooht.com/api/v1/auth/token",
data={"grant_type": "client_credentials", "client_id": "evy_ak_votre_cle", "client_secret": "evy_sk_votre_code", "scope": "products:read"},
)
print(response.json())Demander une permission que votre clé n'a pas donne une erreur 400 invalid_scope.
Envoyer les clés dans l'en-tête#
Au lieu du corps de la requête, vous pouvez envoyer les deux clés dans l'en-tête Authorization: Basic … (le standard « HTTP Basic » : base64(clé d'accès:code de sécurité)). Si le corps contient les deux clés, il l'emporte.
Réutiliser son jeton#
C'est la règle la plus importante :
Demander un jeton à chaque requête marche, mais c'est inutile et vous heurterait à une limite : 10 demandes de jeton par minute et par adresse IP.
Que faire quand le jeton expire#
Vous recevez une erreur 401 avec le code invalid_token. La bonne conduite :
- Demander un nouveau jeton.
- Rejouer la requête une seule fois.
- Si le 401 persiste, c'est un problème de clés, pas de jeton : arrêtez et alertez.
Voici un gestionnaire de jeton complet, qui se renouvelle seul, dans trois langages :
// Gestionnaire de jeton : demande un jeton quand il manque ou qu'il est expiré, et le réutilise.
const BASE = "https://votre-entreprise.evyooht.com/api/v1";
let jeton = null;
let expireA = 0;
async function obtenirJeton() {
// Une marge de 30 secondes : on renouvelle un peu avant l'expiration.
if (jeton && Date.now() < expireA - 30000) return jeton;
const reponse = await fetch(`${BASE}/auth/token`, {
method: "POST",
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.EVYOO_CLIENT_ID,
client_secret: process.env.EVYOO_CLIENT_SECRET,
}),
});
if (!reponse.ok) throw new Error(`Authentification refusée (${reponse.status})`);
const donnees = await reponse.json();
jeton = donnees.access_token;
expireA = Date.now() + donnees.expires_in * 1000;
return jeton;
}
async function lire(chemin) {
let reponse = await fetch(`${BASE}${chemin}`, { headers: { Authorization: `Bearer ${await obtenirJeton()}` } });
if (reponse.status === 401) {
jeton = null; // le jeton a été refusé : on en redemande un, une seule fois
reponse = await fetch(`${BASE}${chemin}`, { headers: { Authorization: `Bearer ${await obtenirJeton()}` } });
}
return reponse.json();
}<?php
// Gestionnaire de jeton : demande un jeton quand il manque ou qu'il est expiré, et le réutilise.
final class EvyooClient
{
private ?string $jeton = null;
private int $expireA = 0;
public function __construct(private string $hote, private string $cle, private string $code)
{
}
private function jeton(): string
{
// Une marge de 30 secondes : on renouvelle un peu avant l'expiration.
if ($this->jeton !== null && time() < $this->expireA - 30) {
return $this->jeton;
}
$ch = curl_init("https://{$this->hote}/api/v1/auth/token");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'client_credentials',
'client_id' => $this->cle,
'client_secret' => $this->code,
]),
]);
$donnees = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) {
throw new RuntimeException('Authentification refusée');
}
curl_close($ch);
$this->jeton = $donnees['access_token'];
$this->expireA = time() + $donnees['expires_in'];
return $this->jeton;
}
public function lire(string $chemin, bool $deuxiemeEssai = false): array
{
$ch = curl_init("https://{$this->hote}/api/v1{$chemin}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $this->jeton()],
]);
$corps = curl_exec($ch);
$statut = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statut === 401 && !$deuxiemeEssai) {
$this->jeton = null; // refusé : on en redemande un, une seule fois
return $this->lire($chemin, true);
}
return json_decode($corps, true);
}
}# Gestionnaire de jeton : demande un jeton quand il manque ou qu'il est expiré, et le réutilise.
import os
import time
import requests
BASE = "https://votre-entreprise.evyooht.com/api/v1"
class EvyooClient:
def __init__(self):
self.jeton = None
self.expire_a = 0.0
def _jeton(self):
# Une marge de 30 secondes : on renouvelle un peu avant l'expiration.
if self.jeton and time.time() < self.expire_a - 30:
return self.jeton
reponse = requests.post(f"{BASE}/auth/token", data={
"grant_type": "client_credentials",
"client_id": os.environ["EVYOO_CLIENT_ID"],
"client_secret": os.environ["EVYOO_CLIENT_SECRET"],
})
reponse.raise_for_status()
donnees = reponse.json()
self.jeton = donnees["access_token"]
self.expire_a = time.time() + donnees["expires_in"]
return self.jeton
def lire(self, chemin, params=None):
for essai in range(2):
reponse = requests.get(f"{BASE}{chemin}", params=params,
headers={"Authorization": f"Bearer {self._jeton()}"})
if reponse.status_code != 401:
break
self.jeton = None # refusé : on en redemande un, une seule fois
return reponse.json()Les clés sont lues dans des variables d'environnement (EVYOO_CLIENT_ID, EVYOO_CLIENT_SECRET) : elles ne sont jamais écrites dans le code.
Les erreurs de l'authentification#
Sur /auth/token, les erreurs suivent le format normalisé d'OAuth : {"error": "…", "error_description": "…"}.
| Statut | error | Cause |
|---|---|---|
| 400 | invalid_request | Corps illisible, ou grant_type absent. |
| 400 | unsupported_grant_type | grant_type n'est pas client_credentials. |
| 400 | invalid_scope | Vous demandez une permission que votre clé n'a pas. |
| 401 | invalid_client | Clés absentes ou incorrectes. |
| 429 | rate_limited | Plus de 10 demandes de jeton par minute depuis cette adresse IP. |
| 429 | too_many_attempts | Trop d'échecs : vous êtes bloqué. L'en-tête Retry-After donne la durée. |
Le blocage#
Après 5 échecs avec la même clé depuis la même adresse, ou 20 échecs depuis une même adresse (toutes clés confondues), vous êtes bloqué 15 minutes : 429 too_many_attempts. Pendant le blocage, même les bonnes clés sont refusées. Un client qui boucle avec un mauvais code s'arrête ainsi tout seul.
Perdre ou changer son code de sécurité#
Le code ne se retrouve pas : on en crée un nouveau. L'administrateur le régénère ; l'ancien code et tous les jetons en cours cessent de marcher aussitôt. Faites de même si vous pensez que le code a fuité.