Le reçu de consentement
Le reçu est l'artéfact présenté au citoyen et vérifiable de façon indépendante.
C'est un justificatif vérifiable (Verifiable Credential) du W3C qui porte les
éléments consentis et les deux preuves : le companySig réutilisé de
l'entreprise et l'assertion WebAuthn du citoyen sur le défi keccak d'Agreely.
Il ne porte délibérément jamais le sel, le consentRef ni l'engagement.
Ceux-ci restent dans l'enveloppe destructible afin que le reçu ne défasse pas la
non-corrélation et ne bloque pas
l'effacement. Comme l'offre, il est sans nombres : chaque valeur est une chaîne.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://agreely.ca/credentials/consent/v1"
],
"type": ["VerifiableCredential", "ConsentReceipt"],
"id": "urn:agreely:receipt:6f9619ff-8b86-d011-b42d-00cf4fc964ff",
"issuer": "did:web:agreely.ca:c:acme",
"validFrom": "2026-06-26T15:04:05Z",
"validUntil": "2031-01-01T00:00:00Z",
"credentialSubject": {
"id": "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B",
"consent": {
"action": "grant",
"assuranceLevel": "citizen_signed",
"items": [
{
"itemId": "0x9a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f7081",
"category": "phone number",
"purpose": "billing"
}
],
"requestId": "0x4b08245219c0e1a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f7081234",
"grantedAt": "2026-06-26T15:04:05Z",
"document": {
"code": "acme-billing-v3",
"name": "Consentement facturation v3",
"version": "3",
"effectiveDate": "2026-01-01",
"ipfsCid": "bafybeib..."
},
"disclosureHash": "0x…sha256…"
}
},
"proof": [
{
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"created": "2026-06-26T15:00:00Z",
"verificationMethod": "did:web:agreely.ca:c:acme#kms-1",
"proofPurpose": "assertionMethod",
"proofValue": "z3Ge8wQ5...base58btc..."
},
{
"type": "WebAuthnAssertion",
"verificationMethod": "did:agreely:citizen:9F8K2M4P7Q1R3T5V8W0X2Y4Z6B#passkey-1",
"nonce": "0x7081234b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f",
"challenge": "0xc0ffee...keccak256...",
"authenticatorData": "SZYN5YgOjGh0NBcP...base64url...",
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0Iiw...base64url...",
"signature": "MEUCIQD...base64url..."
}
]
}
Champ par champ
| Champ | Signification |
|---|---|
@context |
Le contexte VC v2 plus le contexte du justificatif de consentement d'Agreely. |
type |
["VerifiableCredential", "ConsentReceipt"]. |
id |
urn:agreely:receipt:{uuid}. |
issuer |
Le DID de l'entreprise (repris de l'issuer de l'offre). |
validFrom |
grantedAt, le moment où le citoyen a approuvé. |
validUntil |
Le validUntil de l'offre, la durée de vie du consentement. |
credentialSubject.id |
Le DID du citoyen, did:agreely:citizen:.... |
credentialSubject.consent.action |
grant. |
credentialSubject.consent.assuranceLevel |
Le niveau d'assurance public : citizen_signed pour ce reçu, company_attested pour la variante attestée par l'entreprise (voir plus bas). |
credentialSubject.consent.items |
Les éléments consentis (itemId, category normalisée, purpose normalisée). |
credentialSubject.consent.requestId |
Le requestId de l'offre d'origine. |
credentialSubject.consent.grantedAt |
Quand le consentement a été donné (RFC 3339). |
credentialSubject.consent.document |
Le descripteur de document : code, name, version, effectiveDate et, lorsque la version de la divulgation est épinglée sur IPFS, l'ipfsCid de la copie immuable sous laquelle le consentement a été donné (voir intégrité du document sur IPFS). L'ipfsCid est omis quand la version n'est pas épinglée. |
credentialSubject.consent.disclosureHash |
0x + le SHA-256 de la carte de divulgation canonicalisée. |
Deux émetteurs, ne les confondez pas
L'issuer est le DID de l'entreprise (did:web:agreely.ca:c:{slug}); le credentialSubject.id est le DID du citoyen (did:agreely:citizen:...). Ils utilisent des méthodes DID différentes à dessein. Voir documents DID.
La liaison de divulgation
consent.document lie le reçu à la version exacte de la divulgation sous laquelle
le consentement a été donné, par son code, sa version et, lorsque la version est épinglée, son ipfsCid sur IPFS.
consent.disclosureHash est 0x + le SHA-256 de la carte de divulgation
canonicalisée (JCS).
disclosureHash est du SHA-256, pas du keccak256
Soyez précis sur l'algorithme : disclosureHash est du SHA-256, tandis que les engagements par cellule, l'arbre de Merkle et le défi de la clé d'accès sont du keccak256. Ne mélangez pas les deux quand vous recalculez.
La divulgation de l'article 8 couvre les fins, les moyens de
collecte, les catégories, la conservation, le retrait du consentement, les
destinataires, la communication hors Québec, les droits, le responsable et toute
décision automatisée. Sa copie publique et récupérable à l'ipfsCid est décrite sous
intégrité du document sur IPFS.
Le tableau des preuves
proof est un tableau d'exactement deux preuves, et l'ordre est fixe.
proof[0] : la moitié entreprise
La preuve Data Integrity eddsa-jcs-2022, réutilisée mot pour mot depuis le
companySig de l'offre. Son verificationMethod se résout dans le document DID de
l'entreprise; elle signe l'offre d'origine (voir
la demande de consentement).
proof[1] : la moitié citoyen
Une WebAuthnAssertion sur le défi d'Agreely.
| Champ | Signification |
|---|---|
type |
WebAuthnAssertion. |
verificationMethod |
Le fragment de clé d'accès du DID citoyen, p. ex. ...#passkey-1. |
nonce |
Le nonce à usage unique de l'offre. |
challenge |
0x-hex `keccak256( JCS(context) |
authenticatorData |
Données brutes de l'authentificateur WebAuthn, base64url. |
clientDataJSON |
Données client WebAuthn brutes, base64url. Son challenge intégré égale le champ challenge ci-dessus. |
signature |
La signature de l'authentificateur, base64url. |
Effacer une seule cellule invalide la signature partagée
Comme l'entreprise a signé sur l'ensemble complet des éléments, effacer un élément d'un reçu à plusieurs éléments invalide companySig sur l'ensemble survivant : il n'y a pas de preuve à divulgation sélective dans la v1. Les survivants restent vérifiables de façon indépendante via leur commitment + la racine de Merkle + l'ancrage sur la chaîne, et non via le reçu caviardé. La divulgation sélective SD-JWT / BBS+ est le correctif différé.
La variante attestée par l'entreprise
Tous les consentements ne passent pas par une clé d'accès. Lorsqu'une personne a signé un formulaire papier à la main, l'entreprise émet un reçu attesté par l'entreprise (le flux manuel ou hors ligne). Il est bâti comme un reçu citoyen, avec trois différences :
- son
typeporte en plus"CompanyAttestedConsentReceipt"; consent.assuranceLevelvaut"company_attested"et n'est jamais omis;- son
credentialSubjectn'a aucunidcitoyen (il n'y a pas de DID citoyen à lier), et il porte un blocevidence; - son
proofest un tableau d'un seul élément : la moitié entreprise seulement, sans assertion WebAuthn.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://agreely.ca/credentials/consent/v1"
],
"type": ["VerifiableCredential", "ConsentReceipt", "CompanyAttestedConsentReceipt"],
"id": "urn:agreely:receipt:6f9619ff-8b86-d011-b42d-00cf4fc964ff",
"issuer": "did:web:agreely.ca:c:acme",
"validFrom": "2026-06-26T15:04:05Z",
"validUntil": "2031-01-01T00:00:00Z",
"credentialSubject": {
"consent": {
"action": "grant",
"assuranceLevel": "company_attested",
"items": [
{
"itemId": "0x9a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f7081",
"category": "phone number",
"purpose": "billing"
}
],
"requestId": "0x4b08245219c0e1a2b3c4d5e6f70819a2b3c4d5e6f70819a2b3c4d5e6f7081234",
"grantedAt": "2026-06-26T15:04:05Z",
"document": {
"code": "acme-billing-v3",
"name": "Consentement facturation v3",
"version": "3",
"effectiveDate": "2026-01-01",
"ipfsCid": "bafybeib..."
},
"disclosureHash": "0x…sha256…"
},
"evidence": {
"type": "HandSignedPaperConsent",
"pdfHash": "0x…sha256…",
"pdfHeldBy": "company",
"documentVersion": "3"
}
},
"proof": [
{
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"created": "2026-06-26T15:00:00Z",
"verificationMethod": "did:web:agreely.ca:c:acme#kms-1",
"proofPurpose": "assertionMethod",
"proofValue": "z3Ge8wQ5...base58btc..."
}
]
}
| Champ | Signification |
|---|---|
evidence.type |
HandSignedPaperConsent. |
evidence.pdfHash |
0x + le SHA-256 du PDF signé à la main. |
evidence.pdfHeldBy |
"company" ou "agreely-escrow", qui détient le PDF. |
evidence.documentVersion |
La version du document sous laquelle le formulaire a été signé. |
Ce qu'un reçu attesté prouve, et ce qu'il ne prouve pas
Un reçu attesté par l'entreprise prouve que l'entreprise a attesté détenir un PDF signé à la main dont le hachage correspond à evidence.pdfHash. Il ne prouve pas qu'un humain a signé : il n'y a aucune assertion de clé d'accès, seulement la signature de l'entreprise. C'est une attestation d'entreprise, pas une preuve cryptographique de la présence du citoyen.
Deux vocabulaires d'assurance
Il y a deux vocabulaires, et ils ne se mélangent pas :
- Le palier de stockage interne est
full|manual. - L'étiquette publique du reçu et de
/v1/checkestcitizen_signed|company_attested.
manual correspond à company_attested; full correspond à citizen_signed.
Faites la correspondance, ne mélangez jamais les deux vocabulaires.
Ensuite
- Vérifier un reçu : les quatre contrôles qui confirment ce document de bout en bout.