Documentation

API & intégration

Sign in with Capibara (OAuth)

Mis à jour le 24 septembre 2026Plateforme v1 Markdown brut

Deux grants, deux usages

Capibara expose un fournisseur OAuth 2.0 maison (JWT HMAC-SHA256, sans
dépendance externe) avec deux grants réellement fonctionnels :

Grant Usage Nécessite un utilisateur ?
client_credentials Votre backend appelle l’API pour son propre compte (tâches de fond, synchronisation). non
authorization_code + PKCE « Se connecter avec Capibara » — un utilisateur autorise votre application à agir en son nom. oui, avec écran de consentement

Les deux grants émettent le même type de jeton : un JWT compact signé
HS256, valable 1 heure (expires_in: 3600). Ce jeton est opaque pour
vous
: vous pouvez le décoder pour en lire l’expiration, mais vous ne
pouvez pas vérifier sa signature (elle est scellée avec un secret serveur) —
utilisez-le tel quel comme Authorization: Bearer <token>, il n’y a ni
endpoint d’introspection ni JWKS public à ce stade.

Décodé, il porte typ: "access" (son usage), knd (client pour
client_credentials, user pour authorization_code), sub,
cid (votre client_id), scopes, iat et exp. Un jeton sans
typ (émis avant son introduction) est refusé : redemandez-en un.

Débit limité : POST /api/oauth/token accepte au plus 30 requêtes par
5 minutes par IP appelante
; au-delà, 429 avec un en-tête
Retry-After: 300.


Créer un client OAuth

Depuis /dev/keys, section « Clients OAuth » :

Champ Contrainte
Nom 2 à 80 caractères — affiché à l’utilisateur sur l’écran de consentement.
URLs de redirection jusqu’à 10, chacune une URL absolue en https:// (http:// accepté seulement vers localhost, 127.0.0.1 ou [::1], pour le développement), sans identifiants ni fragment #. Doivent correspondre exactement à l’URL utilisée dans la requête d’autorisation.
Scopes sous-ensemble de : crm:read, crm:write, shop:read, shop:write, billing:read, billing:write, site:read, media:read, projects:read, projects:write, planning:read, planning:write, formation:read (familles de module ; l’accès aux données d’une organisation exige en plus les scopes feuilles accordés à l’installation).

La création renvoie un client_id (cid_…) et un client_secret
(csec_…) affichés une seule fois — seul le hash du secret est
conservé côté serveur. Notez-les tout de suite dans votre gestionnaire de
secrets.


Grant client_credentials (serveur-à-serveur)

POST https://capibara.fr/api/oauth/token
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "cid_VOTRE_CLIENT_ID",
  "client_secret": "csec_VOTRE_CLIENT_SECRET",
  "scope": "crm:read shop:read"
}

Réponse (200) :

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "crm:read shop:read"
}

scope est optionnel dans la requête : si omis, le jeton reçoit tous les
scopes déjà accordés au client. S’il est fourni, seule l’intersection
entre les scopes demandés et ceux du client est accordée — demander un scope
que le client ne possède pas ne provoque pas d’erreur, il est simplement
absent du jeton final.

Le corps peut aussi être envoyé en application/x-www-form-urlencoded
(format historique OAuth) — les deux formats sont acceptés par l’endpoint.


Grant authorization_code + PKCE (connexion utilisateur)

PKCE (S256 uniquement — plain est refusé) est obligatoire, y
compris pour un client confidentiel : c’est la seule méthode de preuve de
possession acceptée par cet endpoint.

1. Générez un code_verifier et son code_challenge

const crypto = require('node:crypto');

const codeVerifier = crypto.randomBytes(32).toString('base64url');
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');

Conservez codeVerifier en mémoire côté serveur (associé à la session de
l’utilisateur qui démarre le flux) — vous en aurez besoin à l’étape 4.

2. Redirigez l’utilisateur vers l’autorisation

GET https://capibara.fr/api/oauth/authorize
  ?response_type=code
  &client_id=cid_VOTRE_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fmon-app.example%2Foauth%2Fcallback
  &scope=crm%3Aread%20shop%3Aread
  &state=UN_JETON_ANTI_CSRF_ALEATOIRE
  &code_challenge=LE_CODE_CHALLENGE_CALCULE
  &code_challenge_method=S256

