# GET /.well-known/did.json

Servir le document d'identité décentralisée d'une marque sur son propre nom de domaine, en mode délégué. Point d'entrée public, résolu d'après le nom d'hôte appelé.

Source : https://docs.sealtrust.io/reference/get-well-known-did-json/

---

Vous récupérez le document d'identité décentralisée d'une marque, publié sous le
nom de domaine de cette marque. Ce document liste les clefs publiques avec
lesquelles la marque signe ses passeports numériques de produit. En quittant
cette page, vous saurez appeler cette adresse, lire chaque champ du document, et
distinguer un domaine qui n'est pas encore reconnu d'un domaine reconnu pour
lequel aucune marque n'a été déclarée.

Adresse complète, avec un domaine d'exemple :

```http
GET https://id.exemple-sas.example/.well-known/did.json
```

Ce point d'entrée ne se lit pas comme les autres. Le chemin est toujours le même
pour tout le monde. C'est le **nom d'hôte appelé** qui désigne la marque. Le
serveur lit l'en-tête `Host` de votre requête, cherche la marque qui a déclaré
ce nom de domaine en mode délégué, et sert le document de cette marque.

> [!INFO] À quoi sert le mode délégué
> Une identité décentralisée de la forme `did:web:id.exemple-sas.example` se
> résout, selon la spécification `did:web` du W3C, vers
> `https://id.exemple-sas.example/.well-known/did.json`. La marque fait pointer
> ce nom d'hôte vers nous par un enregistrement DNS de type `CNAME`.
> Le document est alors servi par cette adresse, et la rotation des clefs de la
> marque s'y répercute sans qu'elle ait à republier un fichier. La marque
> conserve son identité, puisque le nom de domaine lui appartient et qu'elle
> peut le refaire pointer ailleurs.

Le même point d'entrée répond aussi sous le préfixe `/v1`, à
`https://id.exemple-sas.example/v1/.well-known/did.json`. Les deux adresses
appellent le même code. Aucun résolveur `did:web` n'utilise cette seconde forme :
la spécification impose le chemin `/.well-known/did.json` à la racine du
domaine. Appelez la forme sans préfixe.

## Autorisation

Aucune, point d'entrée public. Vous n'envoyez ni clef d'API, ni session, ni
en-tête d'origine. La réponse est la même pour tout le monde. C'est ce qui
permet à un vérificateur tiers de contrôler la signature d'un passeport avec une
bibliothèque `did:web` standard, sans compte chez nous.

> [!ATTENTION] Le partage entre origines n'est pas ouvert aux tiers
> Les origines autorisées à appeler l'API depuis un navigateur sont celles de
> SealTrust. Une page web tierce qui appelle ce point d'entrée depuis le
> navigateur verra sa requête refusée par le navigateur lui-même. Résolvez ce
> document depuis votre serveur.

## Plafond d'appels

Aucun plafond propre à ce point d'entrée. Ce chemin relève du compteur général
de l'API, compté par adresse réseau appelante sur une tranche de 60 secondes.

Ce compteur de repli est commun à tous les chemins qui n'ont pas de plafond
propre. Les appels que vous adressez à l'un d'eux entament donc le budget des
autres. Le préfixe `/v1` ne crée pas un second budget.

Chaque réponse porte les en-têtes `X-RateLimit-Limit`, `X-RateLimit-Remaining`
et `X-RateLimit-Reset`. Lisez `X-RateLimit-Remaining` pour savoir combien
d'appels il vous reste dans la fenêtre en cours. `X-RateLimit-Reset` porte
l'instant de bascule vers la fenêtre suivante, en secondes depuis le
1er janvier 1970.

Prévoyez le code 429 dans votre client et respectez l'en-tête `Retry-After`
qu'il porte. La valeur du compteur général peut changer sans préavis : réglez
votre cadence sur les en-têtes de vos réponses, et n'inscrivez aucun nombre en
dur dans votre code.

> [!ATTENTION] Gardez votre propre copie du document
> La réponse interdit la mise en cache, avec l'en-tête
> `Cache-Control: no-store, max-age=0`. Un résolveur `did:web` qui respecte cet
> en-tête rappelle donc cette adresse à chaque vérification de passeport, et
> consomme votre budget d'appels. Conservez le document dans votre propre
> système, et rappelez cette adresse quand vous rencontrez un identifiant de
> clef que votre copie ne connaît pas. Un document d'identité ne change qu'à la
> création ou à la révocation d'une clef.

## Paramètres de chemin et de requête

Ce point d'entrée n'a aucun paramètre de chemin et aucun paramètre de requête.
Le chemin est fixe et identique pour toutes les marques.

### En-têtes

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `Host` | `string` | oui | Le nom d'hôte qui désigne la marque. C'est le seul élément de la requête que le serveur lit pour choisir la réponse. Votre client HTTP le remplit automatiquement à partir de l'adresse que vous appelez. |

