@abraca/dabra 1.2.0 → 1.3.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/dist/abracadabra-provider.cjs +119 -17
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +119 -17
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +35 -3
- package/package.json +1 -1
- package/src/AbracadabraClient.ts +37 -0
- package/src/CryptoIdentityKeystore.ts +23 -0
- package/src/IdentityDoc.ts +2 -2
- package/src/webrtc/AbracadabraWebRTC.ts +79 -25
- package/src/webrtc/types.ts +1 -1
|
@@ -3252,6 +3252,30 @@ var AbracadabraClient = class {
|
|
|
3252
3252
|
auth: false
|
|
3253
3253
|
});
|
|
3254
3254
|
}
|
|
3255
|
+
/** Request a device session token after successful crypto auth. Requires valid JWT. */
|
|
3256
|
+
async requestDeviceSession(opts) {
|
|
3257
|
+
return this.request("POST", "/auth/device-session", { body: {
|
|
3258
|
+
publicKey: opts.publicKey,
|
|
3259
|
+
deviceName: opts.deviceName
|
|
3260
|
+
} });
|
|
3261
|
+
}
|
|
3262
|
+
/** Exchange a device session token for a fresh JWT. No biometric/passkey needed. */
|
|
3263
|
+
async refreshWithDeviceSession(sessionToken) {
|
|
3264
|
+
const res = await this.request("POST", "/auth/refresh", {
|
|
3265
|
+
body: { sessionToken },
|
|
3266
|
+
auth: false
|
|
3267
|
+
});
|
|
3268
|
+
this.token = res.token;
|
|
3269
|
+
return res.token;
|
|
3270
|
+
}
|
|
3271
|
+
/** List active device sessions for the authenticated user. */
|
|
3272
|
+
async listDeviceSessions() {
|
|
3273
|
+
return (await this.request("GET", "/auth/device-session")).sessions;
|
|
3274
|
+
}
|
|
3275
|
+
/** Revoke a device session by ID. */
|
|
3276
|
+
async revokeDeviceSession(sessionId) {
|
|
3277
|
+
await this.request("DELETE", `/auth/device-session/${encodeURIComponent(sessionId)}`);
|
|
3278
|
+
}
|
|
3255
3279
|
/**
|
|
3256
3280
|
* Fetch a short-lived anonymous pairing token for WebRTC signaling.
|
|
3257
3281
|
* No authentication required. The token only grants access to `__pairing_*` rooms.
|
|
@@ -7340,6 +7364,30 @@ var CryptoIdentityKeystore = class {
|
|
|
7340
7364
|
db.close();
|
|
7341
7365
|
}
|
|
7342
7366
|
}
|
|
7367
|
+
/**
|
|
7368
|
+
* Updates the cached username for a given credential (or the first cached identity).
|
|
7369
|
+
* Call this after the user sets/changes their display name so it persists across devices.
|
|
7370
|
+
*/
|
|
7371
|
+
async setUsername(username, credentialIdHint) {
|
|
7372
|
+
const db = await openDb$4();
|
|
7373
|
+
try {
|
|
7374
|
+
if (credentialIdHint) {
|
|
7375
|
+
const stored = await dbGet(db, credentialIdHint);
|
|
7376
|
+
if (stored) await dbPut(db, credentialIdHint, {
|
|
7377
|
+
...stored,
|
|
7378
|
+
username
|
|
7379
|
+
});
|
|
7380
|
+
} else {
|
|
7381
|
+
const all = await dbGetAll(db);
|
|
7382
|
+
if (all.length > 0) await dbPut(db, all[0].key, {
|
|
7383
|
+
...all[0].value,
|
|
7384
|
+
username
|
|
7385
|
+
});
|
|
7386
|
+
}
|
|
7387
|
+
} finally {
|
|
7388
|
+
db.close();
|
|
7389
|
+
}
|
|
7390
|
+
}
|
|
7343
7391
|
/** Returns true if an identity is cached in IndexedDB. */
|
|
7344
7392
|
async hasIdentity() {
|
|
7345
7393
|
const db = await openDb$4();
|
|
@@ -9984,6 +10032,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
9984
10032
|
this.yjsChannels = /* @__PURE__ */ new Map();
|
|
9985
10033
|
this.fileChannels = /* @__PURE__ */ new Map();
|
|
9986
10034
|
this.e2eeChannels = /* @__PURE__ */ new Map();
|
|
10035
|
+
this._resolvedE2ee = null;
|
|
10036
|
+
this._resolveE2eePromise = null;
|
|
9987
10037
|
this.peers = /* @__PURE__ */ new Map();
|
|
9988
10038
|
this.localPeerId = null;
|
|
9989
10039
|
this.isConnected = false;
|
|
@@ -10124,6 +10174,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
10124
10174
|
this.signaling.destroy();
|
|
10125
10175
|
this.signaling = null;
|
|
10126
10176
|
}
|
|
10177
|
+
this._resolvedE2ee = null;
|
|
10178
|
+
this._resolveE2eePromise = null;
|
|
10127
10179
|
this.removeAllListeners();
|
|
10128
10180
|
}
|
|
10129
10181
|
setMuted(muted) {
|
|
@@ -10250,25 +10302,69 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
10250
10302
|
this.attachDataHandlers(peerId, pc);
|
|
10251
10303
|
return pc;
|
|
10252
10304
|
}
|
|
10305
|
+
/** Resolve the E2EE identity, supporting both pre-resolved objects and lazy factories. */
|
|
10306
|
+
async resolveE2ee() {
|
|
10307
|
+
if (this._resolvedE2ee) return this._resolvedE2ee;
|
|
10308
|
+
if (!this.config.e2ee) return null;
|
|
10309
|
+
if (typeof this.config.e2ee === "function") {
|
|
10310
|
+
if (!this._resolveE2eePromise) this._resolveE2eePromise = this.config.e2ee().then((id) => {
|
|
10311
|
+
this._resolvedE2ee = id;
|
|
10312
|
+
return id;
|
|
10313
|
+
});
|
|
10314
|
+
return this._resolveE2eePromise;
|
|
10315
|
+
}
|
|
10316
|
+
this._resolvedE2ee = this.config.e2ee;
|
|
10317
|
+
return this._resolvedE2ee;
|
|
10318
|
+
}
|
|
10253
10319
|
attachDataHandlers(peerId, pc) {
|
|
10254
|
-
if (this.config.e2ee) {
|
|
10255
|
-
|
|
10320
|
+
if (!this.config.e2ee) {
|
|
10321
|
+
this.startDataSync(peerId, pc);
|
|
10322
|
+
return;
|
|
10323
|
+
}
|
|
10324
|
+
let e2ee = null;
|
|
10325
|
+
const pendingMessages = [];
|
|
10326
|
+
let pendingKeyExchangeChannel = null;
|
|
10327
|
+
pc.router.on("channelMessage", async ({ name, data }) => {
|
|
10328
|
+
if (name !== KEY_EXCHANGE_CHANNEL) return;
|
|
10329
|
+
const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
10330
|
+
if (!e2ee) {
|
|
10331
|
+
pendingMessages.push(buf);
|
|
10332
|
+
return;
|
|
10333
|
+
}
|
|
10334
|
+
try {
|
|
10335
|
+
await e2ee.handleKeyExchange(buf);
|
|
10336
|
+
} catch (err) {
|
|
10337
|
+
this.emit("e2eeFailed", {
|
|
10338
|
+
peerId,
|
|
10339
|
+
error: err
|
|
10340
|
+
});
|
|
10341
|
+
}
|
|
10342
|
+
});
|
|
10343
|
+
pc.router.on("channelOpen", ({ name, channel }) => {
|
|
10344
|
+
if (name !== KEY_EXCHANGE_CHANNEL) return;
|
|
10345
|
+
if (!e2ee) {
|
|
10346
|
+
pendingKeyExchangeChannel = channel;
|
|
10347
|
+
return;
|
|
10348
|
+
}
|
|
10349
|
+
channel.send(e2ee.getKeyExchangeMessage());
|
|
10350
|
+
});
|
|
10351
|
+
this.resolveE2ee().then(async (identity) => {
|
|
10352
|
+
if (!identity) {
|
|
10353
|
+
this.startDataSync(peerId, pc);
|
|
10354
|
+
return;
|
|
10355
|
+
}
|
|
10356
|
+
e2ee = new E2EEChannel(identity, this.config.docId);
|
|
10256
10357
|
this.e2eeChannels.set(peerId, e2ee);
|
|
10257
10358
|
pc.router.setEncryptor(e2ee);
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
}
|
|
10268
|
-
});
|
|
10269
|
-
pc.router.on("channelOpen", ({ name, channel }) => {
|
|
10270
|
-
if (name === KEY_EXCHANGE_CHANNEL) channel.send(e2ee.getKeyExchangeMessage());
|
|
10271
|
-
});
|
|
10359
|
+
if (pendingKeyExchangeChannel) pendingKeyExchangeChannel.send(e2ee.getKeyExchangeMessage());
|
|
10360
|
+
for (const msg of pendingMessages) try {
|
|
10361
|
+
await e2ee.handleKeyExchange(msg);
|
|
10362
|
+
} catch (err) {
|
|
10363
|
+
this.emit("e2eeFailed", {
|
|
10364
|
+
peerId,
|
|
10365
|
+
error: err
|
|
10366
|
+
});
|
|
10367
|
+
}
|
|
10272
10368
|
e2ee.on("established", () => {
|
|
10273
10369
|
this.emit("e2eeEstablished", { peerId });
|
|
10274
10370
|
this.startDataSync(peerId, pc);
|
|
@@ -10279,7 +10375,13 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
10279
10375
|
error: err
|
|
10280
10376
|
});
|
|
10281
10377
|
});
|
|
10282
|
-
}
|
|
10378
|
+
}).catch((err) => {
|
|
10379
|
+
this.emit("e2eeFailed", {
|
|
10380
|
+
peerId,
|
|
10381
|
+
error: err
|
|
10382
|
+
});
|
|
10383
|
+
this.startDataSync(peerId, pc);
|
|
10384
|
+
});
|
|
10283
10385
|
}
|
|
10284
10386
|
startDataSync(peerId, pc) {
|
|
10285
10387
|
if (this.config.document && this.config.enableDocSync) {
|