@dotrino/identity 0.93.0 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/identity",
3
- "version": "0.93.0",
3
+ "version": "0.94.0",
4
4
  "description": "Identidad y rating de usuarios compartidos entre apps de Dotrino (vault iframe + postMessage)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,4 +1,4 @@
1
- Copia vendorizada de @dotrino/vault@0.64.0 (dotrino-vault/lib/src/{index,enroll,protocol}.js).
1
+ Copia vendorizada de @dotrino/vault@0.65.0 (dotrino-vault/lib/src/{index,enroll,protocol}.js).
2
2
  NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
3
3
  index.js importa ./enroll.js y ./protocol.js (relativos, van en esta misma copia),
4
4
  @dotrino/identity/{capabilities,acta} (= ../../{capabilities,acta}.js) y
@@ -349,23 +349,50 @@ export function createEnrollDesk ({
349
349
  return { ok: true, deviceId: pend.deviceId, adopting: true }
350
350
  }
351
351
 
352
- const { cert } = await identity.signDelegation(pend.dpub, pend.scope, { ttlMs: pend.ttlMs, label: pend.label })
353
-
354
352
  // Aprobar un emparejamiento ES admitir al dispositivo en el perfil: el cert es la
355
353
  // credencial y el acta es la política, y no tiene sentido emitir una sin la otra.
356
354
  // Las capacidades salen del scope que se pidió al emparejar (cert ∩ acta, §2.3).
357
- let record = null
355
+ //
356
+ // NADA DE REPLIEGUES (2026-09-17). Esto antes estaba envuelto en un `catch` que solo
357
+ // anotaba el error, y además se saltaba en silencio si la identidad no sabía admitir o si
358
+ // el scope no daba ninguna capacidad: el aparato recibía su certificado SIN estar en el
359
+ // acta, y el fallo aparecía después y en otro sitio. Ahora, si no se puede admitir, no
360
+ // se entrega nada y se dice.
361
+ if (typeof identity.admitMember !== 'function') {
362
+ throw Object.assign(new Error('this vault cannot add devices to the account record: no certificate was issued'), { code: 'admit-unavailable' })
363
+ }
364
+ // PERMISOS, no tipos (2026-08-22): las capacidades son las del scope ENTERO. Un
365
+ // cajón (`secrets:<ns>`) suma `secrets` y fija el CN; no borra lo demás — un bot
366
+ // con `sign,secrets:eco` firma como aparato del acta Y lee solo su cajón.
367
+ const cn = scopeToCn(pend.scope)
368
+ const caps = [...new Set([...scopeToCaps(pend.scope), ...(cn ? ['secrets'] : [])])]
369
+ if (!caps.length) {
370
+ throw Object.assign(new Error('the pairing scope grants no permission: no certificate was issued'), { code: 'empty-scope' })
371
+ }
372
+
373
+ const { cert } = await identity.signDelegation(pend.dpub, pend.scope, { ttlMs: pend.ttlMs, label: pend.label })
374
+
358
375
  try {
359
- if (typeof identity.admitMember === 'function') {
360
- // PERMISOS, no tipos (2026-08-22): las capacidades son las del scope ENTERO. Un
361
- // cajón (`secrets:<ns>`) suma `secrets` y fija el CN; no borra lo demás — un bot
362
- // con `sign,secrets:eco` firma como aparato del acta Y lee solo su cajón.
363
- const cn = scopeToCn(pend.scope)
364
- const caps = [...new Set([...scopeToCaps(pend.scope), ...(cn ? ['secrets'] : [])])]
365
- if (caps.length) await identity.admitMember({ pub: pend.dpub, encPub: pend.encPub || null, label: pend.label || '', cn, caps, cert, continuity: pend.continuity || null })
376
+ await identity.admitMember({ pub: pend.dpub, encPub: pend.encPub || null, label: pend.label || '', cn, caps, cert, continuity: pend.continuity || null })
377
+ } catch (e) {
378
+ // El certificado ya está firmado y no se entrega: se revoca, para que no quede uno
379
+ // válido suelto. Si ni eso sale, se dice también.
380
+ let revoked = true
381
+ try { await identity.revokeDelegation(cert.nonce) } catch (re) {
382
+ revoked = false
383
+ log('[vault] could not revoke the undelivered certificate %s: %s', cert.nonce, re.message)
366
384
  }
367
- record = (await identity.profileActa?.())?.acta || null
368
- } catch (e) { log('[vault] could not admit into the record:', e.message) }
385
+ audit('rejected', { what: 'approve', device: pend.deviceId, reason: 'admit-failed' })
386
+ reply(pend.from, { type: MSG_ERROR, error: 'the vault could not add this device to the account: pairing failed, try again' })
387
+ pending.delete(pend.token)
388
+ fire(onPendingChange)
389
+ log('[vault] could not add %s to the record, no certificate was delivered: %s', pend.deviceId, e.message)
390
+ throw Object.assign(
391
+ new Error(`could not add the device to the account record (${e.message}): no certificate was delivered${revoked ? '' : ', and the signed one could not be revoked'}`),
392
+ { code: 'admit-failed', cause: e }
393
+ )
394
+ }
395
+ const record = (await identity.profileActa?.())?.acta || null
369
396
 
370
397
  audit('enroll', { device: pend.deviceId, label: pend.label || '', scope: pend.scope })
371
398
  // Echamos el código tipeado junto al cert: el DISPOSITIVO acepta solo si coincide