Validations effectuées par cet endpoint avant de continuer : response_type
doit valoir code, client_id/redirect_uri/code_challenge sont
requis, code_challenge_method doit être S256, le client doit exister et
redirect_uri doit correspondre exactement à l’une de ses URLs
enregistrées. Les scopes accordés sont réduits à l’intersection avec ceux du
client, silencieusement (pas d’erreur si vous en demandez un de trop).

Tant que le client et son redirect_uri ne sont pas validés (client
inconnu, redirect_uri absent, non enregistré ou non sûr), la réponse est
un JSON 400 { "error": "invalid_client" | "invalid_request", "error_description": "…" },
sans redirection : Capibara ne renvoie jamais un utilisateur vers une
adresse que vous n’avez pas enregistrée. Une fois ce couple validé, les
autres erreurs repartent vers votre redirect_uri avec
?error=<code>&error_description=…&state=… — traitez donc error sur
votre callback.

Si tout est valide, l’utilisateur est redirigé vers l’écran de consentement
Capibara (/oauth/consent) — connecté d’abord si nécessaire.

3. L’utilisateur autorise (ou refuse)

L’écran de consentement affiche le nom de votre application, votre nom de
développeur, et la liste des scopes demandés en clair (ex. crm:read). Deux
issues :

  • Refus → redirection vers redirect_uri?error=access_denied&state=….
  • Acceptation → un code d’autorisation à usage unique est généré (valable
    10 minutes), et l’utilisateur est redirigé vers :
    redirect_uri?code=UN_CODE&state=…

4. Échangez le code contre un jeton

POST https://capibara.fr/api/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "LE_CODE_RECU_SUR_VOTRE_REDIRECT_URI",
  "client_id": "cid_VOTRE_CLIENT_ID",
  "redirect_uri": "https://mon-app.example/oauth/callback",
  "code_verifier": "LE_CODE_VERIFIER_DE_L_ETAPE_1"
}

Réponse (200) — identique en forme à celle du grant client_credentials :

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "crm:read shop:read"
}

Note. Cet échange ne demande pas client_secret : la preuve de
possession repose uniquement sur code_verifier (PKCE), ce qui permet à
ce même grant de fonctionner pour une application qui ne peut pas garder un
secret confidentiel (mobile, SPA). Le code est vérifié sur client_id,
redirect_uri (doit correspondre exactement à celui de l’étape 2) et
code_verifier (son empreinte SHA-256 doit correspondre au
code_challenge envoyé à l’étape 2) ; il est consommé au premier
échange réussi — le réutiliser renvoie invalid_grant.


Erreurs standard renvoyées

Sur POST /api/oauth/token (corps JSON { "error": "<code>" }) :

HTTP error Quand
400 invalid_request paramètre requis manquant (code, code_verifier, client_secret…).
401 invalid_client client_id/client_secret incorrects, ou client inconnu (grant client_credentials).
400 invalid_grant code d’autorisation expiré, déjà consommé, ou ne correspondant pas au client_id/redirect_uri/code_verifier fournis.
400 unsupported_grant_type grant_type ni client_credentials ni authorization_code.
400 unauthorized_client votre compte développeur est suspendu.
429 rate_limited plus de 30 requêtes/5 min depuis la même IP. En-tête Retry-After: 300.

Sur GET /api/oauth/authorize, en JSON 400, sans redirection (le
couple client + adresse n’est pas validé) :

error Quand
invalid_request client_id ou redirect_uri manquant ; redirect_uri non enregistré pour ce client, ou non sûr.
invalid_client client inconnu.

Puis en paramètres de la redirection vers votre redirect_uri :

error Quand
unsupported_response_type response_type ≠ code.
invalid_request code_challenge manquant ; code_challenge_method ≠ S256.
unauthorized_client votre compte développeur est suspendu.
access_denied l’utilisateur a refusé le consentement.

Utiliser le jeton obtenu

Le jeton du grant client_credentials s’utilise contre l’API
publique
, comme une clé d’API. Le jeton du grant
authorization_code (émis au nom d’un utilisateur) n’ouvre pas encore
l’API publique : ce qu’il donnera le droit de lire est en cours de
définition. Tout jeton est revérifié à chaque appel : un client supprimé ou
un compte développeur suspendu le rend inutilisable avant son expiration.

GET https://capibara.fr/api/public/v1/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…
Sign in with Capibara (OAuth)