@dotrino/identity 0.113.0 → 0.114.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/README.md CHANGED
@@ -179,6 +179,11 @@ const v = await verifySignedFor({ data: pin, signature, publickey, chain, audien
179
179
  `aud` es obligatorio igual que en la prueba; `exp` es opcional, porque la caducidad de lo
180
180
  publicado la lleva el servicio (el TTL del pin) y no el cuerpo.
181
181
 
182
+ Si quien firma es un **servicio** de la cuenta (un miembro con `cn`), pásale `ns` con su
183
+ cajón (0.114.0+): un servicio solo firma dentro del suyo, y sin `ns` no pasa nunca. Así el
184
+ directorio de nombres acepta lo que firma su aparato `names` sin aceptar lo de cualquier
185
+ otro servicio de la misma cuenta.
186
+
182
187
  ### Permiso por origen (0.86.0+)
183
188
 
184
189
  Una prueba dice **quién eres** sin preguntar nada: quien llega al iframe ya pasó el filtro
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/identity",
3
- "version": "0.113.0",
3
+ "version": "0.114.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",
package/vault/acta.js CHANGED
@@ -622,7 +622,7 @@ export async function sealActa ({ acta, privateKey, privateJwk }) {
622
622
  * @param {{ data?: any, signature?: string, publickey?: string, chain?: any[], expectedProfileId?: string|null }} [args]
623
623
  */
624
624
  export async function verifySignedBy (args = {}) {
625
- const { data, signature, publickey, chain, expectedProfileId = null } = args
625
+ const { data, signature, publickey, chain, expectedProfileId = null, ns = null } = args
626
626
  if (!data || typeof signature !== 'string' || typeof publickey !== 'string') {
627
627
  return { ok: false, reason: 'shape' }
628
628
  }
@@ -631,7 +631,10 @@ export async function verifySignedBy (args = {}) {
631
631
  const cabeza = chain[chain.length - 1]
632
632
  // Quien firmó tiene que ser miembro CON `sign` en el acta vigente de esa cadena. Si le
633
633
  // quitaste el permiso, sus firmas dejan de contar en cuanto se ve un acta más nueva.
634
- if (!memberCanSign(cabeza, publickey)) return { ok: false, reason: 'firmante-no-autorizado' }
634
+ // Un SERVICIO (miembro con `cn`) solo firma dentro de su cajón: quien verifica dice cuál
635
+ // con `ns` (el directorio de nombres acepta lo firmado por su aparato `names`, no por
636
+ // cualquier servicio de la cuenta). Sin `ns`, un servicio nunca pasa, como siempre.
637
+ if (!memberCanSign(cabeza, publickey, ns)) return { ok: false, reason: 'firmante-no-autorizado' }
635
638
  if (!(await verifyDeviceSig({ publickey, data, signature }))) return { ok: false, reason: 'firma-invalida' }
636
639
  return { ok: true, profileId: c.profileId, seq: c.seq, signer: publickey }
637
640
  }
@@ -191,10 +191,13 @@ export async function verifyAssertion (assertion, opts = {}) {
191
191
  * quien la lleva es el servicio; si el cuerpo trae `exp`, se respeta.
192
192
  */
193
193
  /**
194
- * @param {{ data?: any, signature?: string, publickey?: string, chain?: any[], audience?: string, expectedProfileId?: string|null, now?: number, maxSkewMs?: number }} [args]
194
+ * `ns`: el cajón del servicio que firma (su `cn` en el acta). Solo hace falta cuando lo
195
+ * firmado viene de un SERVICIO de la cuenta y no de un aparato de la persona.
196
+ *
197
+ * @param {{ data?: any, signature?: string, publickey?: string, chain?: any[], audience?: string, expectedProfileId?: string|null, ns?: string|null, now?: number, maxSkewMs?: number }} [args]
195
198
  */
196
199
  export async function verifySignedFor (args = {}) {
197
- const { data, signature, publickey, chain, audience, expectedProfileId = null, now = Date.now(), maxSkewMs = ASSERTION_MAX_SKEW_MS } = args
200
+ const { data, signature, publickey, chain, audience, expectedProfileId = null, ns = null, now = Date.now(), maxSkewMs = ASSERTION_MAX_SKEW_MS } = args
198
201
  if (typeof audience !== 'string' || !audience.trim()) return { ok: false, reason: 'no-audience' }
199
202
  if (!data || typeof data !== 'object') return { ok: false, reason: 'shape' }
200
203
  if (typeof data.aud !== 'string' || !data.aud) return { ok: false, reason: 'sin-destinatario' }
@@ -207,7 +210,7 @@ export async function verifySignedFor (args = {}) {
207
210
  if (!Number.isFinite(data.iat)) return { ok: false, reason: 'shape' }
208
211
  if (data.iat > now + maxSkewMs) return { ok: false, reason: 'del-futuro' }
209
212
  }
210
- const v = await verifySignedBy({ data, signature, publickey, chain, expectedProfileId })
213
+ const v = await verifySignedBy({ data, signature, publickey, chain, expectedProfileId, ns })
211
214
  if (!v.ok) return { ok: false, reason: 'firma:' + v.reason }
212
215
  return { ok: true, profileId: v.profileId, signer: v.signer, seq: v.seq, aud: data.aud }
213
216
  }