API FlavosoFondamentauxAuthentification
Authentification
OAuth 2.0 avec PKCE : comment un propriétaire connecte votre application à son établissement, et comment vous échangez, rafraîchissez et révoquez les jetons.
Vue d'ensemble
Une application n'obtient accès à un établissement qu'avec le consentement de son propriétaire. Flavoso utilise pour cela OAuth 2.0 avec code d'autorisation (RFC 6749) et PKCE avec S256 (RFC 7636), ainsi que le paramètre iss au retour (RFC 9207), la révocation selon la RFC 7009 et les métadonnées selon la RFC 8414.
- 1
Votre application
« Connecter avec Flavoso »
Votre application crée state et un code_verifier et envoie le navigateur vers /oauth/authorize.
- 2
Flavoso
S'identifier
Le propriétaire s'identifie avec son compte Flavoso, avec un second facteur s'il en a configuré un.
- 3
Flavoso
Consentement
Il choisit l'établissement, lit le nom, le site web et les droits de votre application et clique sur « Connecter ».
- 4
Navigateur
Retour avec un code
Flavoso redirige vers votre redirect_uri avec code, state et iss. Le code est valable 10 minutes, et une seule fois.
- 5
Votre serveur
Échange
Votre serveur échange code et code_verifier contre un jeton d'accès et un jeton de rafraîchissement.
- 6
Votre serveur
Requêtes
Appelez l'API avec le jeton d'accès (1 heure) ; le jeton de rafraîchissement permet d'en obtenir de nouveaux.
| Point de terminaison | Adresse |
|---|---|
| Autorisation (navigateur) | https://app.flavoso.com/oauth/authorize |
| Jeton | POST https://api.flavoso.com/oauth/token |
| Révocation | POST https://api.flavoso.com/oauth/revoke |
| Métadonnées | https://api.flavoso.com/.well-known/oauth-authorization-server |
L'autorisation est une page sur app.flavoso.com, car c'est là que le propriétaire s'identifie. Le jeton, la révocation et les métadonnées se trouvent sur https://api.flavoso.com. Les points de terminaison et tous leurs paramètres figurent aussi dans la référence.
Clients confidentiels et publics
Vous choisissez le type de client lors de la création de l'application :
- Confidentiel (
confidential) : pour les applications dotées de leur propre serveur. Elles reçoivent un client secret (flv_cs_…), qui ne s'affiche qu'une seule fois et n'a sa place que sur le serveur. Auprès du point de terminaison des jetons, elles s'authentifient par HTTP Basic ou envoientclient_idetclient_secretdans le corps. Les vrais établissements ne les connectent que via une URI de redirectionhttps. - Public (
public) : pour les applications qui ne peuvent pas garder un secret en sécurité, par exemple une application de caisse sur une tablette ou un programme sur l'ordinateur de l'établissement. Elles ne reçoivent pas de secret et n'envoient queclient_id. Elles peuvent utiliser des adresses loopback et un schéma personnalisé (RFC 8252), y compris pour de vrais établissements.
PKCE avec S256 est obligatoire pour les deux. Le point de terminaison des jetons n'accepte aucun appel depuis un navigateur (pas de CORS) ; une application web échange donc le code et le jeton de rafraîchissement sur son serveur, ce qui en fait un client confidentiel.
Le client ID (flv_app_…) n'est pas secret. « Créer un nouveau client secret » dans la console remplace le client secret ; l'ancien reste valable 24 heures, le temps de basculer sans interruption.
URI de redirection
Après le consentement, Flavoso renvoie le navigateur vers l'une des URI de redirection de votre application. Les règles sont les suivantes :
- Flavoso compare l'adresse caractère par caractère avec les adresses enregistrées. Seule exception : le port des adresses loopback.
https, sans identifiants dans l'adresse. Les vrais établissements connectent toute application par ce biais.- Loopback (
http://127.0.0.1:<port>/…,http://localhost:<port>/…,http://[::1]:<port>/…, n'importe quel port, RFC 8252) et schémas personnalisés contenant un point (com.example.pos:/callback) : pour une application publique, avec n'importe quel établissement ; pour une application confidentielle, uniquement avec votre propre établissement de test. L'écran de consentement ne propose alors que celui-ci. - Pas de fragment (
#), 300 caractères au maximum, 10 adresses au maximum par application.
Le consentement
Sur l'écran de consentement, l'utilisateur s'identifie avec son compte Flavoso ; s'il a configuré un second facteur, celui-ci lui est demandé. Il choisit ensuite l'établissement, voit le logo, le nom et le site web de votre application ainsi que les droits demandés en clair, et clique sur « Connecter » ou « Annuler ». Les applications validées y portent la mention « Vérifiée par Flavoso ».
- Seuls les propriétaires peuvent connecter une application. Une personne ayant un autre rôle dans l'établissement ne le voit pas dans la liste.
- Avant la validation, vous seul pouvez connecter l'application : à votre établissement de test et aux établissements dont vous êtes propriétaire. Via une adresse loopback ou un schéma personnalisé, une application confidentielle ne se connecte qu'à votre établissement de test, même après la validation.
- Tous les propriétaires de l'établissement reçoivent un e-mail après la connexion : quelle application, quel éditeur, quels droits, qui et quand. De même lorsqu'une connexion existante obtient davantage de droits.
- Connecter à nouveau remplace les droits de la connexion existante, à la hausse comme à la baisse. Votre application reçoit alors
app.scopes_changedavec les anciens droits.
Le retour
Après « Connecter », Flavoso redirige vers votre redirect_uri avec code, state et iss. Vérifiez que state correspond à la valeur envoyée et que iss vaut exactement https://api.flavoso.com, l'issuer des métadonnées (même si le consentement a eu lieu sur app.flavoso.com). Ensuite seulement, échangez le code.
Le code est valable 10 minutes et une seule fois. S'il est utilisé une seconde fois, Flavoso révoque tout ce qui a été délivré avec lui.
Avec un client_id inconnu, une redirect_uri non enregistrée ou des paramètres en double, Flavoso affiche une page d'erreur et ne redirige pas. Les autres erreurs reviennent sous forme de redirection vers la redirect_uri, avec error, error_description, state et iss, mais seulement pour une application validée ou si vous êtes identifié comme son développeur. Sinon, Flavoso affiche lui-même l'erreur ; une application refusée ou bloquée n'obtient jamais de redirection. error peut prendre ces valeurs :
| error | Signification |
|---|---|
access_denied | L'utilisateur a cliqué sur « Annuler », ou il n'a pas le droit de connecter cette application à l'établissement choisi : par exemple parce qu'il n'en est pas propriétaire, ou parce qu'une application confidentielle ne peut se connecter qu'à votre établissement de test via une adresse loopback ou un schéma personnalisé. |
invalid_request | Un paramètre manque ou est invalide, par exemple state ou le code_challenge. |
invalid_scope | Un droit demandé n'existe pas, ou votre application n'a pas le droit de le demander. |
unsupported_response_type | response_type n'est pas code. |
server_error | Un problème est survenu du côté de Flavoso, par exemple la connexion n'a pas pu être enregistrée. Réessayez plus tard. |
Échanger le code contre des jetons
Pour échanger le code, envoyez un POST à https://api.flavoso.com/oauth/token, avec un corps en application/x-www-form-urlencoded : grant_type=authorization_code avec code, redirect_uri (la même que lors de l'autorisation) et code_verifier. Les clients confidentiels s'authentifient par HTTP Basic, les clients publics envoient client_id.
# Confidential client: HTTP Basic (or client_id and client_secret in the body)
curl https://api.flavoso.com/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code=AUTHORIZATION_CODE \
--data-urlencode redirect_uri=https://example.com/flavoso/callback \
-d code_verifier=$CODE_VERIFIER
# Public client (native or desktop app): client_id only, no secret
curl https://api.flavoso.com/oauth/token \
-d grant_type=authorization_code \
-d code=AUTHORIZATION_CODE \
--data-urlencode redirect_uri=com.example.pos:/callback \
-d code_verifier=$CODE_VERIFIER \
-d client_id=$CLIENT_IDLa réponse contient les deux jetons, les droits accordés, l'identifiant de la connexion et l'établissement :
{
"access_token": "flv_at_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "flv_rt_...",
"scope": "orders:write menu:write store:write reports:read",
"connection_id": "4b3f7c1e-9a2d-4e8b-b5f6-0c1d2e3f4a5b",
"tenant": {
"id": "7d0e5b8a-3c41-4f2e-9a6b-1e2d3c4b5a69",
"name": "Pizzeria Example",
"slug": "pizzeria-example"
}
}Enregistrez les jetons par connexion, par exemple sous tenant.id. Un jeton n'est valable que pour un seul établissement ; chaque établissement supplémentaire obtient sa propre connexion avec ses propres jetons.
Jetons et durées de validité
| Élément | Format | Validité |
|---|---|---|
| Code d'autorisation | flv_ac_… | 10 minutes, une seule fois |
| Jeton d'accès | flv_at_… | 1 heure (expires_in: 3600) |
| Jeton de rafraîchissement | flv_rt_… | 180 jours à compter de la dernière utilisation, remplacé à chaque utilisation |
| Client secret | flv_cs_… | jusqu'au prochain « Créer un nouveau client secret », l'ancien reste alors valable 24 heures |
Traitez les jetons comme des mots de passe : enregistrez-les chiffrés, ne les écrivez jamais dans des journaux, ne les placez jamais dans un navigateur ni dans une adresse.
Rafraîchir avec rotation
Vous obtenez un nouveau jeton d'accès avec grant_type=refresh_token et refresh_token. La réponse contient aussi un nouveau jeton de rafraîchissement ; l'ancien est alors consommé. Enregistrez le nouveau avant d'utiliser le nouveau jeton d'accès.
Si la réponse se perd en route et que vous renvoyez le même jeton de rafraîchissement dans les 60 secondes, vous obtenez une nouvelle paire, mais seulement tant que le jeton de rafraîchissement de la réponse perdue n'a pas encore servi. Flavoso révoque alors la paire de la réponse perdue. Dans tous les autres cas, une seconde utilisation est considérée comme un vol : Flavoso révoque toute la famille, c'est-à-dire tous les jetons issus du même consentement, et le propriétaire doit connecter l'application à nouveau.
Avec scope, vous restreignez le nouveau jeton d'accès à moins de droits, par exemple pour une partie de votre système qui ne fait que lire. Le jeton de rafraîchissement conserve tous les droits de la connexion.
// A valid access token for one connection. tokenRequest() as above.
// Refresh at one place at a time per connection, and always store the NEW refresh token.
export async function accessTokenFor(conn) {
if (Date.now() < conn.expiresAt - 60_000) return conn.accessToken;
try {
const t = await tokenRequest({ grant_type: "refresh_token", refresh_token: conn.refreshToken });
conn.accessToken = t.access_token;
conn.refreshToken = t.refresh_token; // the old one is used up
conn.expiresAt = Date.now() + t.expires_in * 1000;
await saveConnection(conn); // persist BEFORE using the new token
return conn.accessToken;
} catch (err) {
if (err.code === "invalid_grant") await markDisconnected(conn); // the owner has to connect again
if (err.code === "unauthorized_client") await markSuspended(conn); // app suspended: keep the tokens, retry later
throw err;
}
}Révoquer et déconnecter
POST https://api.flavoso.com/oauth/revoke avec token révoque un jeton (RFC 7009) ; vous pouvez envoyer token_type_hint, mais Flavoso reconnaît le type à la forme du jeton. Le client s'authentifie comme auprès du point de terminaison des jetons. Sans token, Flavoso répond 400 invalid_request ; si l'authentification échoue, 401 invalid_client. Sinon, la réponse est toujours 200, y compris pour un jeton inconnu ou déjà révoqué. Révoquer un jeton de rafraîchissement révoque toute sa famille ; un jeton d'accès, seulement lui-même. La connexion subsiste.
Pour mettre fin à la connexion avec l'établissement, votre application appelle DELETE /connection. Plus aucun jeton de cette connexion ne fonctionne alors, les propriétaires reçoivent un e-mail, et votre webhook reçoit app.disconnected. Pour la même application et le même établissement, un nouveau consentement est ensuite nécessaire.
# Revoke a token (RFC 7009). The client authenticates as at the token endpoint;
# after that the answer is always 200. A refresh token takes its whole family with it.
curl https://api.flavoso.com/oauth/revoke \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d token=$REFRESH_TOKEN \
-d token_type_hint=refresh_token
# End the connection to the restaurant: every token of it stops working, the owners are notified
curl -X DELETE https://api.flavoso.com/v1/connection \
-H "Authorization: Bearer $ACCESS_TOKEN"Métadonnées
Les adresses et les méthodes prises en charge sont disponibles sous une forme lisible par machine à https://api.flavoso.com/.well-known/oauth-authorization-server (RFC 8414). De nombreuses bibliothèques OAuth les lisent automatiquement.
curl https://api.flavoso.com/.well-known/oauth-authorization-serverErreurs du point de terminaison des jetons
Le point de terminaison des jetons répond selon la RFC 6749 avec error et error_description, toujours avec Cache-Control: no-store :
| error | HTTP | Signification |
|---|---|---|
invalid_request | 400 | Un paramètre manque, est répété ou est invalide. |
invalid_client | 401 | L'authentification du client a échoué, avec WWW-Authenticate. |
invalid_grant | 400 | Le code ou le jeton de rafraîchissement est invalide, expiré, déjà utilisé ou révoqué, ou redirect_uri ou code_verifier ne correspond pas. Lors d'un rafraîchissement, cela signifie : la connexion n'existe plus. |
unauthorized_client | 400 | L'application est bloquée. Ses connexions subsistent : conservez les jetons et réessayez plus tard. |
unsupported_grant_type | 400 | Ce grant_type n'existe pas. |
invalid_scope | 400 | Un droit est invalide ou dépasse ceux de la connexion. |
temporarily_unavailable | 429 | Trop de requêtes ; Retry-After indique le délai d'attente en secondes. |
server_error | 500 | Un problème est survenu du côté de Flavoso. |
{
"error": "invalid_grant",
"error_description": "The authorization code is invalid, expired or already used."
}Plusieurs établissements
Un système de caisse utilisé par de nombreux établissements a de nombreuses connexions. Chacune a ses propres jetons, sa propre limite de requêtes et son connection_id. Le webhook de votre application reçoit les événements de tous les établissements ; tenant_id et connection_id dans l'enveloppe indiquent à quel établissement chacun appartient.
Un établissement qui connecte votre application une seconde fois conserve la même connexion, avec les nouveaux droits. Après une déconnexion, la prochaine autorisation crée une nouvelle connexion avec un nouveau connection_id.
Une question reste ouverte ? Écrivez à support@flavoso.com en indiquant l'identifiant de X-Request-Id. Il nous permet de retrouver votre requête dans le journal.