157 lines
7.0 KiB
Markdown
157 lines
7.0 KiB
Markdown
# Intégration iframe – Spécification de messagerie
|
||
|
||
Ce document définit les échanges entre un site hôte et `ihm_client` intégré en iframe, via `window.postMessage`. Aucun exemple de code exécutable n’est inclus.
|
||
|
||
## Principes
|
||
|
||
- Canal: postMessage entre la page parente et l’iframe `ihm_client`.
|
||
- Découverte: `ihm_client` émet `LISTENING` pour signaler la disponibilité des listeners (côté iframe). En mode E2E (`?e2e=1`) et seulement lorsque l’app tourne en iframe, `LISTENING` est émis périodiquement (jusqu’à 100 fois) pour éviter les courses de tests. Hors E2E, pas d’émission périodique.
|
||
- Sécurité:
|
||
- Utiliser la cible `event.origin` pour les réponses.
|
||
- De nombreuses opérations exigent un `accessToken` valide (lié à `origin`).
|
||
- `messageId` recommandé pour corréler requêtes/réponses.
|
||
- Pré-requis: pour la plupart des actions de processus, l’appareil doit être appairé.
|
||
|
||
## Types de message
|
||
|
||
Référence: `src/models/process.model.ts` (enum `MessageType`).
|
||
|
||
### LISTENING
|
||
- Émis par `ihm_client` au parent pour signaler la disponibilité des listeners.
|
||
- Réception côté parent (aucune requête attendue). Pas de payload spécifique.
|
||
|
||
---
|
||
|
||
### REQUEST_LINK (Requête)
|
||
- Objet: ouverture d’un consentement utilisateur pour lier l’hôte à l’appareil.
|
||
- Requête (parent → iframe):
|
||
- `type`: `REQUEST_LINK`
|
||
- `messageId`: string (optionnel mais recommandé)
|
||
- Réponses (iframe → parent):
|
||
- Succès: `type`: `LINK_ACCEPTED`, `accessToken`: string, `refreshToken`: string, `messageId`
|
||
- Erreur: `type`: `ERROR`, `error`: string, `messageId`
|
||
|
||
### VALIDATE_TOKEN (Requête)
|
||
- Objet: vérifier la validité du couple `{accessToken, refreshToken}`.
|
||
- Requête: `type`, `accessToken`, `refreshToken`, `messageId`
|
||
- Réponse: `type`: `VALIDATE_TOKEN`, `isValid`: boolean, `accessToken`, `refreshToken`, `messageId`
|
||
|
||
### RENEW_TOKEN (Requête)
|
||
- Objet: obtenir un nouveau `accessToken` depuis un `refreshToken`.
|
||
- Requête: `type`, `refreshToken`, `messageId`
|
||
- Réponses:
|
||
- Succès: `type`: `RENEW_TOKEN`, `accessToken`, `refreshToken`, `messageId`
|
||
- Erreur: `ERROR`
|
||
|
||
---
|
||
|
||
### CREATE_PAIRING (Requête)
|
||
- Objet: initier l’appairage de l’appareil.
|
||
- Pré-requis: `accessToken` valide; appareil non appairé.
|
||
- Requête: `type`, `accessToken`, `messageId`
|
||
- Réponses:
|
||
- Succès: `type`: `PAIRING_CREATED`, `pairingId`: string, `messageId`
|
||
- Erreur: `ERROR`
|
||
|
||
### GET_PAIRING_ID (Requête)
|
||
- Objet: récupérer l’identifiant d’appairage utilisateur.
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête: `type`, `accessToken`, `messageId`
|
||
- Réponse: `type`: `GET_PAIRING_ID`, `userPairingId`: string, `messageId`
|
||
|
||
---
|
||
|
||
### GET_MY_PROCESSES (Requête)
|
||
- Objet: lister les processus auxquels l’utilisateur participe.
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête: `type`, `accessToken`, `messageId`
|
||
- Réponse: `type`: `GET_MY_PROCESSES`, `myProcesses`: string[] | null, `messageId`
|
||
|
||
### GET_PROCESSES (Requête)
|
||
- Objet: récupérer tous les processus connus localement.
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête: `type`, `accessToken`, `messageId`
|
||
- Réponse: `type`: `PROCESSES_RETRIEVED`, `processes`: Record<string, Process>, `messageId`
|
||
|
||
---
|
||
|
||
### RETRIEVE_DATA (Requête)
|
||
- Objet: déchiffrer et retourner les attributs autorisés d’un état de processus.
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête: `type`, `processId`: string, `stateId`: string (32 bytes hex), `accessToken`, `messageId`
|
||
- Réponse: `type`: `DATA_RETRIEVED`, `data`: Record<string, any>, `messageId`
|
||
|
||
### DECODE_PUBLIC_DATA (Requête)
|
||
- Objet: décoder des données publiques encodées.
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête: `type`, `accessToken`, `encodedData`: number[], `messageId`
|
||
- Réponse: `type`: `PUBLIC_DATA_DECODED`, `decodedData`: any, `messageId`
|
||
|
||
---
|
||
|
||
### CREATE_PROCESS (Requête)
|
||
- Objet: créer un nouveau processus.
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête:
|
||
- `type`
|
||
- `processData`: Record<string, any> (données brutes à répartir en public/privé)
|
||
- `privateFields`: string[] (liste des nouveaux champs à forcer en privé)
|
||
- `roles`: Record<string, RoleDefinition>
|
||
- `accessToken`, `messageId`
|
||
- Réponse succès: `type`: `PROCESS_CREATED`, `processCreated`: { `processId`: string, `process`: Process, `processData`: Record<string, any> }, `messageId`
|
||
- Réponse erreur: `ERROR`
|
||
|
||
### UPDATE_PROCESS (Requête)
|
||
- Objet: créer un nouvel état avec des mises à jour (champs publics/privés déterminés automatiquement, `privateFields` pour les nouveaux champs privés).
|
||
- Pré-requis: appairé + `accessToken` valide + processus existant avec état committé.
|
||
- Requête: `type`, `processId`, `newData`: Record<string, any>, `privateFields`: string[], `roles` (ou null pour conserver), `accessToken`, `messageId`
|
||
- Réponse: `type`: `PROCESS_UPDATED`, `updatedProcess`: Process, `messageId`
|
||
|
||
---
|
||
|
||
### NOTIFY_UPDATE (Requête)
|
||
- Objet: demander l’émission d’un PRD Update pour un état précis.
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête: `type`, `processId`, `stateId` (32 bytes hex), `accessToken`, `messageId`
|
||
- Réponse: `type`: `UPDATE_NOTIFIED`, `messageId`
|
||
|
||
### VALIDATE_STATE (Requête)
|
||
- Objet: valider un état (selon règles/permissions WASM).
|
||
- Pré-requis: appairé + `accessToken` valide.
|
||
- Requête: `type`, `processId`, `stateId` (32 bytes hex), `accessToken`, `messageId`
|
||
- Réponse: `type`: `STATE_VALIDATED`, `validatedProcess`: Process, `messageId`
|
||
|
||
---
|
||
|
||
### HASH_VALUE (Requête)
|
||
- Objet: obtenir le hash d’un blob (document) pour un engagement donné.
|
||
- Requête: `type`, `accessToken`, `commitedIn`: string, `label`: string, `fileBlob`: { type: string; data: Uint8Array }, `messageId`
|
||
- Réponse: `type`: `VALUE_HASHED`, `hash`: string, `messageId`
|
||
|
||
### GET_MERKLE_PROOF (Requête)
|
||
- Objet: récupérer la preuve Merkle d’un attribut dans un état.
|
||
- Requête: `type`, `accessToken`, `processState`: ProcessState, `attributeName`: string, `messageId`
|
||
- Réponse: `type`: `MERKLE_PROOF_RETRIEVED`, `proof`: MerkleProofResult, `messageId`
|
||
|
||
### VALIDATE_MERKLE_PROOF (Requête)
|
||
- Objet: valider une preuve Merkle pour un document donné.
|
||
- Requête: `type`, `accessToken`, `merkleProof`: string (JSON sérialisé), `documentHash`: string, `messageId`
|
||
- Réponse: `type`: `MERKLE_PROOF_VALIDATED`, `isValid`: boolean, `messageId`
|
||
|
||
---
|
||
|
||
## Erreurs communes (`ERROR`)
|
||
- Conditions typiques:
|
||
- `Device not paired` (opération nécessitant appairage)
|
||
- `Invalid or expired session token`
|
||
- `Failed to ...` (création/lecture/validation)
|
||
- `Invalid state id` / `Process not found`
|
||
- Payload générique: `{ type: 'ERROR', error: string, messageId?: string }`
|
||
|
||
## Bonnes pratiques d’intégration
|
||
- Toujours préciser et propager `messageId` pour tracer les échanges.
|
||
- Conserver `origin` côté parent et utiliser la même valeur dans les requêtes suivantes.
|
||
- Renouveler régulièrement le `accessToken` via `RENEW_TOKEN`.
|
||
- Séparer données publiques/privées dès la création d’un processus.
|
||
- Valider les formats (ids hex 32 bytes, structures de rôles) avant envoi.
|