Le serveur ramène ce nom d'hôte en minuscules et en retire le numéro de port
avant de chercher la marque. `ID.Exemple-SAS.example` et
`id.exemple-sas.example:443` désignent donc la même marque.

Aucun autre en-tête n'est requis.

## Corps de la requête

Aucun. Cette requête n'a pas de corps.

## Requête d'exemple

Document d'identité de la marque fictive Exemple SAS, publié sur son domaine
délégué `id.exemple-sas.example`.

> [!INFO] Le SDK TypeScript ne couvre pas ce point d'entrée
> Aucune méthode du paquet `@sealtrust-io/sdk` n'appelle cette adresse.
> L'exemple TypeScript ci-dessous utilise `fetch`, sans dépendance.

:::onglets
```bash title="curl"
curl -i https://id.exemple-sas.example/.well-known/did.json
```
```typescript
const reponse = await fetch(
  "https://id.exemple-sas.example/.well-known/did.json",
);

if (reponse.status === 404) {
  console.log("Aucune marque n'est publiée sous ce nom de domaine.");
} else if (reponse.ok) {
  const document = await reponse.json();
  console.log(document.id);
  for (const methode of document.verificationMethod) {
    console.log(methode.id, methode.publicKeyJwk.crv);
  }
  console.log(document.assertionMethod);
} else {
  console.log(reponse.status, await reponse.json());
}
```
```python
import requests

response = requests.get(
    "https://id.exemple-sas.example/.well-known/did.json",
    timeout=30,
)

if response.status_code == 404:
    print("Aucune marque n'est publiée sous ce nom de domaine.")
elif response.ok:
    document = response.json()
    print(document["id"])
    for methode in document["verificationMethod"]:
        print(methode["id"], methode["publicKeyJwk"]["crv"])
    print(document["assertionMethod"])
else:
    print(response.status_code, response.json())
```
:::

## Réponse d'exemple

Code HTTP `200`. Une marque qui a fait tourner sa clef une fois publie deux
méthodes de vérification, l'ancienne et la nouvelle.

```json
{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/jws-2020/v1"
  ],
  "id": "did:web:id.exemple-sas.example",
  "verificationMethod": [
    {
      "id": "did:web:id.exemple-sas.example#key-1",
      "type": "JsonWebKey2020",
      "controller": "did:web:id.exemple-sas.example",
      "publicKeyJwk": {
        "kty": "EC",
        "crv": "P-256",
        "x": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
        "y": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
      }
    },
    {
      "id": "did:web:id.exemple-sas.example#key-2",
      "type": "JsonWebKey2020",
      "controller": "did:web:id.exemple-sas.example",
      "publicKeyJwk": {
        "kty": "EC",
        "crv": "P-256",
        "x": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
        "y": "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
      }
    }
  ],
  "assertionMethod": [
    "did:web:id.exemple-sas.example#key-1",
    "did:web:id.exemple-sas.example#key-2"
  ],
  "authentication": [
    "did:web:id.exemple-sas.example#key-1",
    "did:web:id.exemple-sas.example#key-2"
  ]
}
```

La réponse compte cinq champs et rien d'autre.

| Champ | Type | Description |
| --- | --- | --- |
| `@context` | `string[]` | Les deux vocabulaires qui donnent leur sens aux autres champs. Toujours `https://www.w3.org/ns/did/v1` puis `https://w3id.org/security/suites/jws-2020/v1`, dans cet ordre. |
| `id` | `string` | L'identifiant décentralisé de la marque, de la forme `did:web:` suivi du nom de domaine appelé. C'est la valeur que porte le champ `issuer` des passeports signés par cette marque. |
| `verificationMethod` | `object[]` | La liste des clefs publiques de la marque. Une entrée par clef. Voir le tableau ci-dessous. |
| `assertionMethod` | `string[]` | Les identifiants des clefs autorisées à signer un passeport, dans le même ordre que `verificationMethod`. |
| `authentication` | `string[]` | La même liste d'identifiants que `assertionMethod`. |

Chaque entrée de `verificationMethod` porte quatre champs.

| Champ | Type | Description |
| --- | --- | --- |
| `id` | `string` | L'identifiant de la clef, de la forme `<identifiant de la marque>#key-<numéro de version>`. C'est la valeur que porte l'en-tête `kid` d'un passeport signé, et c'est elle qui vous dit quelle clef employer. |
| `type` | `string` | Toujours `JsonWebKey2020`. |
| `controller` | `string` | L'identifiant décentralisé de la marque. Même valeur que le champ `id` du document. |
| `publicKeyJwk` | `object` | La clef publique au format JSON Web Key. Une clef sur courbe elliptique NIST P-256 : `kty` vaut `EC`, `crv` vaut `P-256`, `x` et `y` sont les deux coordonnées du point public, encodées en base64url. |

La réponse porte aussi ces en-têtes.

