From fbed49c701899753b485332b60a59b686c0f25a2 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Mon, 1 Jun 2026 16:21:00 +0200 Subject: [PATCH 1/4] feat(filesharing): accept signer name from passport/idcard/drivinglicence Replace the previous (PR #239) mandatory pbdf.gemeente.personalData.fullname disclosure with a Yivi disjunction-of-conjunctions: the signer must disclose a name, but they may satisfy that from any one of four credentials -- gemeente fullname, OR firstName+lastName from pbdf.pbdf.{passport,idcard,drivinglicence}. This addresses dobby's review of #239: requiring only the gemeente credential silently locked out everyone without a Dutch municipality attestation. The disjunction is mandatory -- disclosure refuses to complete unless at least one option is satisfied. Optional mobilenumber and dateofbirth remain unchanged. The disjunction lives in a new signAttributes.ts module exporting a typed AttrConItem[] consumed by SendButton.svelte. Splitting it out keeps the component file focused and the attribute list reviewable in isolation. Locale copy updates flagged by dobby on en.json/nl.json: - emailSenderSubHeading describes the four-credential rule. - yiviTip no longer frames the name as optional. The $derived.by + (!canEncrypt) call-site fixes from #239 are preserved. Depends on encryption4all/postguard#198 (PKG), postguard-js#78 (sign.yivi attrs), and cryptify#170 (render firstName+lastName). Supersedes #239 in this repo. npm run check: 0 errors, 0 warnings. --- .../components/filesharing/SendButton.svelte | 25 ++++--------- .../components/filesharing/signAttributes.ts | 37 +++++++++++++++++++ src/lib/locales/en.json | 4 +- src/lib/locales/nl.json | 4 +- 4 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 src/lib/components/filesharing/signAttributes.ts diff --git a/src/lib/components/filesharing/SendButton.svelte b/src/lib/components/filesharing/SendButton.svelte index e4e43db..3650708 100644 --- a/src/lib/components/filesharing/SendButton.svelte +++ b/src/lib/components/filesharing/SendButton.svelte @@ -19,6 +19,7 @@ import { MAX_UPLOAD_SIZE, ROLLING_LIMIT } from '$lib/env' import { parseLimitExceededBody, bytesToGB } from '$lib/usage' import { recordUpload, getLocalUsedBytes } from '$lib/localUsage' + import { SIGN_ATTRIBUTES } from './signAttributes' interface props { encryptState: EncryptState @@ -37,7 +38,7 @@ const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ - let canEncrypt = $derived(() => { + let canEncrypt = $derived.by(() => { if (encryptState.files.length === 0) return false const totalSize = encryptState.files.reduce((a, f) => a + f.size, 0) if (totalSize >= MAX_UPLOAD_SIZE) return false @@ -138,7 +139,7 @@ await tick() try { - if (!canEncrypt()) return + if (!canEncrypt) return // Build recipients const recipients = encryptState.recipients.map( @@ -151,23 +152,13 @@ } ) - // Build sign method — email always included, other attributes optional + // Build sign method — email and a name attribute are always + // required so the recipient mail can show a real name. The + // name may come from any one of four credentials; see + // signAttributes.ts for the disjunction. const sign = pg.sign.yivi({ element: '#crypt-irma-qr', - attributes: [ - { - t: 'pbdf.gemeente.personalData.fullname', - optional: true, - }, - { - t: 'pbdf.sidn-pbdf.mobilenumber.mobilenumber', - optional: true, - }, - { - t: 'pbdf.gemeente.personalData.dateofbirth', - optional: true, - }, - ], + attributes: SIGN_ATTRIBUTES, includeSender: true, }) diff --git a/src/lib/components/filesharing/signAttributes.ts b/src/lib/components/filesharing/signAttributes.ts new file mode 100644 index 0000000..b750ad7 --- /dev/null +++ b/src/lib/components/filesharing/signAttributes.ts @@ -0,0 +1,37 @@ +import type { AttrConItem } from '@e4a/pg-js' + +/** + * Yivi attributes the sender must (or may) disclose when signing a + * PostGuard file share. The PostGuard PKG prepends the email attribute, + * so it's not listed here. + * + * The first entry is the **mandatory name disjunction** — the sender + * proves their name from any one of four credentials: + * + * - `pbdf.gemeente.personalData.fullname` (Dutch municipality), OR + * - `pbdf.pbdf.passport.{firstName,lastName}`, OR + * - `pbdf.pbdf.idcard.{firstName,lastName}`, OR + * - `pbdf.pbdf.drivinglicence.{firstName,lastName}`. + * + * The remaining entries are unchanged optional extras from before + * postguard#239 — kept in the legacy flat shape with `optional: true`. + */ +export const SIGN_ATTRIBUTES: AttrConItem[] = [ + [ + [{ t: 'pbdf.gemeente.personalData.fullname' }], + [ + { t: 'pbdf.pbdf.passport.firstName' }, + { t: 'pbdf.pbdf.passport.lastName' }, + ], + [ + { t: 'pbdf.pbdf.idcard.firstName' }, + { t: 'pbdf.pbdf.idcard.lastName' }, + ], + [ + { t: 'pbdf.pbdf.drivinglicence.firstName' }, + { t: 'pbdf.pbdf.drivinglicence.lastName' }, + ], + ], + { t: 'pbdf.sidn-pbdf.mobilenumber.mobilenumber', optional: true }, + { t: 'pbdf.gemeente.personalData.dateofbirth', optional: true }, +] diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json index 2df57fd..8fe444f 100644 --- a/src/lib/locales/en.json +++ b/src/lib/locales/en.json @@ -202,7 +202,7 @@ "emailSender": "Email address", "emailSenderHeading": "Your information", "emailSenderSubHeadingToggle": "Why do you need this information?", - "emailSenderSubHeading": "Let the recipient(s) know these files are from you. Before sending, you sign the files by proving your email address and any additional personal data with the Yivi app.", + "emailSenderSubHeading": "Let the recipient(s) know these files are from you. Before sending, you sign the files with the Yivi app by proving your email address and your name (from a Dutch municipality credential, or from a passport, ID card, or driving licence).", "messageHeading": "Message (optional)", "messageText": "This message will not be encrypted and will be included in the notification email.", "messagePlaceholder": "Type your message here...", @@ -210,7 +210,7 @@ "yiviInfo": "What is Yivi?", "yiviInfoText": "Yivi is a free and privacy-friendly authentication app. With Yivi you can prove who you are by selectively sharing personal data, such as your email address, phone number, or name. At PostGuard we use Yivi to securely encrypt and decrypt files.", "yiviInfoLink": "Learn more about Yivi", - "yiviTip": "Tip: In the Yivi app you can add optional data. This way you let the recipient(s) know for sure that these files come from you.", + "yiviTip": "Tip: In the Yivi app you can also add an optional phone number or date of birth. This way you let the recipient(s) know for sure that these files come from you.", "sending": "Your files are being sent", "retrying": "Connection hiccup, retrying… (attempt {attempt} of {max})", "encrypting": "Encrypting & uploading...", diff --git a/src/lib/locales/nl.json b/src/lib/locales/nl.json index 173f5be..2e6ae3b 100644 --- a/src/lib/locales/nl.json +++ b/src/lib/locales/nl.json @@ -201,7 +201,7 @@ "emailSender": "E-mailadres", "emailSenderHeading": "Jouw gegevens", "emailSenderSubHeadingToggle": "Waarom heb je deze gegevens nodig?", - "emailSenderSubHeading": "Laat de ontvanger(s) weten dat deze bestanden van jou afkomstig zijn. Voor het verzenden onderteken je de bestanden door je e-mailadres en eventuele aanvullende persoonlijke gegevens aan te tonen met de Yivi-app.", + "emailSenderSubHeading": "Laat de ontvanger(s) weten dat deze bestanden van jou afkomstig zijn. Voor het verzenden onderteken je de bestanden met de Yivi-app door je e-mailadres en je naam aan te tonen (afkomstig uit je gemeente, paspoort, ID-kaart of rijbewijs).", "messageHeading": "Bericht (optioneel)", "messageText": "Dit bericht wordt niet versleuteld en wordt opgenomen in de notificatie-e-mail.", "messagePlaceholder": "Typ hier je bericht...", @@ -209,7 +209,7 @@ "yiviInfo": "Wat is Yivi?", "yiviInfoText": "Yivi is een gratis en privacy-vriendelijke authenticatie-app. Met Yivi kun je bewijzen wie je bent door selectief persoonlijke gegevens te delen, zoals je e-mailadres, telefoonnummer of naam. Bij PostGuard gebruiken we Yivi om bestanden veilig te versleutelen en ontsleutelen.", "yiviInfoLink": "Meer informatie over Yivi", - "yiviTip": "Tip: In de Yivi-app kun je optionele gegevens toevoegen. Zo laat je de ontvanger(s) zeker weten dat deze bestanden van jou komen.", + "yiviTip": "Tip: In de Yivi-app kun je ook een optioneel telefoonnummer of geboortedatum toevoegen. Zo laat je de ontvanger(s) zeker weten dat deze bestanden van jou komen.", "sending": "Je bestanden worden verzonden", "retrying": "Verbindingshapering, opnieuw proberen… (poging {attempt} van {max})", "encrypting": "Ondertekenen & verzenden...", From 06c08dffa3de41ba52645a6565407cb7b8b86a51 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Tue, 2 Jun 2026 09:25:11 +0200 Subject: [PATCH 2/4] feat(filesharing): only require email on sign step; name is optional Remove the mandatory name disjunction from SIGN_ATTRIBUTES so senders only need to prove their email address (the pre-#239 UX). The condiscon infrastructure in postguard/pg-js remains available for future use. Update locale strings to no longer mention the name requirement. --- .../components/filesharing/signAttributes.ts | 33 +++---------------- src/lib/locales/en.json | 2 +- src/lib/locales/nl.json | 2 +- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/src/lib/components/filesharing/signAttributes.ts b/src/lib/components/filesharing/signAttributes.ts index b750ad7..3d720ee 100644 --- a/src/lib/components/filesharing/signAttributes.ts +++ b/src/lib/components/filesharing/signAttributes.ts @@ -1,37 +1,12 @@ import type { AttrConItem } from '@e4a/pg-js' /** - * Yivi attributes the sender must (or may) disclose when signing a - * PostGuard file share. The PostGuard PKG prepends the email attribute, - * so it's not listed here. - * - * The first entry is the **mandatory name disjunction** — the sender - * proves their name from any one of four credentials: - * - * - `pbdf.gemeente.personalData.fullname` (Dutch municipality), OR - * - `pbdf.pbdf.passport.{firstName,lastName}`, OR - * - `pbdf.pbdf.idcard.{firstName,lastName}`, OR - * - `pbdf.pbdf.drivinglicence.{firstName,lastName}`. - * - * The remaining entries are unchanged optional extras from before - * postguard#239 — kept in the legacy flat shape with `optional: true`. + * Yivi attributes the sender may optionally disclose when signing a + * PostGuard file share. The PostGuard PKG prepends the mandatory email + * attribute, so it's not listed here. Everything here is optional — the + * sender only needs to prove their email address. */ export const SIGN_ATTRIBUTES: AttrConItem[] = [ - [ - [{ t: 'pbdf.gemeente.personalData.fullname' }], - [ - { t: 'pbdf.pbdf.passport.firstName' }, - { t: 'pbdf.pbdf.passport.lastName' }, - ], - [ - { t: 'pbdf.pbdf.idcard.firstName' }, - { t: 'pbdf.pbdf.idcard.lastName' }, - ], - [ - { t: 'pbdf.pbdf.drivinglicence.firstName' }, - { t: 'pbdf.pbdf.drivinglicence.lastName' }, - ], - ], { t: 'pbdf.sidn-pbdf.mobilenumber.mobilenumber', optional: true }, { t: 'pbdf.gemeente.personalData.dateofbirth', optional: true }, ] diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json index 8fe444f..f667937 100644 --- a/src/lib/locales/en.json +++ b/src/lib/locales/en.json @@ -202,7 +202,7 @@ "emailSender": "Email address", "emailSenderHeading": "Your information", "emailSenderSubHeadingToggle": "Why do you need this information?", - "emailSenderSubHeading": "Let the recipient(s) know these files are from you. Before sending, you sign the files with the Yivi app by proving your email address and your name (from a Dutch municipality credential, or from a passport, ID card, or driving licence).", + "emailSenderSubHeading": "Let the recipient(s) know these files are from you. Before sending, you sign the files with the Yivi app by proving your email address.", "messageHeading": "Message (optional)", "messageText": "This message will not be encrypted and will be included in the notification email.", "messagePlaceholder": "Type your message here...", diff --git a/src/lib/locales/nl.json b/src/lib/locales/nl.json index 2e6ae3b..9474f32 100644 --- a/src/lib/locales/nl.json +++ b/src/lib/locales/nl.json @@ -201,7 +201,7 @@ "emailSender": "E-mailadres", "emailSenderHeading": "Jouw gegevens", "emailSenderSubHeadingToggle": "Waarom heb je deze gegevens nodig?", - "emailSenderSubHeading": "Laat de ontvanger(s) weten dat deze bestanden van jou afkomstig zijn. Voor het verzenden onderteken je de bestanden met de Yivi-app door je e-mailadres en je naam aan te tonen (afkomstig uit je gemeente, paspoort, ID-kaart of rijbewijs).", + "emailSenderSubHeading": "Laat de ontvanger(s) weten dat deze bestanden van jou afkomstig zijn. Voor het verzenden onderteken je de bestanden met de Yivi-app door je e-mailadres aan te tonen.", "messageHeading": "Bericht (optioneel)", "messageText": "Dit bericht wordt niet versleuteld en wordt opgenomen in de notificatie-e-mail.", "messagePlaceholder": "Typ hier je bericht...", From 49694ebc02a228c97e7771c556fae96213195d53 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Tue, 2 Jun 2026 09:30:02 +0200 Subject: [PATCH 3/4] fix: remove AttrConItem import (not yet in published pg-js) --- src/lib/components/filesharing/signAttributes.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/components/filesharing/signAttributes.ts b/src/lib/components/filesharing/signAttributes.ts index 3d720ee..37a12a9 100644 --- a/src/lib/components/filesharing/signAttributes.ts +++ b/src/lib/components/filesharing/signAttributes.ts @@ -1,12 +1,10 @@ -import type { AttrConItem } from '@e4a/pg-js' - /** * Yivi attributes the sender may optionally disclose when signing a * PostGuard file share. The PostGuard PKG prepends the mandatory email * attribute, so it's not listed here. Everything here is optional — the * sender only needs to prove their email address. */ -export const SIGN_ATTRIBUTES: AttrConItem[] = [ +export const SIGN_ATTRIBUTES = [ { t: 'pbdf.sidn-pbdf.mobilenumber.mobilenumber', optional: true }, { t: 'pbdf.gemeente.personalData.dateofbirth', optional: true }, ] From c4ff46002e820069ff6a0892f147711609d8033b Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Tue, 2 Jun 2026 09:53:18 +0200 Subject: [PATCH 4/4] feat(filesharing): optional name disjunction via pg-js 1.11.0 condiscon Bump @e4a/pg-js to 1.11.0 which adds AttrDiscon support. Use it to request the sender's name optionally from any of four credentials (gemeente fullname, passport, ID card, or driving licence). The leading empty alternative keeps the group skippable for senders without any of these credentials. --- package-lock.json | 8 ++-- package.json | 2 +- .../components/filesharing/SendButton.svelte | 6 +-- .../components/filesharing/signAttributes.ts | 37 ++++++++++++++++--- src/lib/locales/en.json | 2 +- src/lib/locales/nl.json | 2 +- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index d3a610f..14f8f7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.7.0", "dependencies": { "@deltablot/dropzone": "^7.4.3", - "@e4a/pg-js": "^1.10.0", + "@e4a/pg-js": "^1.11.0", "@iconify/svelte": "^5.2.1", "@privacybydesign/yivi-css": "^1.0.1", "country-flag-icons": "^1.6.17", @@ -59,9 +59,9 @@ } }, "node_modules/@e4a/pg-js": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@e4a/pg-js/-/pg-js-1.10.0.tgz", - "integrity": "sha512-grC1snIzsM2+VEL/oFRWz4+c2IRaLFR5duV8XUKso92S97r3e8z2IEZHXblS4uNtJEtOlbA+UA/1UWEHi0mimg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@e4a/pg-js/-/pg-js-1.11.0.tgz", + "integrity": "sha512-EKg6Bqv+frHD5Wx2xlbFaGCJ82za27E7s4Zi63GtucSZRWzON8D+NGZ7WNqA/ghMuNIgZObYMM60D/w4M5TyUA==", "license": "MIT", "dependencies": { "@e4a/pg-wasm": "^0.6.1", diff --git a/package.json b/package.json index e7846fc..8d27c79 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ }, "dependencies": { "@deltablot/dropzone": "^7.4.3", - "@e4a/pg-js": "^1.10.0", + "@e4a/pg-js": "^1.11.0", "@iconify/svelte": "^5.2.1", "@privacybydesign/yivi-css": "^1.0.1", "country-flag-icons": "^1.6.17", diff --git a/src/lib/components/filesharing/SendButton.svelte b/src/lib/components/filesharing/SendButton.svelte index 3650708..15dc88b 100644 --- a/src/lib/components/filesharing/SendButton.svelte +++ b/src/lib/components/filesharing/SendButton.svelte @@ -152,10 +152,8 @@ } ) - // Build sign method — email and a name attribute are always - // required so the recipient mail can show a real name. The - // name may come from any one of four credentials; see - // signAttributes.ts for the disjunction. + // Build sign method — email is always required; name is optional + // (any of four credentials accepted). See signAttributes.ts. const sign = pg.sign.yivi({ element: '#crypt-irma-qr', attributes: SIGN_ATTRIBUTES, diff --git a/src/lib/components/filesharing/signAttributes.ts b/src/lib/components/filesharing/signAttributes.ts index 37a12a9..fb10b9c 100644 --- a/src/lib/components/filesharing/signAttributes.ts +++ b/src/lib/components/filesharing/signAttributes.ts @@ -1,10 +1,37 @@ +import type { AttrConItem } from '@e4a/pg-js' + /** - * Yivi attributes the sender may optionally disclose when signing a - * PostGuard file share. The PostGuard PKG prepends the mandatory email - * attribute, so it's not listed here. Everything here is optional — the - * sender only needs to prove their email address. + * Yivi attributes the sender may disclose when signing a PostGuard file + * share. The PKG prepends the mandatory email attribute automatically. + * + * The first entry is an optional name disjunction — the sender may prove + * their name from any one of four credentials, or skip entirely: + * + * - `pbdf.gemeente.personalData.fullname` (Dutch municipality), OR + * - `pbdf.pbdf.passport.{firstName,lastName}`, OR + * - `pbdf.pbdf.idcard.{firstName,lastName}`, OR + * - `pbdf.pbdf.drivinglicence.{firstName,lastName}`. + * + * The leading `[]` alternative makes the whole group optional per Yivi + * convention — senders without any of these credentials can still send. */ -export const SIGN_ATTRIBUTES = [ +export const SIGN_ATTRIBUTES: AttrConItem[] = [ + [ + [], + [{ t: 'pbdf.gemeente.personalData.fullname' }], + [ + { t: 'pbdf.pbdf.passport.firstName' }, + { t: 'pbdf.pbdf.passport.lastName' }, + ], + [ + { t: 'pbdf.pbdf.idcard.firstName' }, + { t: 'pbdf.pbdf.idcard.lastName' }, + ], + [ + { t: 'pbdf.pbdf.drivinglicence.firstName' }, + { t: 'pbdf.pbdf.drivinglicence.lastName' }, + ], + ], { t: 'pbdf.sidn-pbdf.mobilenumber.mobilenumber', optional: true }, { t: 'pbdf.gemeente.personalData.dateofbirth', optional: true }, ] diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json index f667937..2eed2ec 100644 --- a/src/lib/locales/en.json +++ b/src/lib/locales/en.json @@ -202,7 +202,7 @@ "emailSender": "Email address", "emailSenderHeading": "Your information", "emailSenderSubHeadingToggle": "Why do you need this information?", - "emailSenderSubHeading": "Let the recipient(s) know these files are from you. Before sending, you sign the files with the Yivi app by proving your email address.", + "emailSenderSubHeading": "Let the recipient(s) know these files are from you. Before sending, you sign the files with the Yivi app by proving your email address. You can optionally also share your name from a municipality, passport, ID card, or driving licence.", "messageHeading": "Message (optional)", "messageText": "This message will not be encrypted and will be included in the notification email.", "messagePlaceholder": "Type your message here...", diff --git a/src/lib/locales/nl.json b/src/lib/locales/nl.json index 9474f32..4e3575c 100644 --- a/src/lib/locales/nl.json +++ b/src/lib/locales/nl.json @@ -201,7 +201,7 @@ "emailSender": "E-mailadres", "emailSenderHeading": "Jouw gegevens", "emailSenderSubHeadingToggle": "Waarom heb je deze gegevens nodig?", - "emailSenderSubHeading": "Laat de ontvanger(s) weten dat deze bestanden van jou afkomstig zijn. Voor het verzenden onderteken je de bestanden met de Yivi-app door je e-mailadres aan te tonen.", + "emailSenderSubHeading": "Laat de ontvanger(s) weten dat deze bestanden van jou afkomstig zijn. Voor het verzenden onderteken je de bestanden met de Yivi-app door je e-mailadres aan te tonen. Je kunt optioneel ook je naam delen vanuit je gemeente, paspoort, ID-kaart of rijbewijs.", "messageHeading": "Bericht (optioneel)", "messageText": "Dit bericht wordt niet versleuteld en wordt opgenomen in de notificatie-e-mail.", "messagePlaceholder": "Typ hier je bericht...",