@dotrino/identity 0.102.0 → 0.102.2

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.102.0",
3
+ "version": "0.102.2",
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",
package/vault/core.js CHANGED
@@ -1336,6 +1336,8 @@ export async function createIdentityCore ({ kv: hostKv, peers, makeSync = null,
1336
1336
  // Un cert YA vencido o revocado no puede renovarse (ahí sí, re-emparejar).
1337
1337
  // Ya no hay ventana de caducidad: el papel no vence. Lo único que obliga a pedir uno
1338
1338
  // nuevo es que el ACTA diga algo distinto de lo que lleva escrito.
1339
+ /** Rechazos de la bóveda que se arreglan con un papel nuevo (el mismo juego que la app nativa). */
1340
+ const PAPER_REASONS = new Set(['scope', 'acta-vieja', 'untrusted-issuer', 'expired', 'legacy-cert-retirado', 'no-acta'])
1339
1341
  const RENEW_RETRY_MS = 60 * 60 * 1000 // si falla (vault apagado), no insistir >1 vez/hora
1340
1342
  let renewLastTry = 0
1341
1343
  /**
@@ -3007,8 +3009,19 @@ export async function createIdentityCore ({ kv: hostKv, peers, makeSync = null,
3007
3009
  const v = loadVaultCert(); const device = loadVaultDevice()
3008
3010
  if (!v?.cert || !device) throw new Error('this device is not paired with a vault')
3009
3011
  maybeRenewVaultCert()
3012
+ const ask = (cert) => remoteApproval({ master: v.master, proxy: v.proxy, device, cert, op, id, onRevoked: wipeVaultLink })
3010
3013
  try {
3011
- const r = await remoteApproval({ master: v.master, proxy: v.proxy, device, cert: v.cert, op, id, onRevoked: wipeVaultLink })
3014
+ let r
3015
+ try { r = await ask(v.cert) } catch (e) {
3016
+ // PAPEL VIEJO: tras `caps <ID> +aprueba` el papel que tengo no trae `vault:approve`,
3017
+ // y si el aviso del acta nueva no llegó (o llegó y el freno de una hora ya había
3018
+ // gastado su intento) no se renovaba nunca: el aprobador se quedaba en
3019
+ // «unauthorized: scope» para siempre. Se renueva UNA vez y se reintenta; la bóveda
3020
+ // decide qué papel toca según SU acta. Un aparato revocado o sin el permiso no se
3021
+ // reintenta: renovar no lo arregla y esconder el motivo sería peor.
3022
+ if (e?.code !== 'unauthorized' || !PAPER_REASONS.has(e.reason)) throw e
3023
+ r = await ask(await renovarCert())
3024
+ }
3012
3025
  // El comando de cada pedido viene sellado a este aparato: se abre aquí, que es
3013
3026
  // donde está la llave, y sale ya legible.
3014
3027
  if (Array.isArray(r?.items)) return { ...r, items: await abrirContextos(r.items, currentPid) }
package/vault/remote.js CHANGED
@@ -128,7 +128,7 @@ async function askVault (client, qr) {
128
128
  * @param {(c:{deviceId:string, code:string})=>void} [opts.onChallenge] Para mostrar el código a tipear en el PC.
129
129
  * @param {string} [opts.label]
130
130
  * @param {number} [opts.approveTimeoutMs] Espera de la aprobación humana (def 3 min).
131
- * @returns {Promise<{device, cert, master:string, proxy:string, deviceId:string}>}
131
+ * @returns {Promise<{device, cert, master:string, proxy:string, deviceId:string, account:string}>}
132
132
  */
133
133
  export async function enrollDevice ({ qr, device, onChallenge, label = '', continuity = null, encPub = null, approveTimeoutMs = 180000, intent = 'join', profileId = null, onAdopt = null } = {}) {
134
134
  if (!qr?.sn || !(qr.iss || qr.conn)) throw new Error('invalid qr: missing vault or nonce')
@@ -206,7 +206,7 @@ export async function enrollDevice ({ qr, device, onChallenge, label = '', conti
206
206
  if (adopting) {
207
207
  if (!res.acta) throw new Error('the vault did not return the adopted record')
208
208
  if (res.acta.sealer !== qr.iss) throw new Error('the record is sealed by a vault other than the one you saw')
209
- return { device: dev, cert: null, master: qr.iss, proxy: qr.proxy, deviceId, acta: res.acta, adopted: true }
209
+ return { device: dev, cert: null, master: qr.iss, proxy: qr.proxy, deviceId, acta: res.acta, adopted: true, account: qr.acct || '' }
210
210
  }
211
211
 
212
212
  // Validación estricta antes de guardar (cierra inyección de cert / sustitución de maestra).
@@ -225,7 +225,10 @@ export async function enrollDevice ({ qr, device, onChallenge, label = '', conti
225
225
  // emparejaba con una segunda bóveda o con una que adoptó la cuenta (`checkVaultReply`).
226
226
  const chk = await checkVaultReply({ acta: res.acta, cert: res.cert, vault: qr.iss, sub: dev.publickey, justSealed: true })
227
227
  if (!chk.ok) throw new Error('the vault reply does not check out: ' + chk.reason)
228
- return { device: dev, cert: res.cert, master: qr.iss, proxy: qr.proxy, deviceId, acta: res.acta || null }
228
+ // `account`: el nombre que la bóveda dio a la cuenta. Con la invitación corta no viene en
229
+ // el QR sino en el `hello`, así que solo lo sabe esto — y sin él, quien tiene varias
230
+ // cuentas en un aparato no sabe de cuál es cada cosa.
231
+ return { device: dev, cert: res.cert, master: qr.iss, proxy: qr.proxy, deviceId, acta: res.acta || null, account: qr.acct || '' }
229
232
  } finally { try { client.close() } catch (_) {} }
230
233
  }
231
234
 
@@ -267,6 +270,7 @@ const REVOKE_GRACE_MS = 1500
267
270
  function vaultError (p) {
268
271
  const e = new Error(p?.error || 'vault error')
269
272
  if (p?.code) e.code = p.code
273
+ if (p?.reason) e.reason = p.reason
270
274
  if (p?.tries != null) e.tries = p.tries
271
275
  if (p?.waitSec != null) e.waitSec = p.waitSec
272
276
  return e
@@ -1,4 +1,4 @@
1
- Copia vendorizada de @dotrino/vault@0.72.0 (dotrino-vault/lib/src/{index,enroll,protocol,passwordLogins,loginClient,b64}.js).
1
+ Copia vendorizada de @dotrino/vault@0.73.0 (dotrino-vault/lib/src/{index,enroll,protocol,passwordLogins,loginClient,b64}.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