Aller au contenu principal

Authentification & onboarding

Les identifiants vivent dans les variables d’environnement, jamais dans la page. Ce guide couvre la façon dont la console d’essai s’authentifie, et comment tendre un environnement prêt à l’emploi à un coéquipier avec un setup link.

L’environnement d’abord

Un environnement, c’est un nom, une URL de base, des variables et des en-têtes par défaut ajoutés à chaque requête — plus une couleur d’une palette fermée, affichée sur le sélecteur : la production en rouge se voit avant l’envoi, pas après. Le sélecteur porte aussi un badge quand des variables d’identifiants attendues par le schéma manquent, et au premier chargement d’un schéma, un bouton propose de créer un environnement par entrée servers — un amorçage sur demande, jamais automatique. Les environnements déclarés dans la config sont ajoutés par leur nom au chargement, sans rien écraser de ce que le lecteur a déjà ; ensuite, c’est son stockage qui fait foi.

Ce que le schéma déclare, la console le comprend

Chaque scheme de vos securitySchemes obtient un champ qui fonctionne : bearer, basic, apiKey en en-tête, en paramètre de requête ou en cookie, OAuth2 et OpenID Connect. La convention tient en une ligne : au scheme X correspond la variable auth.X. Renseignez-la dans l’environnement actif et chaque opération protégée par ce scheme l’envoie — laissez-la vide et l’interpolation {{var}} bloque l’envoi plutôt que de laisser fuir un {{auth.X}} littéral vers votre API.

Les variables sensibles sont masquées dans l’interface, avec une mise en garde honnête : les valeurs vivent dans le stockage local du navigateur, non chiffrées. Sur une machine partagée, agissez en conséquence.

OAuth2 sans backend

ApiGlow exécute Authorization Code + PKCE entièrement dans le navigateur : redirection complète vers votre fournisseur d’identité, échange du code dans la page, jeton dans auth.X — aucun serveur chez nous, jamais de secret client dans la config. Le flux client credentials est aussi pris en charge pour les API de machine à machine. Les flux OAuth de la démo pointent vers un fournisseur simulé ; face à votre API, vous fournissez vos vraies URL d’autorisation et de jeton.

La seule chose que la config transporte est le client id public, par scheme — jamais un secret — et le lecteur peut le surcharger avec la variable auth.X.clientId :

{ "oauth": { "petstore_auth": { "clientId": "public-client-id" } } }

Les flows pilotables sont exactement ces deux-là — Authorization Code + PKCE et client credentials. implicit et password sont documentés depuis le schéma mais ne portent pas de bouton « Obtenir un jeton ».

Ce qu’un navigateur ne sait pas faire, dit là où ça compte

Deux schemes sont rendus avec leur limite de plateforme énoncée plutôt qu’avec un bouton cassé, à trois endroits chacun — la doc de l’opération, le résumé d’authentification et le bloc credentials de la console :

  • mutualTLS est documenté mais jamais envoyé : « un navigateur ne peut pas présenter de certificat client — appelez cet endpoint depuis un client qui le peut (cURL, votre propre code) ».
  • Le flow OAuth2 device authorization (OpenAPI 3.2) n’est pas exécuté : « il interroge le endpoint de jeton hors du navigateur — obtenez le jeton ailleurs et collez-le dans la variable ».

Les deux limites sont celles de la plateforme, pas du schéma : la documentation reste complète, la console refuse simplement de faire semblant.

Quand la page hôte a déjà une session

La page de doc est statique — aucun serveur ne la rend, donc rien ne peut y injecter un jeton par lecteur à la livraison. Mais le lecteur a souvent déjà une session (cookie, SSO) dans ce navigateur, et votre backend saurait lui frapper un jeton d’API à la demande. Le pont d’identifiants hôte ferme cet écart : la page qui embarque ApiGlow enregistre un provider, et l’app le sollicite quand elle a besoin d’identifiants.

<!-- après la balise script d'ApiGlow -->
<script type="module">
  apidoc.registerCredentialsProvider(async ({ specId, reason }) => {
    const r = await fetch('/api/docs-token', { credentials: 'include' })
    if (!r.ok) return null
    return { bearerAuth: (await r.json()).access_token }
  })
