From 5ca8f80c452a2298ae8006be97b697ed3b29e1bb Mon Sep 17 00:00:00 2001 From: Baptiste LAFOURCADE Date: Wed, 22 Jul 2026 06:49:18 +0200 Subject: [PATCH] =?UTF-8?q?feat(verify):=20retire=20le=20QR=20affich=C3=A9?= =?UTF-8?q?=20(boucle),=20expose=20la=20preuve=20en=20fichier=20+=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le QR sur la page de vérif pointe vers cette page même -> boucle inutile. Retiré de l'affichage, gardé en téléchargement (à coller ailleurs). Expose la preuve de deux façons pour un vérificateur tiers : téléchargement du fichier ET son URL publique absolue (à donner à un outil qui prend une URL). Lien vers le validateur 1EdTech. +tests. Co-Authored-By: Claude Opus 4.8 --- site/verify-page.mjs | 29 +++++++++++++++-------------- site/verify-page.test.mjs | 11 ++++++----- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/site/verify-page.mjs b/site/verify-page.mjs index d43469c..db0c9b9 100644 --- a/site/verify-page.mjs +++ b/site/verify-page.mjs @@ -17,13 +17,15 @@ const fmtDate = (iso) => { }; /** - * Rend le résultat dans le conteneur. `base` = dossier du membre (sans slash final), - * pour que QR et téléchargements marchent que l'URL ait un slash final ou non. - * Pur DOM, testable via une racine injectée. + * Rend le résultat dans le conteneur. `base` = dossier du membre (sans slash final) ; + * `origin` = origine absolue (pour donner l'URL publique de la preuve). Le QR n'est + * PAS affiché ici (il pointerait vers cette page même — boucle) : seulement en + * téléchargement. Pur DOM, testable via une racine injectée. */ -export function render(root, result, base = '.') { +export function render(root, result, base = '.', origin = '') { const label = LABELS[result.state] || LABELS[STATE.INVALID]; const d = result.details || {}; + const proofUrl = `${origin}${base}/credential.jwt`; const rows = result.state === STATE.INVALID ? `

${result.reason || 'Ce badge n\'a pas pu être authentifié.'}

` : ` @@ -34,21 +36,19 @@ export function render(root, result, base = '.') {
Émis le
${fmtDate(d.validFrom)}
Valable jusqu'au
${fmtDate(d.validUntil)}
-
- QR code de vérification -
Scanne pour vérifier
-

- ⤓ QR code - ⤓ Preuve brute (VC-JWT) -

`; + ⤓ Télécharger la preuve + ⤓ QR code (à afficher ailleurs) +

+

Vérifier avec un outil indépendant : + dépose la preuve sur le validateur public 1EdTech, + ou donne-lui l'URL de la preuve :
${proofUrl}

`; root.className = `result ${label.tone}`; root.innerHTML = `

${label.title}

${rows} -

Signature vérifiée dans votre navigateur, sans dépendre d'un serveur AIDD. - Vérifier avec un outil tiers.

`; +

Signature vérifiée dans votre navigateur, sans dépendre d'un serveur AIDD.

`; } /** @@ -68,10 +68,11 @@ export function credentialUrl(pathname) { export async function mount(doc = document, fetchImpl = fetch) { const root = doc.getElementById('app'); const base = memberBase(doc.location && doc.location.pathname); + const origin = (doc.location && doc.location.origin) || ''; try { const jwt = await (await fetchImpl(`${base}/credential.jwt`)).text(); const result = await verifyBadge(jwt.trim()); - render(root, result, base); + render(root, result, base, origin); } catch { render(root, { state: STATE.INVALID, reason: 'Aucune preuve trouvée à cette adresse.' }); } diff --git a/site/verify-page.test.mjs b/site/verify-page.test.mjs index c15f638..fd61926 100644 --- a/site/verify-page.test.mjs +++ b/site/verify-page.test.mjs @@ -64,23 +64,24 @@ describe('render', () => { assert.match(root.innerHTML, /vc\.1ed\.tech/); }); - it('affiche le QR code et les téléchargements pour un badge authentifié', () => { + it('propose le téléchargement de la preuve et du QR, sans afficher le QR (pas de boucle)', () => { const root = fakeElement(); render(root, { state: STATE.VALID, details: { handle: 'jd' } }); - assert.match(root.innerHTML, /src="\.\/qr\.svg"/); + assert.doesNotMatch(root.innerHTML, /]*qr\.svg/); // pas de QR affiché (il boucle) assert.match(root.innerHTML, /href="\.\/qr\.svg" download/); assert.match(root.innerHTML, /href="\.\/credential\.jwt" download/); }); - it('utilise la base fournie pour QR et preuve (chemins absolus, URL sans slash)', () => { + it('expose l\'URL publique absolue de la preuve (pour un vérificateur tiers)', () => { const root = fakeElement(); - render(root, { state: STATE.VALID, details: { handle: 'jd' } }, '/u/jd'); + render(root, { state: STATE.VALID, details: { handle: 'jd' } }, '/u/jd', 'https://verify.ai-driven-dev.fr'); - assert.match(root.innerHTML, /src="\/u\/jd\/qr\.svg"/); assert.match(root.innerHTML, /href="\/u\/jd\/credential\.jwt" download/); + assert.match(root.innerHTML, /https:\/\/verify\.ai-driven-dev\.fr\/u\/jd\/credential\.jwt<\/code>/); + assert.match(root.innerHTML, /vc\.1ed\.tech/); }); it('n\'affiche ni QR ni téléchargements pour un badge invalide', () => {