ncantu f9fe0e3419 Website-skeleton partie connectée, contrat en dur, navigate-login; UserWallet pairing-relay-status, redirect; website-data, proxy data, cryptographie, fixKnowledge
**Motivations:**
- Partie connectée du skeleton accessible seulement si pairing satisfait + relais OK, avec page type skeleton (avatar, notifications).
- Éviter « Aucun service disponible » : contrat présent en dur dans la page, transmis à l’iframe ; navigation évidente ou automatique vers login.
- Sécuriser postMessage (origine UserWallet uniquement) ; déployer data sur le proxy et certificat data.certificator.4nkweb.com.
- Vulgariser cryptographie (ECDH, AES-GCM, Schnorr, workflow, collecte signatures) ; documenter correctifs et architecture.

**Root causes:**
- Section connectée affichée sans vérifier pairing/relay ; possibilité de forger pairing-relay-status depuis la console.
- Iframe masquée ou /login chargé avant réception du contrat → graphe vide, redirection vers /services.
- Pas de contrôle d’origine sur les messages reçus ; pas de projet website-data ni config Nginx/certificat pour data.

**Correctifs:**
- Vérification msg.origin === USERWALLET_ORIGIN dans handleMessage (skeleton).
- Si session mais pas pairingRelayStatus : afficher iframe pour réception du statut, message « Vérification du statut… ».
- Contrat envoyé dès load iframe (init iframe.src = USERWALLET_ORIGIN) ; au clic « Se connecter », envoi contract + navigate-login (service, membre).
- UserWallet : écoute navigate-login → navigation /login?service=&membre= ; LoginScreen avec service+membre en URL ne redirige plus vers /services, dispatch E_SELECT_SERVICE / E_SELECT_MEMBER.

**Evolutions:**
- Message pairing-relay-status (iframe → parent) ; canShowConnectedSection exige login + pairing OK + relay OK ; page connectée avec header avatar + icône notifications.
- Skeleton : getLoginContext, sendNavigateLoginToIframe, onIframeLoad, loginRequested/iframeLoaded ; contrat envoyé avec serviceUuid, membreUuid.
- UserWallet : PairingRelayStatusMessage, envoi depuis HomeScreen/LoginScreen ; type navigate-login, handleNavigateLogin dans useChannel.
- Page cryptographie.html (workflow, algorithmes, collecte signatures) ; liens nav, build.
- website-data (Vite, channel, config), start/service/install ; configure-nginx-proxy + Certbot pour data.certificator.4nkweb.com.
- fixKnowledge (postmessage-origin, section-connectee-non-affichee) ; features (partie-connectee-pairing-relay, userwallet-iframe-key-isolation).

**Pages affectées:**
- website-skeleton (index, main, config, serviceContract, cryptographie, technique, membre, contrat, vite.config, README).
- userwallet (HomeScreen, LoginScreen, useChannel, iframeChannel, relay, crypto, iframe, Pairing*, RelaySettings, WordInputGrid, syncUpdateGraph, specs/synthese).
- website-data (nouveau), configure-nginx-proxy, docs DOMAINS_AND_PORTS README, features, fixKnowledge, userwallet features/docs.
2026-01-29 00:55:58 +01:00

