Authentification
Une clé pour votre propre entreprise ; OAuth 2.0 pour une app que d'autres entreprises utilisent.
Deux façons
| Clé API | OAuth 2.0 | |
|---|---|---|
| Pour | Votre propre logiciel, votre propre entreprise | Une app tierce qu'un client autorise |
| Obtenue | Dans l'application, Développeur → Clés API | Par le flux d'autorisation ci-dessous |
| Jeton | aim_live_…, pas d'expiration | aim_at_… 60 minutes, renouvelé avec aim_rt_… 30 jours |
| Permissions sensibles | Accordées à la création | Approbation d'AIMERICA requise pour les apps tierces |
| Agit comme | La personne qui a créé la clé | La personne qui a cliqué Autoriser (ou l'entreprise, en client_credentials) |
Dans les deux cas le jeton voyage dans l'en-tête Authorization: Bearer …. Un jeton absent, inconnu, expiré ou révoqué répond 401 unauthorized ; un jeton valide sans la permission voulue répond 403 insufficient_scope avec l'en-tête WWW-Authenticate qui nomme la permission manquante.
OAuth 2.0 : code d'autorisation + PKCE
AIMERICA est le serveur d'autorisation. Enregistrez votre app dans l'application (Développeur → Apps) : vous obtenez un client_id (aim_app_…), pour une app confidentielle un client_secret affiché une fois, et vous déclarez vos adresses de retour exactes et les permissions que l'app peut demander. Une app publique (mobile, navigateur) n'a pas de secret et doit utiliser PKCE.
Your app The person's browser AIMERICA
─────── ──────────────────── ────────
1. make code_verifier,
code_challenge = S256(verifier)
2. send them to /oauth/authorize ─────────► a1merica.ai/voip/authorize
(sign in if needed, read the scopes,
Allow or Deny)
3. ◄──── 302 redirect_uri?code=…&state=… ────────────
4. POST /oauth/token (code + code_verifier) ─────────────────────────►
5. ◄──── access_token (60 min) + refresh_token (30 d)
6. Authorization: Bearer aim_at_… on every /v1 request
7. POST /oauth/token grant_type=refresh_token when expires_in runs out
1. Envoyer la personne s'autoriser
# Python: build the PKCE pair
import base64, hashlib, secrets
verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode()
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b"=").decode()
GET https://api.a1merica.ai/oauth/authorize
?response_type=code
&client_id=aim_app_1a2b3c4d5e6f
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&scope=users%3Aread%20calls%3Aread
&state=xyz
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
redirect_uri doit correspondre exactement à l'une des adresses enregistrées ; les scope demandés doivent faire partie de ceux de l'app ; state revient tel quel. La personne se connecte si nécessaire, voit l'app, ses permissions en phrases simples et l'entreprise concernée, puis Autoriser ou Refuser. En cas de refus : redirect_uri?error=access_denied&state=….
2. Échanger le code
Le code vaut 10 minutes et une seule fois. Le corps est encodé en formulaire (RFC 6749).
POST https://api.a1merica.ai/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=…
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&client_id=aim_app_1a2b3c4d5e6f
&code_verifier=… # public apps: PKCE instead of a secret
# confidential apps add client_secret=… (or HTTP Basic client_id:client_secret)
{
"access_token": "aim_at_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "aim_rt_…",
"refresh_token_expires_in": 2592000,
"scope": "users:read calls:read",
"org_id": "a1b2…",
"user_id": "9d8c…"
}
3. Renouveler
POST https://api.a1merica.ai/oauth/token
grant_type=refresh_token&refresh_token=aim_rt_…&client_id=aim_app_…
Le renouvellement fait tourner la paire : nouveau jeton d'accès, nouveau jeton de renouvellement. L'ancien jeton de renouvellement meurt dès que le nouveau jeton d'accès est utilisé une première fois, ou 60 secondes après — de quoi survivre à un réseau qui coupe entre la réponse et son enregistrement.
Identifiants client (sans personne)
Une app confidentielle peut agir pour l'entreprise qui l'a enregistrée, sans personne derrière : grant_type=client_credentials. Le jeton n'a pas de user_id ; les routes qui parlent de « moi » répondent 403.
POST https://api.a1merica.ai/oauth/token
grant_type=client_credentials&scope=calls%3Aread&client_id=aim_app_…&client_secret=…
Durées et révocation
| Jeton | Préfixe | Durée | Comment il meurt |
|---|---|---|---|
| Clé API | aim_live_ | Sans limite | Révoquée dans l'application (effet immédiat) |
| Code d'autorisation | — | 10 minutes, usage unique | Utilisé, ou expiré |
| Jeton d'accès | aim_at_ | 60 minutes | Expire ; POST /oauth/revoke ; la personne retire l'app ; l'app est suspendue |
| Jeton de renouvellement | aim_rt_ | 30 jours | Rotation à l'usage ; POST /oauth/revoke ; la personne retire l'app |
POST https://api.a1merica.ai/oauth/revoke
token=aim_rt_…&client_id=aim_app_…
POST /oauth/revoke (RFC 7009) accepte un jeton d'accès ou de renouvellement et répond 200 même s'il est déjà mort. POST /v1/oauth/introspect (authentifié par le client) dit si un jeton est actif, ses permissions, son entreprise et sa personne. Quand une personne retire une app dans l'application (Développeur → Apps → autorisations), tous ses jetons pour cette app cessent à l'instant : la prochaine requête répond 401.
Le point de terminaison /oauth/token répond aux erreurs à la façon de la RFC — {"error": "invalid_grant", "error_description": "…"} — parce que les bibliothèques OAuth s'y attendent ; partout ailleurs, c'est l'enveloppe AIMERICA.
HTTP/1.1 400 Bad Request
{"error": "invalid_grant", "error_description": "The code has expired or was already used"}
Ne stockez jamais un jeton ou une clé en clair dans un dépôt ou un journal. Nous ne gardons que des empreintes SHA-256 : une clé perdue ne peut pas être retrouvée, seulement remplacée.
