@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
|
@@ -3222,6 +3222,30 @@ var AbracadabraClient = class {
|
|
|
3222
3222
|
auth: false
|
|
3223
3223
|
});
|
|
3224
3224
|
}
|
|
3225
|
+
/** Request a device session token after successful crypto auth. Requires valid JWT. */
|
|
3226
|
+
async requestDeviceSession(opts) {
|
|
3227
|
+
return this.request("POST", "/auth/device-session", { body: {
|
|
3228
|
+
publicKey: opts.publicKey,
|
|
3229
|
+
deviceName: opts.deviceName
|
|
3230
|
+
} });
|
|
3231
|
+
}
|
|
3232
|
+
/** Exchange a device session token for a fresh JWT. No biometric/passkey needed. */
|
|
3233
|
+
async refreshWithDeviceSession(sessionToken) {
|
|
3234
|
+
const res = await this.request("POST", "/auth/refresh", {
|
|
3235
|
+
body: { sessionToken },
|
|
3236
|
+
auth: false
|
|
3237
|
+
});
|
|
3238
|
+
this.token = res.token;
|
|
3239
|
+
return res.token;
|
|
3240
|
+
}
|
|
3241
|
+
/** List active device sessions for the authenticated user. */
|
|
3242
|
+
async listDeviceSessions() {
|
|
3243
|
+
return (await this.request("GET", "/auth/device-session")).sessions;
|
|
3244
|
+
}
|
|
3245
|
+
/** Revoke a device session by ID. */
|
|
3246
|
+
async revokeDeviceSession(sessionId) {
|
|
3247
|
+
await this.request("DELETE", `/auth/device-session/${encodeURIComponent(sessionId)}`);
|
|
3248
|
+
}
|
|
3225
3249
|
/**
|
|
3226
3250
|
* Fetch a short-lived anonymous pairing token for WebRTC signaling.
|
|
3227
3251
|
* No authentication required. The token only grants access to `__pairing_*` rooms.
|
|
@@ -7310,6 +7334,30 @@ var CryptoIdentityKeystore = class {
|
|
|
7310
7334
|
db.close();
|
|
7311
7335
|
}
|
|
7312
7336
|
}
|
|
7337
|
+
/**
|
|
7338
|
+
* Updates the cached username for a given credential (or the first cached identity).
|
|
7339
|
+
* Call this after the user sets/changes their display name so it persists across devices.
|
|
7340
|
+
*/
|
|
7341
|
+
async setUsername(username, credentialIdHint) {
|
|
7342
|
+
const db = await openDb$4();
|
|
7343
|
+
try {
|
|
7344
|
+
if (credentialIdHint) {
|
|
7345
|
+
const stored = await dbGet(db, credentialIdHint);
|
|
7346
|
+
if (stored) await dbPut(db, credentialIdHint, {
|
|
7347
|
+
...stored,
|
|
7348
|
+
username
|
|
7349
|
+
});
|
|
7350
|
+
} else {
|
|
7351
|
+
const all = await dbGetAll(db);
|
|
7352
|
+
if (all.length > 0) await dbPut(db, all[0].key, {
|
|
7353
|
+
...all[0].value,
|
|
7354
|
+
username
|
|
7355
|
+
});
|
|
7356
|
+
}
|
|
7357
|
+
} finally {
|
|
7358
|
+
db.close();
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7313
7361
|
/** Returns true if an identity is cached in IndexedDB. */
|
|
7314
7362
|
async hasIdentity() {
|
|
7315
7363
|
const db = await openDb$4();
|
|
@@ -9932,6 +9980,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
9932
9980
|
this.yjsChannels = /* @__PURE__ */ new Map();
|
|
9933
9981
|
this.fileChannels = /* @__PURE__ */ new Map();
|
|
9934
9982
|
this.e2eeChannels = /* @__PURE__ */ new Map();
|
|
9983
|
+
this._resolvedE2ee = null;
|
|
9984
|
+
this._resolveE2eePromise = null;
|
|
9935
9985
|
this.peers = /* @__PURE__ */ new Map();
|
|
9936
9986
|
this.localPeerId = null;
|
|
9937
9987
|
this.isConnected = false;
|
|
@@ -10072,6 +10122,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
10072
10122
|
this.signaling.destroy();
|
|
10073
10123
|
this.signaling = null;
|
|
10074
10124
|
}
|
|
10125
|
+
this._resolvedE2ee = null;
|
|
10126
|
+
this._resolveE2eePromise = null;
|
|
10075
10127
|
this.removeAllListeners();
|
|
10076
10128
|
}
|
|
10077
10129
|
setMuted(muted) {
|
|
@@ -10198,25 +10250,69 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
10198
10250
|
this.attachDataHandlers(peerId, pc);
|
|
10199
10251
|
return pc;
|
|
10200
10252
|
}
|
|
10253
|
+
/** Resolve the E2EE identity, supporting both pre-resolved objects and lazy factories. */
|
|
10254
|
+
async resolveE2ee() {
|
|
10255
|
+
if (this._resolvedE2ee) return this._resolvedE2ee;
|
|
10256
|
+
if (!this.config.e2ee) return null;
|
|
10257
|
+
if (typeof this.config.e2ee === "function") {
|
|
10258
|
+
if (!this._resolveE2eePromise) this._resolveE2eePromise = this.config.e2ee().then((id) => {
|
|
10259
|
+
this._resolvedE2ee = id;
|
|
10260
|
+
return id;
|
|
10261
|
+
});
|
|
10262
|
+
return this._resolveE2eePromise;
|
|
10263
|
+
}
|
|
10264
|
+
this._resolvedE2ee = this.config.e2ee;
|
|
10265
|
+
return this._resolvedE2ee;
|
|
10266
|
+
}
|
|
10201
10267
|
attachDataHandlers(peerId, pc) {
|
|
10202
|
-
if (this.config.e2ee) {
|
|
10203
|
-
|
|
10268
|
+
if (!this.config.e2ee) {
|
|
10269
|
+
this.startDataSync(peerId, pc);
|
|
10270
|
+
return;
|
|
10271
|
+
}
|
|
10272
|
+
let e2ee = null;
|
|
10273
|
+
const pendingMessages = [];
|
|
10274
|
+
let pendingKeyExchangeChannel = null;
|
|
10275
|
+
pc.router.on("channelMessage", async ({ name, data }) => {
|
|
10276
|
+
if (name !== KEY_EXCHANGE_CHANNEL) return;
|
|
10277
|
+
const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
10278
|
+
if (!e2ee) {
|
|
10279
|
+
pendingMessages.push(buf);
|
|
10280
|
+
return;
|
|
10281
|
+
}
|
|
10282
|
+
try {
|
|
10283
|
+
await e2ee.handleKeyExchange(buf);
|
|
10284
|
+
} catch (err) {
|
|
10285
|
+
this.emit("e2eeFailed", {
|
|
10286
|
+
peerId,
|
|
10287
|
+
error: err
|
|
10288
|
+
});
|
|
10289
|
+
}
|
|
10290
|
+
});
|
|
10291
|
+
pc.router.on("channelOpen", ({ name, channel }) => {
|
|
10292
|
+
if (name !== KEY_EXCHANGE_CHANNEL) return;
|
|
10293
|
+
if (!e2ee) {
|
|
10294
|
+
pendingKeyExchangeChannel = channel;
|
|
10295
|
+
return;
|
|
10296
|
+
}
|
|
10297
|
+
channel.send(e2ee.getKeyExchangeMessage());
|
|
10298
|
+
});
|
|
10299
|
+
this.resolveE2ee().then(async (identity) => {
|
|
10300
|
+
if (!identity) {
|
|
10301
|
+
this.startDataSync(peerId, pc);
|
|
10302
|
+
return;
|
|
10303
|
+
}
|
|
10304
|
+
e2ee = new E2EEChannel(identity, this.config.docId);
|
|
10204
10305
|
this.e2eeChannels.set(peerId, e2ee);
|
|
10205
10306
|
pc.router.setEncryptor(e2ee);
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
}
|
|
10216
|
-
});
|
|
10217
|
-
pc.router.on("channelOpen", ({ name, channel }) => {
|
|
10218
|
-
if (name === KEY_EXCHANGE_CHANNEL) channel.send(e2ee.getKeyExchangeMessage());
|
|
10219
|
-
});
|
|
10307
|
+
if (pendingKeyExchangeChannel) pendingKeyExchangeChannel.send(e2ee.getKeyExchangeMessage());
|
|
10308
|
+
for (const msg of pendingMessages) try {
|
|
10309
|
+
await e2ee.handleKeyExchange(msg);
|
|
10310
|
+
} catch (err) {
|
|
10311
|
+
this.emit("e2eeFailed", {
|
|
10312
|
+
peerId,
|
|
10313
|
+
error: err
|
|
10314
|
+
});
|
|
10315
|
+
}
|
|
10220
10316
|
e2ee.on("established", () => {
|
|
10221
10317
|
this.emit("e2eeEstablished", { peerId });
|
|
10222
10318
|
this.startDataSync(peerId, pc);
|
|
@@ -10227,7 +10323,13 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
|
|
|
10227
10323
|
error: err
|
|
10228
10324
|
});
|
|
10229
10325
|
});
|
|
10230
|
-
}
|
|
10326
|
+
}).catch((err) => {
|
|
10327
|
+
this.emit("e2eeFailed", {
|
|
10328
|
+
peerId,
|
|
10329
|
+
error: err
|
|
10330
|
+
});
|
|
10331
|
+
this.startDataSync(peerId, pc);
|
|
10332
|
+
});
|
|
10231
10333
|
}
|
|
10232
10334
|
startDataSync(peerId, pc) {
|
|
10233
10335
|
if (this.config.document && this.config.enableDocSync) {
|