</script>

La table retournée est indexée par les noms de schemes de vos securitySchemes : une chaîne remplit auth.X, un objet remplit les suffixes façon auth.X.username. Le lecteur arrive sur une doc dont le badge d’identifiants dit déjà « fournis par l’hôte » — rien à coller, rien à configurer.

Quatre règles rendent le raisonnement sûr :

  • Il ne remplit que le vide. Une valeur de l’hôte ne s’applique que là où l’environnement sélectionné est vide ; une valeur tapée par le lecteur (ou écrite par un flux OAuth) gagne toujours, et l’effacer retombe sur la valeur de l’hôte.
  • Mémoire seulement, toujours. Les valeurs de l’hôte ne sont jamais persistées — ni dans le stockage, ni dans les exports d’historique (elles sont capturées comme sensibles, le caviardage s’applique), ni dans les exports d’environnements. Rechargez la page : le provider est simplement redemandé.
  • Un seul rejeu sur 401. Si une requête dont les identifiants venaient de l’hôte prend un 401, l’app redemande une fois le provider (reason: "expired") et rejoue une fois — avec une note visible dans le panneau de réponse, jamais une boucle silencieuse.
  • La frontière de confiance, c’est la page. Seul le code de la page hôte peut enregistrer un provider ; rien dans un document OpenAPI ne peut en déclencher ou en influencer un, et l’app elle-même ne télécharge jamais d’URL d’identifiants.

Deux membres de plus couvrent les cas simples — apidoc.setCredentials(map) pousse des valeurs que l’hôte détient déjà, apidoc.clearCredentials() vide le pont à la déconnexion — et un script classique susceptible de tourner avant l’app écoute l’événement apidoc:ready au lieu de supposer la globale. En multi-spec, un seul provider sert toutes les specs : il reçoit le specId actif et route en interne.

Il n’y a délibérément aucune clé de config pour tout cela : le pont est une surface d’exécution, pour qu’un jeton ne puisse jamais finir écrit dans une page publique. Le fetch du provider s’authentifie typiquement par le cookie de session — credentials: "include" et les contraintes CORS qui l’accompagnent sont à votre charge, comme pour tout appel authentifié par cookie.

Le gestionnaire d’environnements porte une bande à lui — Préparer un coéquipier — avec deux portes d’entrée. La première partage l’environnement que vous avez : vous choisissez ce qui voyage, les variables cochées emportent leur valeur, les autres voyagent par leur nom, avec une valeur vide — un squelette que votre coéquipier remplit avec ses propres identifiants. Les lignes non sensibles partent cochées, les sensibles décochées.

Si vous embarquez un identifiant, la boîte de partage le dit sans détour, en place plutôt que derrière une confirmation : qui détient le lien détient l’identifiant, et un lien survit à la conversation qui l’a porté. Les liens très longs ont aussi leur avertissement, au-delà de 2 000 caractères — certaines messageries tronquent sans prévenir, et c’est le pire sort qui puisse arriver à un lien.

Le fabriquer sans créer l’environnement

Partager part d’un environnement que vous possédez déjà — donc de le créer d’abord chez vous, avec des valeurs que vous ne souhaitez pas forcément garder sur votre machine. La seconde porte de cette bande, Partir de zéro, lève cette contrainte : un formulaire qui décrit l’environnement que l’équipe devrait avoir, et qui en produit le lien. Il est aussi proposé comme carte sur la vue d’accueil, là où un lead atterrit vraiment.

Ce formulaire est un générateur pur : il n’écrit jamais rien. Fabriquer un lien n’est pas créer un environnement ; la boîte n’a aucun chemin vers votre stockage. Vous remplissez un nom, une URL de base, une couleur, des lignes de variables et des lignes d’en-têtes par défaut — chaque variable portant un marqueur sensible et une case embarquer la valeur, décochée par défaut pour les lignes sensibles : un identifiant ne voyage que si vous le dites.

