Méthode GET/passport /{identifier} /vc /verify
Contrôler la signature du passeport numérique délivré sous forme d'attestation vérifiable, et lire les données révélées au niveau d'accès demandé. Aucune session n'est demandée pour les niveaux public et end_user.
Sur cette page
- Autorisation
- D'où vous appelez
- Le niveau que vous demandez
- Plafond d'appels
- Paramètres de chemin et de requête
- Les trois formes d'identifiant acceptées
- Les six valeurs de access_tier
- En-têtes
- Corps de la requête
- Requête d'exemple
- Réponse d'exemple
- Un contrôle qui échoue reste une réponse 200
- Ce qui fait basculer verified à false
- Contrôler la signature vous-même
- Erreurs
- Voir aussi
Vous faites contrôler la signature du passeport numérique d'un produit, et vous récupérez les données que cette signature couvre. En quittant cette page, vous saurez demander ce contrôle depuis n'importe quel identifiant de produit, distinguer un contrôle qui échoue d'une requête qui échoue, et savoir quelles données la réponse vous montre selon le niveau d'accès que vous demandez.
Adresse complète :
GET https://api.sealtrust.io/v1/passport/{identifier}/vc/verifyLe même point d'entrée répond aussi sans le préfixe /v1, à
https://api.sealtrust.io/passport/{identifier}/vc/verify. Les deux adresses
appellent le même code. Utilisez la forme /v1 pour une nouvelle intégration.
Le passeport est délivré au format SD-JWT-VC, une attestation signée dont
chaque champ peut être révélé ou retenu séparément. L'émetteur est la marque,
identifiée par un identifiant décentralisé did:web. Ce point d'entrée
recompose l'attestation au niveau d'accès que vous demandez, contrôle sa
signature, et vous rend les données révélées.
#Autorisation
Aucun droit de clef d'API n'est vérifié sur ce point d'entrée. Deux contrôles s'appliquent malgré tout : l'origine de votre appel, puis le niveau d'accès que vous demandez.
#D'où vous appelez
Appelez ce point d'entrée depuis votre serveur.
Nous refusons en 403 tout appel qui porte un en-tête Origin ou Referer
désignant un domaine autre que les nôtres. Le champ detail vaut alors
Forbidden origin. Un navigateur pose toujours l'un de ces deux en-têtes, donc
une page web hébergée ailleurs que chez nous ne peut pas appeler cette adresse
depuis le navigateur de son visiteur.
Le cookie de session ne fonctionne que depuis une page servie par un de nos
domaines. Nous refusons en 403 un appel qui porte ce cookie sans en-tête
Origin ni Referer, avec detail à Origin or Referer header required.
Depuis un serveur, présentez donc le jeton de session dans l'en-tête
Authorization: Bearer.
#Le niveau que vous demandez
Les niveaux public et end_user ne demandent aucun compte. Les cinq autres
valeurs du paramètre access_tier exigent une session de compte, présentée par
l'en-tête Authorization: Bearer <jeton de session> ou par le cookie de
session posé à la connexion.
| Niveau demandé | Ce qu'il faut |
|---|---|
public | rien |
end_user | rien |
repairer | une session, et une accréditation de réparateur active sur la marque du produit |
recycler | une session, et une accréditation de recycleur active sur la marque du produit |
upstream | une session ayant accès à la marque du produit, ou le rôle d'autorité |
authority | une session portant le rôle d'autorité de surveillance du marché |
Une session ayant accès à la marque du produit ouvre les trois niveaux de métier sur ses propres produits. Le rôle d'autorité de surveillance du marché les ouvre également, sur tous les produits.
Une clef d'API partenaire n'ouvre rien ici. Le jeton reçu dans l'en-tête
Authorization est décodé comme un jeton de session de compte utilisateur, et
une clef d'API n'en est pas un. La lecture échoue en silence et l'appel se
poursuit comme un appel anonyme.
#Plafond d'appels
60 appels par tranche de 60 secondes, comptés par adresse IP appelante. La fenêtre est fixe.
Ce plafond est partagé par toutes les adresses qui commencent par /passport.
Les formes /passport/… et /v1/passport/… alimentent le même compteur, le
préfixe /v1 ne crée pas un second budget.
Chaque réponse acceptée porte trois en-têtes qui décrivent ce compteur.
| En-tête | Contenu |
|---|---|
X-RateLimit-Limit | le plafond appliqué sur la fenêtre, ici 60 |
X-RateLimit-Remaining | ce qu'il vous reste dans la fenêtre en cours |
X-RateLimit-Reset | l'horodatage de fin de la fenêtre, en secondes |
Un dépassement renvoie 429, avec les mêmes trois en-têtes et un Retry-After.
Sur ce point d'entrée, Retry-After vaut la durée de la fenêtre, soit 60
secondes.
#Paramètres de chemin et de requête
| Nom | Type | Obligatoire | Description |
|---|---|---|---|
identifier | string | oui | L'identifiant du produit. Trois formes sont acceptées, voir ci-dessous. |
access_tier | string | non | Le niveau d'accès demandé. Vaut public par défaut. Six valeurs acceptées, listées plus bas. Nous refusons toute autre valeur en 422. |
#Les trois formes d'identifiant acceptées
| Forme | À quoi elle ressemble | Où vous la trouvez |
|---|---|---|
| Numéro de série | 12 caractères, chiffres et lettres | Imprimé sur le produit, c'est ce que porte son QR |
| Identifiant de jeton | Une suite de chiffres, souvent très longue | Rendu par nos réponses dans le champ token_id |
| Empreinte d'identifiant | 0x suivi de 64 caractères hexadécimaux | Rendue par nos réponses dans le champ uid_hash |
L'empreinte d'identifiant existe pour un produit en QR seul comme pour un produit à puce NFC. Le serveur la tire au hasard pour un produit en QR seul. Il la dérive de l'identifiant de la puce pour un produit à puce NFC. Les deux formes ont donc la même allure et se demandent de la même manière.
Nous reconnaissons la forme à l'écriture. Une valeur qui commence par 0x et
fait exactement 66 caractères, nous la cherchons comme une empreinte
d'identifiant. Toute autre valeur, nous la cherchons d'abord comme un
identifiant de jeton, puis comme un numéro de série quand la première recherche
n'a rien donné.
Vous écrivez l'empreinte d'identifiant dans la casse que vous voulez. Vous
écrivez le numéro de série dans la casse que vous voulez également, et nous le
canonicalisons comme le fait le résolveur du QR : nous y lisons les lettres I
et L comme un 1, la lettre O comme un 0. Vous pouvez donc recopier à la
main un numéro lu sur une étiquette.
Le numéro de certificat d'authenticité n'est pas accepté ici.
Un produit détruit sur la chaîne ou retiré du catalogue ne se résout plus par ce point d'entrée, et la réponse est alors 404.
#Les six valeurs de access_tier
Ces niveaux sont des publics différents, sans hiérarchie entre eux. Chacun des trois niveaux de métier hérite du niveau public et du niveau utilisateur final, puis ajoute ce que son métier demande.
| Valeur | Ce qu'elle ajoute aux champs révélés |
|---|---|
public | identification du produit, conformité ESPR, REACH et marquage CE, pourcentage de recyclabilité, pourcentage de contenu recyclé, étiquettes, spécification générale de batterie |
end_user | impact environnemental, circularité complète, matière principale, matière certifiée biologique, durabilité, efficacité énergétique, empreinte carbone |
repairer | nomenclature, notice de démontage, indice de réparabilité, état de santé de batterie |
recycler | composition matière, substances préoccupantes, notice de démontage, état de santé de batterie |
upstream | composition matière, substances préoccupantes, fabrication, chaîne d'approvisionnement |
authority | l'intégralité des données, sans filtrage |
Une marque peut remplacer ces règles par les siennes. Le tableau ci-dessus décrit ce qui s'applique à défaut de règles propres à la marque.
#En-têtes
Aucun en-tête n'est requis pour les niveaux public et end_user. Les cinq
autres niveaux exigent l'en-tête Authorization ou le cookie de session. Dans
tous les cas, respectez la règle d'origine décrite plus haut.
#Corps de la requête
Aucun. Cette requête n'a pas de corps.
#Requête d'exemple
Contrôle de l'attestation du produit dont le numéro imprimé est
EXEMPLE00001, au niveau public.
Les trois exemples s'exécutent depuis un serveur. Aucun ne fonctionne dans le
navigateur d'un visiteur : le navigateur pose un en-tête Origin que nous
refusons, et vous recevez 403.
curl -i "https://api.sealtrust.io/v1/passport/EXEMPLE00001/vc/verify?access_tier=public"const url = new URL(
"https://api.sealtrust.io/v1/passport/EXEMPLE00001/vc/verify",
);
url.searchParams.set("access_tier", "public");
const reponse = await fetch(url);
if (reponse.status === 404) {
console.log("Aucune attestation signée à contrôler pour ce produit.");
} else if (reponse.status === 403) {
console.log(
"Appel refusé : exécutez ce code depuis un serveur, jamais depuis un navigateur.",
);
} else if (reponse.ok) {
const resultat = await reponse.json();
console.log(resultat.verified, resultat.error);
console.log(resultat.issuer, resultat.key_version);
console.log(resultat.credential_subject);
} else {
console.log(reponse.status, await reponse.json());
}import requests
response = requests.get(
"https://api.sealtrust.io/v1/passport/EXEMPLE00001/vc/verify",
params={"access_tier": "public"},
timeout=30,
)
if response.status_code == 404:
print("Aucune attestation signée à contrôler pour ce produit.")
elif response.status_code == 403:
print("Appel refusé : exécutez ce code depuis un serveur, jamais depuis un navigateur.")
elif response.ok:
resultat = response.json()
print(resultat["verified"], resultat["error"])
print(resultat["issuer"], resultat["key_version"])
print(resultat["credential_subject"])
else:
print(response.status_code, response.json())#Réponse d'exemple
Code HTTP 200OK
Code HTTP 200. La signature est valide et le niveau demandé est public.
{
"passport_id": 1,
"issuer": "did:web:api.sealtrust.io:brand:4242",
"vct": "https://schema.sealtrust.io/vct/digital-product-passport",
"key_version": 1,
"access_tier": "public",
"verified": true,
"error": null,
"credential_subject": {
"product_identity": {
"gtin": "03701234567890",
"model": "Cartable Exemple 32",
"brand": "Exemple SAS",
"made_in": "FR"
},
"compliance": {
"eu_espr": true,
"reach": true,
"ce_marking": false
},
"circularity": {
"recyclability_percentage": 62,
"recycled_content_percentage": 0
}
}
}La réponse compte huit champs et rien d'autre.
| Champ | Type | Description |
|---|---|---|
passport_id | integer | L'identifiant de la version de passeport sur laquelle le contrôle a porté. |
issuer | string | L'identifiant décentralisé did:web de la marque émettrice. Toujours renseigné sur ce point d'entrée. |
vct | string | Le type d'attestation. Vaut https://schema.sealtrust.io/vct/digital-product-passport quand la marque n'en a pas déclaré un autre. |
key_version | integer ou null | Le numéro de version de la clef de signature de la marque qui a signé l'attestation. null quand ce numéro n'a pas été enregistré à la délivrance. |
access_tier | string | Le niveau d'accès demandé, repris tel quel. Ce point d'entrée ne change jamais le niveau demandé. |
verified | boolean | true quand la signature a été contrôlée avec succès. |
error | string ou null | null quand verified vaut true. Vaut verification_failed sinon. C'est la seule valeur possible. |
credential_subject | object ou null | Les données révélées au niveau demandé, telles que la signature les couvre. Vaut null dès que verified vaut false. |
#Un contrôle qui échoue reste une réponse 200
C'est le point à retenir de cette page. Un échec de contrôle n'est pas une erreur HTTP. La réponse reste 200 et porte le verdict.
{
"passport_id": 1,
"issuer": "did:web:api.sealtrust.io:brand:4242",
"vct": "https://schema.sealtrust.io/vct/digital-product-passport",
"key_version": 1,
"access_tier": "public",
"verified": false,
"error": "verification_failed",
"credential_subject": null
}Lisez donc toujours verified. Un code 200 ne dit rien à lui seul.
#Ce qui fait basculer verified à false
| Cause | Ce qu'elle signifie |
|---|---|
| La signature ne correspond pas au contenu | L'attestation a été modifiée après sa délivrance. |
| L'émetteur inscrit dans l'attestation n'est pas celui attendu pour cette marque | L'attestation a été délivrée sous une autre identité que celle de la marque du produit. |
| L'attestation ne désigne aucune version de clef lisible | L'en-tête de l'attestation ne porte pas de numéro de version de clef exploitable. |
| La version de clef citée par l'attestation n'existe pas pour cette marque | La clef qui a signé n'est pas connue. |
| Cette version de clef a été révoquée | La marque a retiré cette clef. Les attestations qu'elle a signées ne sont plus reconnues. |
La réponse ne dit pas laquelle de ces causes s'applique. Le champ error vaut
verification_failed dans tous ces cas.
#Contrôler la signature vous-même
Vous n'êtes pas obligé de nous demander ce verdict. La marque publie ses clefs
publiques de signature dans un document d'identité décentralisé, servi
publiquement à l'adresse GET https://api.sealtrust.io/brand/{brand_id}/did.json.
Une marque qui héberge son identité sur son propre domaine le sert à l'adresse
https://<son domaine>/.well-known/did.json.
Avec ce document et n'importe quelle bibliothèque did:web et SD-JWT-VC du
commerce, vous contrôlez la signature sans passer par nous. C'est ce qui rend
le passeport opposable sans dépendre de notre disponibilité.
#Erreurs
Le corps d'une réponse d'erreur porte un champ detail.
Les contrôles s'enchaînent dans cet ordre : origine de l'appel, résolution du produit, recherche du passeport publié, contrôle du niveau demandé, présence d'une attestation délivrée, puis identification de la marque émettrice. La première étape qui échoue donne la réponse.
| Code | Condition | Que faire |
|---|---|---|
| 401 | access_tier=authority est demandé sans session valide. detail vaut Authority-tier access requires authentication. | Connectez-vous avec un compte portant le rôle d'autorité de surveillance du marché. Une clef d'API partenaire ne convient pas. |
| 401 | access_tier vaut repairer, recycler ou upstream, et l'appel ne porte aucune session valide. detail vaut Professional-tier access requires authentication. | Présentez un jeton de session de compte, ou demandez le niveau public ou end_user. |
| 403 | L'appel porte un en-tête Origin ou Referer qui ne désigne pas un de nos domaines, ce qui arrive pour tout appel émis depuis une page web hébergée ailleurs. detail vaut Forbidden origin. | Appelez ce point d'entrée depuis votre serveur. Un appel émis par le navigateur d'un visiteur ne peut aboutir. |
| 403 | L'appel porte le cookie de session et n'a ni en-tête Origin ni en-tête Referer, ce qui arrive quand on rejoue un cookie de navigateur en ligne de commande. detail vaut Origin or Referer header required. | Retirez le cookie et présentez le jeton de session dans l'en-tête Authorization: Bearer. |
| 403 | access_tier=authority est demandé par un compte connecté qui ne porte pas ce rôle. detail vaut Authority-tier access is restricted to market surveillance authorities. | Demandez le niveau qui correspond à votre habilitation. |
| 403 | Un niveau professionnel est demandé par un compte connecté qui n'a ni accès à la marque du produit, ni l'accréditation correspondante sur cette marque. detail vaut This tier is restricted to the product's brand, partners holding the matching accreditation, and market-surveillance authorities. | Demandez à la marque l'accréditation qui correspond à votre métier, puis demandez le niveau de ce métier. |
| 404 | Aucun produit ne correspond à cet identifiant, sous aucune des trois formes acceptées. detail vaut Product not found. | Vérifiez l'identifiant. Un produit détruit sur la chaîne ou retiré du catalogue donne cette même réponse. |
| 404 | Le produit existe, mais aucun passeport public n'est publié pour lui ni pour son modèle. detail vaut No published passport found for this product. | Il n'y a rien à contrôler. Un passeport réservé au propriétaire ou à la marque donne aussi cette réponse. |
| 404 | Un passeport public existe, mais aucune attestation signée n'a été délivrée pour cette version. detail vaut No VC issued for this passport yet. | Demandez à la marque de délivrer l'attestation de cette version du passeport. Le passeport reste lisible par les points d'entrée de lecture. |
| 404 | La marque du passeport n'a pas pu être retrouvée. detail vaut Brand not found. | Contactez le support en indiquant l'identifiant que vous avez appelé. Aucune action de votre côté ne corrige cette réponse. |
| 422 | access_tier n'est pas une des six valeurs acceptées. detail est une liste, chaque entrée portant loc, type et msg. | Lisez loc pour savoir quel paramètre est en cause, puis corrigez sa valeur. |
| 429 | Le plafond de 60 appels par 60 secondes est atteint pour votre adresse IP, tous chemins commençant par /passport confondus. detail vaut Rate limit exceeded: 60 requests per 60s. | Attendez le nombre de secondes indiqué par Retry-After, puis réessayez. |
| 500 | Une erreur inattendue s'est produite pendant le traitement de votre appel. detail vaut Internal Server Error. La réponse porte un en-tête X-Request-Id. | Réessayez. Si l'erreur persiste, contactez le support en indiquant la valeur de X-Request-Id. |
#Voir aussi
GET /passport/{identifier}/vc, récupérer le justificatif signé du passeport, au format SD-JWT-VC.GET /passport/{identifier}/vc/preview, voir, sans signature, ce qu'un niveau d'accès exposerait.GET /brand/{brand_id}/did.json, récupérer les clefs publiques de signature d'une marque.GET /.well-known/did.json, servir le document d'identité d'une marque sur son propre domaine.
Cette page vous a-t-elle été utile ?
Votre réponse ouvre un courriel pré-rempli dans votre messagerie, à destination de contact@sealtrust.io. Vous le relisez avant de l'envoyer.