| En-tête | Contenu |
| --- | --- |
| `Content-Type` | `application/json`. |
| `Cache-Control` | `no-store, max-age=0`. La réponse ne doit être conservée dans aucun cache intermédiaire. |
| `X-Request-Id` | L'identifiant de votre appel chez nous. Donnez cette valeur au support quand vous signalez une réponse inattendue. |
| `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` | Votre budget d'appels, voir la section Plafond d'appels. |

> [!ATTENTION] Le type de contenu servi est `application/json`
> Certains résolveurs `did:web` exigent le type `application/did+json` et
> refusent la réponse autrement. Configurez le vôtre pour accepter
> `application/json`, ou lisez le corps sans contrôler le type de contenu.

### Ce que contient la liste des clefs

Les clefs sont rendues de la plus ancienne version à la plus récente, par numéro
de version croissant.

Une clef retirée du service reste dans le document tant qu'elle n'a pas été
révoquée. C'est voulu : un passeport signé sous une ancienne version continue de
se vérifier après une rotation. Prenez la clef dont l'identifiant correspond à
l'en-tête `kid` du passeport que vous vérifiez, jamais la dernière de la liste.

Une clef révoquée disparaît du document. Les signatures produites avec elle
cessent alors de se vérifier, et c'est le résultat attendu.

Une marque qui n'a encore aucune clef reçoit un document valide dont
`verificationMethod`, `assertionMethod` et `authentication` sont des listes
vides. Prévoyez ce cas dans votre code.

> [!ATTENTION] Un document servi ne dit rien de la marque elle-même
> Ce point d'entrée publie des clefs publiques. Il ne dit pas qu'une marque est
> légitime, ni qu'un produit est authentique. Il vous donne de quoi vérifier
> vous-même la signature d'un passeport.

### Faire pointer votre domaine

Trois façons d'héberger le document existent, et une seule passe par cette
adresse chez nous.

| Mode | Où vit le document | Adresse résolue |
| --- | --- | --- |
| Plateforme | chez nous, sous notre nom de domaine | `https://api.sealtrust.io/brand/{brand_id}/did.json` |
| Auto-hébergé | chez vous, vous servez le fichier vous-même | `https://votre-domaine/.well-known/did.json` |
| Délégué | chez nous, sous votre nom de domaine | `https://votre-domaine/.well-known/did.json` |

Le mode plateforme est celui appliqué par défaut. Le mode délégué est celui que
cette page décrit. Le mode appliqué à votre marque est posé par SealTrust. Écrivez
à contact@sealtrust.io pour en changer.

> [!ATTENTION] Le domaine doit être déclaré avant d'être appelé
> Un appel portant un nom de domaine qui n'est pas déclaré chez nous reçoit un
> code 400. Faites déclarer et vérifier votre domaine délégué par le support
> avant de publier votre identité décentralisée. Un enregistrement DNS correct
> ne suffit pas.

## Erreurs

Le corps d'une réponse d'erreur porte un champ `detail`, sauf pour le 400 décrit
ci-dessous. Toute réponse porte un en-tête `X-Request-Id`, l'identifiant de votre
appel chez nous.

| Code | Condition | Que faire |
| --- | --- | --- |
| 400 | Le nom de domaine appelé n'est pas déclaré chez nous. La réponse est du texte brut, `Invalid host header`, sans champ `detail`. Une requête sans en-tête `Host`, ou avec un en-tête `Host` vide, reçoit le même 400. | Contactez le support pour faire déclarer votre domaine délégué avant de mettre l'identité en service. |
| 404 | Aucune marque n'a déclaré ce nom de domaine comme son domaine d'identité en mode délégué. `detail` vaut `No DID Document for this host`. | Vérifiez le nom de domaine appelé. Une marque en mode plateforme ou en mode auto-hébergé ne répond jamais ici, même si son domaine pointe vers nous. |
| 422 | Le document d'identité de la marque trouvée ne peut pas être construit. `detail` porte le motif du refus. | Signalez-le au support en indiquant le nom de domaine appelé. Vous ne pouvez rien corriger de votre côté. |
| 429 | Trop d'appels depuis votre adresse réseau. La réponse porte l'en-tête `Retry-After`, en secondes. | Attendez le nombre de secondes indiqué par `Retry-After`, puis réessayez. Espacez vos appels : le compteur est partagé avec tous les autres chemins qui n'ont pas de plafond propre. |
| 500 | Une erreur inattendue s'est produite pendant le traitement de votre appel. `detail` vaut `Internal Server Error`. | Réessayez. Si l'erreur persiste, contactez le support en indiquant la valeur de `X-Request-Id`. |

## Voir aussi

- [`GET /brand/{brand_id}/did.json`](/reference/get-brand-did-json/),
  récupérer les clefs publiques de signature d'une marque.
- [`GET /passport/{identifier}/vc/verify`](/reference/get-passport-vc-verify/),
  contrôler la signature du justificatif et lire les données révélées.
- [Confiance et preuves](/confiance-et-preuves/),
  ce que chaque preuve établit et comment un tiers refait la vérification.