Le lien se reconstruit à la frappe, et les bornes sont vérifiées en direct — celle qui casse est nommée, plutôt que de vous laisser partir avec un lien que le destinataire refuserait. Prévisualiser en destinataire ferme le constructeur et ouvre le lien sur-le-champ : pas une reproduction de la boîte d’arrivée, la boîte d’arrivée elle-même, pour que l’aperçu ne puisse pas diverger de ce que verra votre coéquipier. Annuler n’écrit rien ; Appliquer applique pour de bon, et le bouton le dit. En multi-spec, le lien vise la spec où vous êtes.

Voici la même idée, jouée sur cette page plutôt que dans l’application — un vrai setup link, du format exact qu’ApiGlow sait lire, pointé sur la démo :

FABRIQUER UN SETUP LINK · ESSAI SUR LA DÉMO

Case décochée : auth.api_key voyage par son nom, valeur vide — le squelette que le destinataire remplit.

Ouvrez le lien : vous voilà à la place du destinataire, et rien n’est écrit tant que vous n’acceptez pas. Le lien ouvre un tableau de relecture — chaque variable marquée créée, mise à jour ou conservée, valeurs sensibles cachées, valeurs vides signalées « à remplir » — et le lien est aussitôt retiré de la barre d’adresse. Trois garde-fous refusent les situations douteuses : un lien illisible ne change rien, un lien construit pour une autre spec ne change rien, et sur une installation environmentsLocked, un setup link ne peut rien changer du tout.

Générer des liens depuis un script

Ni l’un ni l’autre formulaire n’a quoi que ce soit de magique, et aucun n’est nécessaire pour produire un lien : c’est l’URL de votre doc suivie d’un pseudo-paramètre setup dans le hash, portant un JSON encodé en base64url. C’est ce qui met les liens d’accueil à la portée d’un job de CI ou de la page d’intranet de l’équipe :

https://docs.example.com/#/?setup=<base64url(payload)>

Le payload, version 1 :

{
  "v": 1,
  "spec": "petstore",
  "env": {
    "name": "Staging",
    "baseUrl": "https://staging.petstore.example",
    "vars": [
      ["auth.api_key", "", true],
      ["team", "docs-squad"]
    ],
    "headers": [["X-Team", "docs"]]
  }
}

Ce qu’il faut savoir pour l’écrire soi-même :

  • Chaque variable est une ligne [nom, valeur] ou [nom, valeur, true] — le troisième élément marque la variable sensible (le champ du destinataire naît masqué). Le drapeau voyage même quand la valeur ne voyage pas.
  • Une valeur vide est le squelette : la variable est créée par son nom, le destinataire la remplit.
  • spec est optionnel mais recommandé sur une installation multi-spec : sans lui, pas de garde-fou « mauvaise API ».
  • baseUrl, color et headers sont optionnels.
  • L’encodage est du base64url sans padding (+-, /_, = retirés). En Node : Buffer.from(JSON.stringify(payload)).toString("base64url").
  • Bornes appliquées à la lecture : 8 Ko de JSON décodé, 50 variables, 20 en-têtes, 200 caractères par nom, 4 Ko par valeur — au-delà de l’une d’elles le lien entier est refusé comme illisible, et un nom employé deux fois le refuse aussi. Et au-delà de ~2 000 caractères d’URL, méfiez-vous des messageries qui tronquent.

Le premier appel, généré

features: { "onboarding": true } ajoute une page « Premier appel » en tête de la nav de référence (#/first-call), pour le lecteur qui n’a encore rien envoyé : ApiGlow choisit la lecture la plus simple que le schéma déclare — un GET, sans corps, plus rien à taper une fois les exemples déclarés pré-remplis — et la présente sous un préambule en trois pas : choisir un langage, renseigner les identifiants, appuyer sur Envoyer. Les trois pas se jouent dans la vraie console d’essai : le lecteur finit exactement là où il travaillera. Désactivée par défaut, et absente de toute façon quand le schéma ne déclare aucune lecture de ce genre.

Déploiements verrouillés

environmentsLocked s’adresse aux installations d’entreprise encadrées : les environnements viennent de la configuration du site et les lecteurs ne peuvent pas les modifier — ce qui neutralise aussi les setup links entrants. Voir la référence de configuration pour la clé, et Console d’essai & CORS pour comprendre pourquoi un appel correctement authentifié peut malgré tout être bloqué par le navigateur.