@dotrino/identity 0.59.0 → 0.60.1
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.
|
|
3
|
+
"version": "0.60.1",
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"url": "git+https://github.com/imdotrino/dotrino-identity.git"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@dotrino/proxy-client": "0.
|
|
59
|
+
"@dotrino/proxy-client": "^0.11.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"fake-indexeddb": "^6.2.5",
|
package/src/index.js
CHANGED
|
@@ -412,6 +412,11 @@ export class Identity {
|
|
|
412
412
|
return this._call('vaultApprovals', { op, ...(args || {}) }, 20000)
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
+
/** Registra el token de push de la app nativa (FCM/APNs) bajo la llave de este aparato. */
|
|
416
|
+
async registerPush (args) {
|
|
417
|
+
return this._call('registerPush', args || {}, 20000)
|
|
418
|
+
}
|
|
419
|
+
|
|
415
420
|
/** ¿El cert de este dispositivo le permite aprobar pedidos de la bóveda? */
|
|
416
421
|
async canApproveVault () {
|
|
417
422
|
return this._call('canApproveVault', {}, 20000)
|
package/vault/core.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { signDelegationWith, MAX_DELEGATION_MS, DEFAULT_DELEGATION_MS } from './capabilities.js'
|
|
22
22
|
import * as Acta from './acta.js'
|
|
23
23
|
import * as Content from './content.js'
|
|
24
|
-
import { pubkeyId as pubkeyIdOf } from './capabilities.js'
|
|
24
|
+
import { pubkeyId as pubkeyIdOf, signWithDevice } from './capabilities.js'
|
|
25
25
|
import { enrollDevice as remoteEnroll, requestSign as remoteSign, requestStore as remoteStore, requestDevices as remoteDevices, requestRenew as remoteRenew, requestAdmin as remoteAdmin, requestApproval as remoteApproval, requestRenounce as remoteRenounce, checkMembership as remoteCheck } from './remote.js'
|
|
26
26
|
|
|
27
27
|
export const KEY_STORAGE = 'dotrino.identity.keypair'
|
|
@@ -1969,6 +1969,28 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
|
|
|
1969
1969
|
catch (e) { return handleVaultError(e) }
|
|
1970
1970
|
},
|
|
1971
1971
|
|
|
1972
|
+
/**
|
|
1973
|
+
* PUSH DE LA APP NATIVA: registra el token (FCM/APNs) de este aparato en el proxio,
|
|
1974
|
+
* bajo la llave del dispositivo, para que la bóveda pueda «timbrarlo» cuando haya
|
|
1975
|
+
* un pedido. La app nativa le pasa el token a la página; la página lo trae aquí.
|
|
1976
|
+
*/
|
|
1977
|
+
async registerPush ({ kind = 'fcm', token } = {}) {
|
|
1978
|
+
const device = loadVaultDevice()
|
|
1979
|
+
if (!device) throw new Error('this device is not paired with a vault')
|
|
1980
|
+
if (typeof token !== 'string' || !token) throw new Error('registerPush requires a token')
|
|
1981
|
+
const v = loadVaultCert()
|
|
1982
|
+
const { WebSocketProxyClient } = await import('@dotrino/proxy-client')
|
|
1983
|
+
const client = new WebSocketProxyClient({ url: v?.proxy || 'wss://proxy.dotrino.com', enableWebRTC: false, autoReconnect: false })
|
|
1984
|
+
await client.connect()
|
|
1985
|
+
try {
|
|
1986
|
+
await client.registerPushToken({
|
|
1987
|
+
publicKey: device.publickey, token, kind,
|
|
1988
|
+
sign: async (data) => (await signWithDevice({ privateJwk: device.privateJwk, privateKey: device.privateKey, publickey: device.publickey, data })).signature
|
|
1989
|
+
})
|
|
1990
|
+
return { ok: true, kind }
|
|
1991
|
+
} finally { try { client.close() } catch (_) {} }
|
|
1992
|
+
},
|
|
1993
|
+
|
|
1972
1994
|
/** ¿Puede ESTE dispositivo aprobar pedidos? Mismo criterio que `canAdminVault`. */
|
|
1973
1995
|
async canApproveVault () {
|
|
1974
1996
|
if (certDesfasadoDelActa()) { try { await renovarCert() } catch (_) {} }
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Ver dotrino-identity: el iframe se sirve estatico, transporte self-hosted (no CDN).
|
|
1
|
+
0.11.0
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { buildSignedChannel, getPublicKeyJwk, signData } from './signature.js'
|
|
2
2
|
import { WebRTCManager, RTC_TAG, DEFAULT_ICE_SERVERS } from './webrtc.js'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Error con un `code` estable.
|
|
6
|
+
*
|
|
7
|
+
* El contrato es el CODE, no la frase: comprobar por el texto cruza procesos y
|
|
8
|
+
* se rompe en silencio en cuanto alguien lo reescribe o lo traduce.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} mensaje
|
|
11
|
+
* @param {string} code
|
|
12
|
+
* @returns {Error & { code: string }}
|
|
13
|
+
*/
|
|
14
|
+
function errorCon (mensaje, code) {
|
|
15
|
+
const e = /** @type {Error & { code: string }} */ (new Error(mensaje))
|
|
16
|
+
e.code = code
|
|
17
|
+
return e
|
|
18
|
+
}
|
|
19
|
+
|
|
4
20
|
/**
|
|
5
21
|
* Dotrino WebSocket proxy client.
|
|
6
22
|
* Minimal API: connection + token + messages + channels (publish/list/count/disconnect)
|
|
@@ -84,6 +100,22 @@ export class WebSocketProxyClient {
|
|
|
84
100
|
if (this.ws) {
|
|
85
101
|
try { this.ws.close(1000) } catch (_) {}
|
|
86
102
|
}
|
|
103
|
+
// Cerrar es una decisión de la app: lo que estuviera en vuelo ya no va a
|
|
104
|
+
// llegar (`close()` apaga autoReconnect, y una reconexión tampoco reenvía
|
|
105
|
+
// las peticiones pendientes). Sin esto, quien esperaba un `publish` se comía
|
|
106
|
+
// los 10 s completos del timeout para enterarse de algo que ya se sabía, y
|
|
107
|
+
// el temporizador mantenía vivo el proceso en Node ese rato.
|
|
108
|
+
this._rejectAllPending(errorCon('Connection closed', 'CONNECTION_CLOSED'))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Corta en seco todo lo que esperaba respuesta, con su motivo. */
|
|
112
|
+
_rejectAllPending (error) {
|
|
113
|
+
const pendientes = [...this._pending.values()]
|
|
114
|
+
this._pending.clear()
|
|
115
|
+
for (const entry of pendientes) {
|
|
116
|
+
clearTimeout(entry.timer)
|
|
117
|
+
entry.reject(error)
|
|
118
|
+
}
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
/** Alias for close() to ease migration from older clients. */
|
|
@@ -429,6 +461,20 @@ export class WebSocketProxyClient {
|
|
|
429
461
|
return sub
|
|
430
462
|
}
|
|
431
463
|
|
|
464
|
+
/**
|
|
465
|
+
* Registrar el TOKEN DE PUSH de una app nativa (FCM en Android; APNs más adelante) bajo
|
|
466
|
+
* esta pubkey, en vez de una PushSubscription del navegador. El proxy lo usa para el
|
|
467
|
+
* mismo timbre sin contenido. Firma del vault, igual que enablePush.
|
|
468
|
+
* @param {{ publicKey:string, sign:Function, token:string, kind?:'fcm' }} opts
|
|
469
|
+
*/
|
|
470
|
+
async registerPushToken ({ publicKey, sign, token, kind = 'fcm' } = {}) {
|
|
471
|
+
if (!publicKey || typeof sign !== 'function' || !token) throw new Error('registerPushToken requires { publicKey, sign, token }')
|
|
472
|
+
const data = { op: 'push-subscribe', publickey: publicKey, subscription: JSON.stringify({ kind, token }), ts: Date.now() }
|
|
473
|
+
const signature = await normalizeSignature(sign, data)
|
|
474
|
+
await this._request({ type: 'push-subscribe', data, signature }, 'push-subscribed')
|
|
475
|
+
return { kind, token }
|
|
476
|
+
}
|
|
477
|
+
|
|
432
478
|
/**
|
|
433
479
|
* Desactivar Web Push: cancela la PushSubscription local y la borra del proxy.
|
|
434
480
|
* @param {Object} opts
|
|
@@ -738,7 +784,7 @@ export class WebSocketProxyClient {
|
|
|
738
784
|
|
|
739
785
|
_sendRaw (frame) {
|
|
740
786
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
741
|
-
throw
|
|
787
|
+
throw errorCon('WebSocket not connected', 'NOT_CONNECTED')
|
|
742
788
|
}
|
|
743
789
|
this.ws.send(JSON.stringify(frame))
|
|
744
790
|
}
|
|
@@ -749,7 +795,7 @@ export class WebSocketProxyClient {
|
|
|
749
795
|
const out = { ...frame, id }
|
|
750
796
|
const timer = setTimeout(() => {
|
|
751
797
|
this._pending.delete(id)
|
|
752
|
-
reject(
|
|
798
|
+
reject(errorCon(`Timeout waiting for ${expectedType}`, 'REQUEST_TIMEOUT'))
|
|
753
799
|
}, 10000)
|
|
754
800
|
this._pending.set(id, { resolve, reject, timer, expectedType, channelKey })
|
|
755
801
|
try {
|