Jetons d'accès

Une session, un jeton, frappé par votre serveur et consommé par la poignée de main.

Une session s’ouvre en présentant un jeton d’accès, dans l’adresse de la poignée de main :

https://api-webtransport.<hôte de votre environnement>/measures?accessTokenValue=<jeton>&visual=video-full&format=webm

Il voyage dans l’adresse plutôt que dans un en-tête parce qu’une poignée de main WebTransport n’en porte aucun — c’est l’interface, et elle est la même pour tout le monde. Deux conséquences en découlent, et ce sont elles qui font le jeton tel qu’il est : une adresse s’écrit dans plus d’endroits qu’un en-tête, donc frappez-en un par session et tenez sa durée à l’écart entre la décision de mesurer et la poignée de main elle-même.

Votre serveur le frappe

Votre serveur détient la clé d’API et appelle POST /access-tokens avec la permission measurement.realtime :

{
    "permissions": [{ "name": "measurement.realtime", "maxAttempts": 1 }],
    "duration": "15min"
}

maxAttempts: 1 mérite d’être écrit explicitement : le champ est facultatif, et une borne absente veut dire sans limite — un jeton rejouable pendant toute sa durée, alors qu’il voyage dans une adresse.

La réponse porte value, montré une fois et jamais plus : l’api n’en garde qu’un condensé. C’est cette chaîne que prend la poignée de main, et id — que la même réponse porte — n’ouvre rien ; c’est ce qui révoque le jeton.

La clé d'API reste sur votre serveur

Elle est permanente, sans portée, et ouvre toutes les routes de l’api. Un jeton est son contraire : une chaîne, une session, quelques minutes de vie. Rien de ce qui atteint un client ne doit être autre chose qu’un jeton.

Depuis la librairie de navigateur

La librairie ne détient aucun jeton : elle vous en demande un, par retrieveAccessToken, et elle le demande à chaque start() — un jeton est à usage unique, et la poignée de main le consomme. Rien n’est mis en cache, et c’est pourquoi l’option est un rappel plutôt qu’une valeur.

RealtimeMeasure.create({
    video: "#camera",
    retrieveAccessToken: () => fetch("/mon-serveur/realtime-token").then(response => response.text())
})

Rendez la chaîne, ou une promesse de chaîne. Tout le reste — un rejet, une exception, aucune réponse en dix secondes, ou une réponse qui n’est pas une chaîne non vide — fait échouer le démarrage, et token:error dit laquelle :

reasonCe qui s’est passé
no-callbackAucun retrieveAccessToken n’a été donné. La librairie ne peut pas mesurer sans lui.
strategy-failedVotre rappel a levé, a rejeté, ou est resté muet dix secondes.
unusable-responseIl a répondu autre chose qu’une chaîne non vide.

La valeur du jeton ne voyage sous aucun événement, et ne figure dans aucune charge que la librairie vous remet. Si votre endpoint relaie la réponse de l’api, prenez-y valueid n’ouvre rien, c’est ce qui révoque.

À quoi ressemble un refus

La poignée de main consomme le jeton — le résoudre, c’est le dépenser —, si bien qu’une session ouverte puis abandonnée en a dépensé un tout de même. Une seconde poignée de main demande un second jeton, ce qui fait d’une reprise automatique une décision et non un geste gratuit.

Un jeton expiré, déjà dépensé, malformé, révoqué ou frappé pour l’autre chaîne est refusé en 401, et la session ne s’ouvre pas : le client voit sa promesse ready échouer, sans rien de plus précis. C’est délibéré — un refus qui dirait lequel de ces cas il est répondrait à des questions que personne de légitime ne pose — et ce sont donc vos propres journaux, côté frappe, qui les distinguent.

Une permission par chaîne

measurement.realtime ouvre une session temps réel et rien d’autre. Un jeton frappé avec measurement.scan — la chaîne de Saphere Scan — est refusé ici, et l’inverse aussi.