195 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# website-skeleton
Squelette d'un site qui intègre UserWallet en iframe : écoute des messages `postMessage` (login-proof, error, contract), vérification des preuves de login via `service-login-verify`, et affichage du statut (accepté / refusé). Connexion via un seul parcours : « Se connecter » puis authentification MFA dans l'iframe.
## Prérequis
- **service-login-verify** : `npm run build` dans `../service-login-verify` avant d'installer ou builder le skeleton.
- **UserWallet** : à servir sur l'URL configurée (voir cidessous) pour que l'iframe fonctionne.
## Installation
```bash
cd website-skeleton
npm install
npm run build
```
## Développement
```bash
npm run dev
```
Ouvre par défaut sur `http://localhost:3024`. L'iframe pointe vers UserWallet (`USERWALLET_ORIGIN`).
## Configuration
- **Origine UserWallet** : `src/config.ts` définit `USERWALLET_ORIGIN`. En dev, défaut `http://localhost:3018` (si UserWallet tourne en dev sur ce port). En prod, défaut `https://userwallet.certificator.4nkweb.com`. Pour override : variable d'environnement `VITE_USERWALLET_ORIGIN` (ex. `VITE_USERWALLET_ORIGIN=http://localhost:3018 npm run dev`).
- **Contrat de service** : Le skeleton a un contrat de service réel défini dans `src/serviceContract.ts` avec UUID `32b9095a-562d-4239-ae45-2d7ffb1a40de`. Le contrat est chargé automatiquement au démarrage et envoyé à l'iframe UserWallet.
- **Clé publique du service** : Configurez `VITE_SKELETON_SERVICE_PUBLIC_KEY` avec une clé publique secp256k1 compressée valide (66 hex chars, commençant par 02 ou 03). Exemple : `VITE_SKELETON_SERVICE_PUBLIC_KEY=02abc123... npm run dev`. Si non configurée, un placeholder est utilisé mais les signatures ne pourront pas être vérifiées.
- **Validateurs** : Les validateurs sont extraits automatiquement du contrat de service skeleton. Le skeleton peut aussi recevoir un contrat personnalisé via `postMessage` (type `contract`) qui remplacera le contrat par défaut.
### Chargement dynamique des contrats
Le skeleton écoute les messages `postMessage` de type `contract` pour recevoir le contrat et mettre à jour les validateurs dynamiquement :
```javascript
// Exemple d'envoi de contrat depuis le parent
window.postMessage({
type: 'contract',
payload: {
contrat: {
uuid: '...',
validateurs: { membres_du_role: [...] },
datajson: { types_names_chiffres: 'contrat' }
},
contrats_fils: [...], // Optionnel
actions: [...] // Optionnel, pour extraire l'action login
}
}, '*');
```
Le skeleton :
1. Reçoit le message `contract`
2. Extrait les validateurs de l'action login (recherche dans `actions` pour un type contenant "login")
3. Met à jour les `allowedPubkeys` utilisés pour la vérification
4. Affiche un statut de confirmation
Le skeleton utilise automatiquement le contrat de service skeleton au démarrage. Si un contrat personnalisé est reçu via `postMessage`, il remplace le contrat par défaut.
## Utilisation
1. Lancer UserWallet (dev ou déployé) sur l'URL configurée.
2. Lancer le skeleton (`npm run dev` ou servir `dist/`).
3. Ouvrir la page du skeleton : l'iframe affiche UserWallet.
4. **Envoyer contrat (optionnel)** : envoyer un message `contract` avec le contrat et ses actions pour mettre à jour les validateurs.
5. **Se connecter** : cliquer « Se connecter » → envoi du contrat à l'iframe, affichage de l'iframe.
6. **Login MFA** : depuis l'iframe, effectuer le flux de login (MFA) ; à la fin, UserWallet envoie `login-proof` au parent. Le skeleton vérifie la preuve (`verifyLoginProof`) et affiche « Login accepté » ou « Login refusé : … ».
7. **Description du contrat** : page `contrat.html` (lien depuis l'accueil) décrit le contrat de service skeleton.
8. **Description du membre** : page `membre.html` décrit le <strong>membre connecté</strong> (l'utilisateur), pas le validateur du service. Clés générées dans l'iframe (UserWallet), stockées en IndexedDB. Distinction créateur du service (wallet .env.private, jamais exposé) vs utilisateur (iframe, IndexedDB).
### Cloisonnement iframe
Le skeleton (parent) **ne signe, ne chiffre ni ne déchiffre jamais**. Il utilise uniquement `verifyLoginProof` (vérification de signatures avec clés publiques). Aucune clé privée nest reçue ni utilisée par le parent. Toutes les opérations de signature et de chiffrement restent dans liframe UserWallet. Voir `userwallet/features/userwallet-iframe-key-isolation.md`.
## Exemple d'intégration
### Envoi de contrat depuis le parent
```javascript
// Depuis la page parente qui héberge l'iframe
const iframe = document.getElementById('userwallet');
const userwalletOrigin = 'https://userwallet.example.com';
// Envoyer le contrat avec l'action login
iframe.contentWindow.postMessage({
type: 'contract',
payload: {
contrat: {
uuid: 'contrat-uuid-123',
validateurs: {
membres_du_role: [
{
membre_uuid: 'membre-uuid-456',
signatures_obligatoires: [
{
membre_uuid: 'membre-uuid-456',
cle_publique: '02abc123...', // Clé publique secp256k1
cardinalite_minimale: 1
}
]
}
]
},
datajson: {
types_names_chiffres: 'contrat'
}
},
contrats_fils: [], // Optionnel
actions: [
{
uuid: 'action-login-uuid',
types: {
types_names_chiffres: 'action-login',
types_uuid: ['action-uuid']
},
validateurs_action: {
membres_du_role: [
{
membre_uuid: 'membre-uuid-456',
signatures_obligatoires: [
{
membre_uuid: 'membre-uuid-456',
cle_publique: '02abc123...',
cardinalite_minimale: 1
}
]
}
]
}
}
]
}
}, userwalletOrigin);
```
### Réception et vérification de login-proof
Le skeleton écoute automatiquement les messages `login-proof` et vérifie la preuve :
```javascript
// Le skeleton fait automatiquement :
window.addEventListener('message', (event) => {
if (event.data?.type === 'login-proof') {
const result = verifyLoginProof(event.data.payload, {
allowedPubkeys, // Construit depuis les validateurs
nonceCache, // Cache anti-rejeu
timestampWindowMs: 300000 // 5 minutes
});
if (result.accept) {
// Ouvrir la session utilisateur
console.log('Login accepté');
} else {
// Refuser le login
console.error('Login refusé:', result.reason);
}
}
});
```
### Gestion des erreurs
Les raisons de refus possibles :
- `invalid_proof_structure` : Structure de la preuve invalide
- `timestamp_out_of_window` : Timestamp hors fenêtre (défaut ±5 min)
- `nonce_reused` : Nonce déjà utilisé (anti-rejeu)
- `validators_not_verifiable` : Aucune clé publique dans les validateurs
- `no_validator_signature` : Aucune signature valide de validateurs
- `signature_cle_publique_not_authorized` : Signature avec clé non autorisée
## Structure
- `index.html` : page avec iframe, liens « Description du contrat » / « Description du membre », bouton « Se connecter ».
- `src/main.ts` : chargement de l'iframe, écoute `message`, envoi du contrat au clic « Se connecter », appel à `verifyLoginProof`, gestion des messages `contract`.
- `contrat.html` : page de description du contrat de service skeleton (labels, UUIDs, usage).
- `membre.html` : page de description du <strong>membre connecté</strong> (utilisateur) et de son device (Pair) ; clés iframe / IndexedDB. Distinction service (.env.private) vs utilisateur.
- `cryptographie.html` : page explicative de la cryptographie utilisée (ECDH secp256k1, AES-256-GCM, SHA-256, Schnorr, HKDF) avec workflow complet et vulgarisation.
- `src/config.ts` : `USERWALLET_ORIGIN`, `DEFAULT_VALIDATEURS` (extraits du contrat skeleton), types `Contrat` et `Action`.
- `src/serviceContract.ts` : contrat de service skeleton avec UUID dédié, action login, et configuration de la clé publique via `VITE_SKELETON_SERVICE_PUBLIC_KEY`.
- `src/contract.ts` : extraction des validateurs depuis les contrats (`extractLoginValidators`), validation de structure (`isValidContract`, `isValidAction`).
## Déploiement
- **Production** : `https://skeleton.certificator.4nkweb.com` (proxy → 192.168.1.105:3024).
- **Installation** : `./install-website-skeleton.sh` sur le backend, puis `./update-proxy-nginx.sh` pour proxy + certificat. Voir `docs/WEBSITE_SKELETON.md`.
## Références
- `docs/WEBSITE_SKELETON.md` (documentation détaillée)
- `features/service-login-verify.md`
- `features/userwallet-contrat-login-reste-a-faire.md` (§ 3.7)
- `userwallet/` (iframe)
- `service-login-verify/` (vérification)