@learncard/sss-key-manager 0.2.5 → 0.2.6
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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/sss-key-manager.cjs.development.js +563 -134
- package/dist/sss-key-manager.cjs.development.js.map +3 -3
- package/dist/sss-key-manager.cjs.production.min.js +32 -32
- package/dist/sss-key-manager.cjs.production.min.js.map +3 -3
- package/dist/sss-key-manager.esm.js +563 -134
- package/dist/sss-key-manager.esm.js.map +3 -3
- package/dist/sss-strategy.d.ts.map +1 -1
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +2 -0
- package/src/sss-authority.test.ts +294 -1
- package/src/sss-strategy.test.ts +637 -8
- package/src/sss-strategy.ts +786 -159
- package/src/types.ts +20 -1
|
@@ -24031,7 +24031,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
24031
24031
|
state: external_exports.enum(["none", "enabled", "locked", "stale"]),
|
|
24032
24032
|
enabled: external_exports.boolean(),
|
|
24033
24033
|
attemptsRemaining: external_exports.number().int().nonnegative(),
|
|
24034
|
-
salt: external_exports.string().optional()
|
|
24034
|
+
salt: external_exports.string().optional(),
|
|
24035
|
+
/**
|
|
24036
|
+
* Verified wrong PIN entries against the current PIN (successful releases and
|
|
24037
|
+
* unresolved reservations are not counted). Omitted when the server cannot tell,
|
|
24038
|
+
* e.g. legacy records that predate the verified counter.
|
|
24039
|
+
*/
|
|
24040
|
+
wrongAttempts: external_exports.number().int().nonnegative().optional()
|
|
24035
24041
|
});
|
|
24036
24042
|
var BITSTRING_STATUS_PURPOSES = ["revocation", "suspension"];
|
|
24037
24043
|
var DEFAULT_BITSTRING_STATUS_LIST_SIZE = 131072;
|
|
@@ -25098,6 +25104,7 @@ __export(index_exports, {
|
|
|
25098
25104
|
NitroAttestationError: () => NitroAttestationError,
|
|
25099
25105
|
PIN_MAX_LENGTH: () => PIN_MAX_LENGTH,
|
|
25100
25106
|
PIN_MIN_LENGTH: () => PIN_MIN_LENGTH,
|
|
25107
|
+
PinSignInStaleError: () => PinSignInStaleError,
|
|
25101
25108
|
SSSApiClient: () => SSSApiClient,
|
|
25102
25109
|
SSSKeyManager: () => SSSKeyManager,
|
|
25103
25110
|
SSS_THRESHOLD: () => SSS_THRESHOLD,
|
|
@@ -30154,6 +30161,15 @@ var EscrowPinLockedError = class extends Error {
|
|
|
30154
30161
|
this.name = "EscrowPinLockedError";
|
|
30155
30162
|
}
|
|
30156
30163
|
};
|
|
30164
|
+
var PinSignInStaleError = class extends Error {
|
|
30165
|
+
static {
|
|
30166
|
+
__name(this, "PinSignInStaleError");
|
|
30167
|
+
}
|
|
30168
|
+
constructor() {
|
|
30169
|
+
super("Your account changed while signing in. Try again.");
|
|
30170
|
+
this.name = "PinSignInStaleError";
|
|
30171
|
+
}
|
|
30172
|
+
};
|
|
30157
30173
|
|
|
30158
30174
|
// src/escrow-pin.ts
|
|
30159
30175
|
var PIN_MIN_LENGTH = 6;
|
|
@@ -31016,7 +31032,7 @@ function createSSSStrategy(config) {
|
|
|
31016
31032
|
assertCurrent();
|
|
31017
31033
|
return result;
|
|
31018
31034
|
}, "fetchAuthorityState");
|
|
31019
|
-
const enrollEscrow = /* @__PURE__ */ __name(async (token, providerType, privateKey, primaryDid, shares, shareVersion, signDidAuthVp, verifiedKey, pinMaterial, clearPin = false, assertCurrent = guardedStorage().assertCurrent, recoveryEmail = "", recoveryLanguage = "") => {
|
|
31035
|
+
const enrollEscrow = /* @__PURE__ */ __name(async (token, providerType, privateKey, primaryDid, shares, shareVersion, signDidAuthVp, verifiedKey, pinMaterial, clearPin = false, assertCurrent = guardedStorage().assertCurrent, recoveryEmail = "", recoveryLanguage = "", expectedPinCharges) => {
|
|
31020
31036
|
if (!config.escrow?.enabled) throw new Error("Escrow enrollment is disabled");
|
|
31021
31037
|
if (!signDidAuthVp) throw new Error("DID proof signing is required for escrow enrollment");
|
|
31022
31038
|
const identity = verifiedKey ?? await fetchVerifiedEnclaveKey();
|
|
@@ -31075,7 +31091,8 @@ function createSSSStrategy(config) {
|
|
|
31075
31091
|
enclaveKeyId: keyId,
|
|
31076
31092
|
...pinMaterial ? { pinSalt: pinMaterial.pinSalt } : {},
|
|
31077
31093
|
...(signedIntent ? signedIntent.intent.pinAction === "clear" : clearPin) ? { clearPin: true } : {},
|
|
31078
|
-
...signedIntent ? { signedIntent, sourceEnvelope: authority?.envelope ?? null } : {}
|
|
31094
|
+
...signedIntent ? { signedIntent, sourceEnvelope: authority?.envelope ?? null } : {},
|
|
31095
|
+
...expectedPinCharges ? { expectedPinCharges } : {}
|
|
31079
31096
|
})
|
|
31080
31097
|
});
|
|
31081
31098
|
if (signedIntent) {
|
|
@@ -31088,11 +31105,11 @@ function createSSSStrategy(config) {
|
|
|
31088
31105
|
verifiedEscrowEnvelope = committed.envelope ?? void 0;
|
|
31089
31106
|
}
|
|
31090
31107
|
}, "enrollEscrow");
|
|
31091
|
-
const
|
|
31092
|
-
if (!config.escrow?.enabled) return false;
|
|
31108
|
+
const attemptEnrollEscrow = /* @__PURE__ */ __name(async (...args) => {
|
|
31109
|
+
if (!config.escrow?.enabled) return { enrolled: false };
|
|
31093
31110
|
try {
|
|
31094
31111
|
await enrollEscrow(...args);
|
|
31095
|
-
return true;
|
|
31112
|
+
return { enrolled: true };
|
|
31096
31113
|
} catch (err2) {
|
|
31097
31114
|
try {
|
|
31098
31115
|
if (config.onEscrowError) config.onEscrowError(err2);
|
|
@@ -31100,9 +31117,10 @@ function createSSSStrategy(config) {
|
|
|
31100
31117
|
} catch {
|
|
31101
31118
|
console.warn("SSS: escrow error reporting failed");
|
|
31102
31119
|
}
|
|
31103
|
-
return false;
|
|
31120
|
+
return { enrolled: false, error: err2 };
|
|
31104
31121
|
}
|
|
31105
|
-
}, "
|
|
31122
|
+
}, "attemptEnrollEscrow");
|
|
31123
|
+
const tryEnrollEscrow = /* @__PURE__ */ __name(async (...args) => (await attemptEnrollEscrow(...args)).enrolled, "tryEnrollEscrow");
|
|
31106
31124
|
const withAutoClear = /* @__PURE__ */ __name((shareVersion, autoClearedFrom) => {
|
|
31107
31125
|
if (!autoClearedFrom) return { enrolled: true, changed: true, shareVersion };
|
|
31108
31126
|
try {
|
|
@@ -31163,7 +31181,22 @@ function createSSSStrategy(config) {
|
|
|
31163
31181
|
clearPinForEnrollment ||= current.hasPin && pin === void 0;
|
|
31164
31182
|
}
|
|
31165
31183
|
currentlyEnrolled = currentlyEnrolled && current.status === "active" && current.shareVersion === status.shareVersion && current.envelope?.keyId === verifiedKey.keyId;
|
|
31184
|
+
let identityMismatch = false;
|
|
31185
|
+
if (currentlyEnrolled && current.envelope && status.escrowBlobIdentity !== void 0) {
|
|
31186
|
+
const authorityIdentity = await getEscrowBlobIdentity(current.envelope);
|
|
31187
|
+
if (authorityIdentity !== status.escrowBlobIdentity) {
|
|
31188
|
+
const reread = await self.fetchServerKeyStatus(
|
|
31189
|
+
params.token,
|
|
31190
|
+
params.providerType
|
|
31191
|
+
);
|
|
31192
|
+
storage2.assertCurrent();
|
|
31193
|
+
identityMismatch = reread.escrowBlobIdentity !== authorityIdentity && reread.escrowUpdating !== true;
|
|
31194
|
+
}
|
|
31195
|
+
}
|
|
31196
|
+
if (identityMismatch) currentlyEnrolled = false;
|
|
31166
31197
|
if (currentlyEnrolled) verifiedEscrowEnvelope = current.envelope ?? void 0;
|
|
31198
|
+
if (identityMismatch && current.hasPin && pin === void 0 && !clearPinForEnrollment)
|
|
31199
|
+
return { enrolled: false, reason: "pin-reset-required" };
|
|
31167
31200
|
hasPin = current.hasPin;
|
|
31168
31201
|
const uncarryablePin = !clearPinForEnrollment && current.status === "active" && current.hasPin && pin === void 0 && !currentlyEnrolled && status.escrowPin?.state === "none";
|
|
31169
31202
|
if (uncarryablePin && (config.autoClearUncarryablePin ?? DEFAULT_AUTO_CLEAR_UNCARRYABLE_PIN) && current.shareVersion !== null && status.shareVersion !== null && current.shareVersion < status.shareVersion && !status.recoveryMethods.some(
|
|
@@ -31291,6 +31324,378 @@ function createSSSStrategy(config) {
|
|
|
31291
31324
|
}
|
|
31292
31325
|
};
|
|
31293
31326
|
}, "captureActiveUserGuard");
|
|
31327
|
+
const pinHasRecordedWrongAttempts = /* @__PURE__ */ __name((escrowPin) => {
|
|
31328
|
+
const status = escrowPin;
|
|
31329
|
+
if (status?.state === "none") return false;
|
|
31330
|
+
return typeof status?.wrongAttempts !== "number" || status.wrongAttempts > 0;
|
|
31331
|
+
}, "pinHasRecordedWrongAttempts");
|
|
31332
|
+
const refreshablePinCharges = /* @__PURE__ */ __name((before, after) => {
|
|
31333
|
+
const pre = before;
|
|
31334
|
+
const post = after;
|
|
31335
|
+
if (pinHasRecordedWrongAttempts(before) || pinHasRecordedWrongAttempts(after))
|
|
31336
|
+
return void 0;
|
|
31337
|
+
if (pre?.state !== "enabled" || post?.state !== "enabled") return void 0;
|
|
31338
|
+
if (typeof pre.attemptsRemaining !== "number" || typeof post.attemptsRemaining !== "number")
|
|
31339
|
+
return void 0;
|
|
31340
|
+
if (post.attemptsRemaining !== pre.attemptsRemaining - 1) return void 0;
|
|
31341
|
+
return { attemptsRemaining: post.attemptsRemaining, wrongAttempts: 0 };
|
|
31342
|
+
}, "refreshablePinCharges");
|
|
31343
|
+
const freshPinMaterial = /* @__PURE__ */ __name(async (pin) => {
|
|
31344
|
+
if (!validatePin(pin).ok) return void 0;
|
|
31345
|
+
try {
|
|
31346
|
+
const pinSalt = generatePinSalt();
|
|
31347
|
+
return { pinSalt, pinVerifier: await derivePinProof(pin, pinSalt) };
|
|
31348
|
+
} catch {
|
|
31349
|
+
return void 0;
|
|
31350
|
+
}
|
|
31351
|
+
}, "freshPinMaterial");
|
|
31352
|
+
const adoptReleasedShare = /* @__PURE__ */ __name(async (storage2, storageId, released, shareVersion, currentVersion) => {
|
|
31353
|
+
await storage2.storeDeviceShare(released.recoveryShare, storageId);
|
|
31354
|
+
await storage2.storeShareVersion(shareVersion, storageId);
|
|
31355
|
+
lastEmailShare = void 0;
|
|
31356
|
+
lastShareVersion = shareVersion;
|
|
31357
|
+
lastServerSnapshot = {
|
|
31358
|
+
currentVersion,
|
|
31359
|
+
resolvedVersion: shareVersion,
|
|
31360
|
+
authShare: released.authShare,
|
|
31361
|
+
primaryDid: released.primaryDid
|
|
31362
|
+
};
|
|
31363
|
+
}, "adoptReleasedShare");
|
|
31364
|
+
const completePinSignInKeep = /* @__PURE__ */ __name(async (params, released, pin, pinBefore) => {
|
|
31365
|
+
const storage2 = guardedStorage();
|
|
31366
|
+
const generation = storageGeneration;
|
|
31367
|
+
const storageId = activeStorageId;
|
|
31368
|
+
const assertActive = /* @__PURE__ */ __name(() => {
|
|
31369
|
+
if (activeStorageId !== storageId || storageGeneration !== generation) {
|
|
31370
|
+
throw new Error("This recovery request was cancelled.");
|
|
31371
|
+
}
|
|
31372
|
+
}, "assertActive");
|
|
31373
|
+
const { mode, newPinRequired, refreshCharges } = await withShareUpdateLock(
|
|
31374
|
+
storage2,
|
|
31375
|
+
storageId,
|
|
31376
|
+
() => withRotationLock(async () => {
|
|
31377
|
+
assertActive();
|
|
31378
|
+
const current = await fetchAuthShareRaw(
|
|
31379
|
+
serverUrl,
|
|
31380
|
+
params.token,
|
|
31381
|
+
params.providerType,
|
|
31382
|
+
void 0,
|
|
31383
|
+
tenantId
|
|
31384
|
+
);
|
|
31385
|
+
assertActive();
|
|
31386
|
+
if (!current || current.primaryDid !== released.primaryDid || typeof current.shareVersion !== "number") {
|
|
31387
|
+
throw new PinSignInStaleError();
|
|
31388
|
+
}
|
|
31389
|
+
let mode2;
|
|
31390
|
+
if (current.shareVersion === released.shareVersion) {
|
|
31391
|
+
if (authShareToString(current.authShare) !== released.authShare) {
|
|
31392
|
+
throw new PinSignInStaleError();
|
|
31393
|
+
}
|
|
31394
|
+
mode2 = "kept";
|
|
31395
|
+
} else {
|
|
31396
|
+
const historical = current.shareVersion > released.shareVersion ? await fetchAuthShareRaw(
|
|
31397
|
+
serverUrl,
|
|
31398
|
+
params.token,
|
|
31399
|
+
params.providerType,
|
|
31400
|
+
released.shareVersion,
|
|
31401
|
+
tenantId
|
|
31402
|
+
) : null;
|
|
31403
|
+
if (authShareToString(historical?.authShare) !== released.authShare) {
|
|
31404
|
+
throw new PinSignInStaleError();
|
|
31405
|
+
}
|
|
31406
|
+
mode2 = "deferred-rotation";
|
|
31407
|
+
}
|
|
31408
|
+
assertActive();
|
|
31409
|
+
await adoptReleasedShare(
|
|
31410
|
+
storage2,
|
|
31411
|
+
storageId,
|
|
31412
|
+
released,
|
|
31413
|
+
released.shareVersion,
|
|
31414
|
+
current.shareVersion
|
|
31415
|
+
);
|
|
31416
|
+
pendingIdentityRecovery = void 0;
|
|
31417
|
+
return {
|
|
31418
|
+
mode: mode2,
|
|
31419
|
+
newPinRequired: pinHasRecordedWrongAttempts(pinBefore) || pinHasRecordedWrongAttempts(current.escrowPin),
|
|
31420
|
+
refreshCharges: refreshablePinCharges(pinBefore, current.escrowPin)
|
|
31421
|
+
};
|
|
31422
|
+
})
|
|
31423
|
+
);
|
|
31424
|
+
let pinRefreshed = false;
|
|
31425
|
+
let pinRefreshFailed = false;
|
|
31426
|
+
if (mode === "kept" && !newPinRequired && refreshCharges && params.signDidAuthVp) {
|
|
31427
|
+
const pinMaterial = await freshPinMaterial(pin);
|
|
31428
|
+
pinRefreshed = pinMaterial !== void 0 && await tryEnrollEscrow(
|
|
31429
|
+
params.token,
|
|
31430
|
+
params.providerType,
|
|
31431
|
+
released.privateKey,
|
|
31432
|
+
released.primaryDid,
|
|
31433
|
+
{ recoveryShare: released.recoveryShare },
|
|
31434
|
+
released.shareVersion,
|
|
31435
|
+
params.signDidAuthVp,
|
|
31436
|
+
void 0,
|
|
31437
|
+
pinMaterial,
|
|
31438
|
+
false,
|
|
31439
|
+
storage2.assertCurrent,
|
|
31440
|
+
params.options?.recoveryEmail,
|
|
31441
|
+
params.options?.recoveryLanguage,
|
|
31442
|
+
refreshCharges
|
|
31443
|
+
);
|
|
31444
|
+
pinRefreshFailed = pinMaterial !== void 0 && !pinRefreshed;
|
|
31445
|
+
}
|
|
31446
|
+
assertActive();
|
|
31447
|
+
return {
|
|
31448
|
+
privateKey: released.privateKey,
|
|
31449
|
+
did: released.primaryDid,
|
|
31450
|
+
escrowEnrolled: true,
|
|
31451
|
+
escrowRecovery: true,
|
|
31452
|
+
pinSignIn: {
|
|
31453
|
+
mode,
|
|
31454
|
+
pinRefreshed,
|
|
31455
|
+
newPinRequired,
|
|
31456
|
+
...pinRefreshFailed ? { pinRefreshFailed: true } : {}
|
|
31457
|
+
}
|
|
31458
|
+
};
|
|
31459
|
+
}, "completePinSignInKeep");
|
|
31460
|
+
const completePinSignInRelink = /* @__PURE__ */ __name(async (params, pending, signDidAuthVp) => {
|
|
31461
|
+
const storage2 = guardedStorage();
|
|
31462
|
+
const generation = storageGeneration;
|
|
31463
|
+
const storageId = activeStorageId;
|
|
31464
|
+
const shareVersion = pending.pinKeep.shareVersion;
|
|
31465
|
+
const assertActive = /* @__PURE__ */ __name(() => {
|
|
31466
|
+
if (activeStorageId !== storageId || pendingIdentityRecovery !== pending || storageGeneration !== generation) {
|
|
31467
|
+
throw new Error("This recovery request was cancelled.");
|
|
31468
|
+
}
|
|
31469
|
+
}, "assertActive");
|
|
31470
|
+
await withShareUpdateLock(
|
|
31471
|
+
storage2,
|
|
31472
|
+
storageId,
|
|
31473
|
+
() => withRotationLock(async () => {
|
|
31474
|
+
assertActive();
|
|
31475
|
+
const didAuthVp = await requestFreshDidAuthVp(
|
|
31476
|
+
serverUrl,
|
|
31477
|
+
pending.privateKey,
|
|
31478
|
+
pending.primaryDid,
|
|
31479
|
+
signDidAuthVp,
|
|
31480
|
+
tenantId
|
|
31481
|
+
);
|
|
31482
|
+
assertActive();
|
|
31483
|
+
try {
|
|
31484
|
+
const response = await postJson(
|
|
31485
|
+
`${serverUrl}/keys/recovery-session/pin-sign-in`,
|
|
31486
|
+
{
|
|
31487
|
+
recoverySessionToken: pending.rebindSessionToken,
|
|
31488
|
+
providerType: params.providerType,
|
|
31489
|
+
primaryDid: pending.primaryDid
|
|
31490
|
+
},
|
|
31491
|
+
{
|
|
31492
|
+
...buildHeaders("", didAuthVp, tenantId),
|
|
31493
|
+
"X-Auth-Token": params.token
|
|
31494
|
+
}
|
|
31495
|
+
);
|
|
31496
|
+
if (response.shareVersion !== shareVersion) throw new PinSignInStaleError();
|
|
31497
|
+
} catch (error) {
|
|
31498
|
+
const current = await fetchAuthShareRaw(
|
|
31499
|
+
serverUrl,
|
|
31500
|
+
params.token,
|
|
31501
|
+
params.providerType,
|
|
31502
|
+
void 0,
|
|
31503
|
+
tenantId
|
|
31504
|
+
).catch(() => null);
|
|
31505
|
+
if (!current || current.primaryDid !== pending.primaryDid || current.shareVersion !== shareVersion || authShareToString(current.authShare) !== pending.authShare) {
|
|
31506
|
+
throw error;
|
|
31507
|
+
}
|
|
31508
|
+
}
|
|
31509
|
+
assertActive();
|
|
31510
|
+
await adoptReleasedShare(storage2, storageId, pending, shareVersion, shareVersion);
|
|
31511
|
+
pendingIdentityRecovery = void 0;
|
|
31512
|
+
})
|
|
31513
|
+
);
|
|
31514
|
+
if (activeStorageId !== storageId || storageGeneration !== generation) {
|
|
31515
|
+
throw new Error("This recovery request was cancelled.");
|
|
31516
|
+
}
|
|
31517
|
+
const status = await fetchAuthShareRaw(
|
|
31518
|
+
serverUrl,
|
|
31519
|
+
params.token,
|
|
31520
|
+
params.providerType,
|
|
31521
|
+
void 0,
|
|
31522
|
+
tenantId
|
|
31523
|
+
).catch(() => null);
|
|
31524
|
+
return {
|
|
31525
|
+
privateKey: pending.privateKey,
|
|
31526
|
+
did: pending.primaryDid,
|
|
31527
|
+
escrowEnrolled: true,
|
|
31528
|
+
escrowRecovery: true,
|
|
31529
|
+
pinSignIn: {
|
|
31530
|
+
mode: "relinked",
|
|
31531
|
+
pinRefreshed: false,
|
|
31532
|
+
newPinRequired: pinHasRecordedWrongAttempts(status?.escrowPin)
|
|
31533
|
+
}
|
|
31534
|
+
};
|
|
31535
|
+
}, "completePinSignInRelink");
|
|
31536
|
+
const completeIdentityRecoveryWith = /* @__PURE__ */ __name(async (params) => {
|
|
31537
|
+
const storage2 = guardedStorage();
|
|
31538
|
+
const generation = storageGeneration;
|
|
31539
|
+
const pending = pendingIdentityRecovery;
|
|
31540
|
+
const signDidAuthVp = params.signDidAuthVp;
|
|
31541
|
+
if (!pending) throw new Error("No identity recovery is waiting for a new sign-in");
|
|
31542
|
+
if (!signDidAuthVp) {
|
|
31543
|
+
throw new Error("DID proof signing is required to bind a new sign-in");
|
|
31544
|
+
}
|
|
31545
|
+
if (pending.pinKeep) return completePinSignInRelink(params, pending, signDidAuthVp);
|
|
31546
|
+
const storageId = activeStorageId;
|
|
31547
|
+
const { shares, shareVersion } = await withShareUpdateLock(
|
|
31548
|
+
storage2,
|
|
31549
|
+
storageId,
|
|
31550
|
+
() => withRotationLock(async () => {
|
|
31551
|
+
const assertActive = /* @__PURE__ */ __name(() => {
|
|
31552
|
+
if (activeStorageId !== storageId || pendingIdentityRecovery !== pending || storageGeneration !== generation) {
|
|
31553
|
+
throw new Error("This recovery request was cancelled.");
|
|
31554
|
+
}
|
|
31555
|
+
}, "assertActive");
|
|
31556
|
+
assertActive();
|
|
31557
|
+
const candidates = await readPendingShareCandidates(storage2, storageId);
|
|
31558
|
+
if (pending.rebind && pending.rebind.storageId !== storageId) {
|
|
31559
|
+
throw new Error("Active account changed during identity recovery");
|
|
31560
|
+
}
|
|
31561
|
+
if (candidates.some(
|
|
31562
|
+
(candidate) => candidate.share !== pending.rebind?.shares.deviceShare
|
|
31563
|
+
)) {
|
|
31564
|
+
throw new Error("Reconcile the pending share update before binding a sign-in");
|
|
31565
|
+
}
|
|
31566
|
+
let shareVersion2;
|
|
31567
|
+
const retry = pending.rebind?.attempted ?? false;
|
|
31568
|
+
if (retry && pending.rebind) {
|
|
31569
|
+
const current = await fetchAuthShareRaw(
|
|
31570
|
+
serverUrl,
|
|
31571
|
+
params.token,
|
|
31572
|
+
params.providerType,
|
|
31573
|
+
void 0,
|
|
31574
|
+
tenantId
|
|
31575
|
+
);
|
|
31576
|
+
const authShare = authShareToString(current?.authShare);
|
|
31577
|
+
if (authShare && current?.shareVersion != null) {
|
|
31578
|
+
const health = await verifyStoredShares(
|
|
31579
|
+
{
|
|
31580
|
+
getDevice: /* @__PURE__ */ __name(async () => pending.rebind.shares.deviceShare, "getDevice"),
|
|
31581
|
+
getAuth: /* @__PURE__ */ __name(async () => authShare, "getAuth")
|
|
31582
|
+
},
|
|
31583
|
+
pending.primaryDid,
|
|
31584
|
+
async (key) => key === pending.privateKey ? pending.primaryDid : ""
|
|
31585
|
+
);
|
|
31586
|
+
if (health.healthy) shareVersion2 = current.shareVersion;
|
|
31587
|
+
}
|
|
31588
|
+
}
|
|
31589
|
+
assertActive();
|
|
31590
|
+
if (shareVersion2 === void 0) {
|
|
31591
|
+
const didAuthVp = await requestFreshDidAuthVp(
|
|
31592
|
+
serverUrl,
|
|
31593
|
+
pending.privateKey,
|
|
31594
|
+
pending.primaryDid,
|
|
31595
|
+
signDidAuthVp,
|
|
31596
|
+
tenantId
|
|
31597
|
+
);
|
|
31598
|
+
assertActive();
|
|
31599
|
+
pending.rebind ??= {
|
|
31600
|
+
shares: (await splitAndVerify(pending.privateKey)).shares,
|
|
31601
|
+
storageId,
|
|
31602
|
+
attempted: false
|
|
31603
|
+
};
|
|
31604
|
+
const rebind = pending.rebind;
|
|
31605
|
+
if (!candidates.some((candidate) => candidate.share === rebind.shares.deviceShare)) {
|
|
31606
|
+
await addPendingShareCandidate(
|
|
31607
|
+
storage2,
|
|
31608
|
+
{ share: rebind.shares.deviceShare, createdAt: Date.now() },
|
|
31609
|
+
storageId
|
|
31610
|
+
);
|
|
31611
|
+
}
|
|
31612
|
+
assertActive();
|
|
31613
|
+
rebind.attempted = true;
|
|
31614
|
+
try {
|
|
31615
|
+
const response = await postJson(
|
|
31616
|
+
`${serverUrl}/keys/recovery-session/rebind`,
|
|
31617
|
+
{
|
|
31618
|
+
recoverySessionToken: pending.rebindSessionToken,
|
|
31619
|
+
providerType: params.providerType,
|
|
31620
|
+
primaryDid: pending.primaryDid,
|
|
31621
|
+
authShare: {
|
|
31622
|
+
encryptedData: rebind.shares.authShare,
|
|
31623
|
+
encryptedDek: "",
|
|
31624
|
+
iv: ""
|
|
31625
|
+
}
|
|
31626
|
+
},
|
|
31627
|
+
{
|
|
31628
|
+
...buildHeaders("", didAuthVp, tenantId),
|
|
31629
|
+
"X-Auth-Token": params.token
|
|
31630
|
+
}
|
|
31631
|
+
);
|
|
31632
|
+
shareVersion2 = response.shareVersion;
|
|
31633
|
+
} catch (error) {
|
|
31634
|
+
if (error instanceof ShareWriteRejectedError && !retry) {
|
|
31635
|
+
await removePendingShareCandidate(
|
|
31636
|
+
storage2,
|
|
31637
|
+
rebind.shares.deviceShare,
|
|
31638
|
+
storageId
|
|
31639
|
+
);
|
|
31640
|
+
pending.rebind = void 0;
|
|
31641
|
+
}
|
|
31642
|
+
throw error;
|
|
31643
|
+
}
|
|
31644
|
+
}
|
|
31645
|
+
if (shareVersion2 === void 0 || !pending.rebind) {
|
|
31646
|
+
throw new Error("Server did not confirm the new share");
|
|
31647
|
+
}
|
|
31648
|
+
assertActive();
|
|
31649
|
+
const shares2 = pending.rebind.shares;
|
|
31650
|
+
await storage2.storeDeviceShare(shares2.deviceShare, storageId);
|
|
31651
|
+
await storage2.storeShareVersion(shareVersion2, storageId);
|
|
31652
|
+
await clearPendingShareCandidates(storage2, storageId);
|
|
31653
|
+
assertActive();
|
|
31654
|
+
lastEmailShare = shares2.emailShare;
|
|
31655
|
+
lastShareVersion = shareVersion2;
|
|
31656
|
+
lastServerSnapshot = {
|
|
31657
|
+
currentVersion: shareVersion2,
|
|
31658
|
+
resolvedVersion: shareVersion2,
|
|
31659
|
+
authShare: shares2.authShare,
|
|
31660
|
+
primaryDid: pending.primaryDid
|
|
31661
|
+
};
|
|
31662
|
+
pendingIdentityRecovery = void 0;
|
|
31663
|
+
return { shares: shares2, shareVersion: shareVersion2 };
|
|
31664
|
+
})
|
|
31665
|
+
);
|
|
31666
|
+
const enrolled = pending.fromEscrow && await tryEnrollEscrow(
|
|
31667
|
+
params.token,
|
|
31668
|
+
params.providerType,
|
|
31669
|
+
pending.privateKey,
|
|
31670
|
+
pending.primaryDid,
|
|
31671
|
+
shares,
|
|
31672
|
+
shareVersion,
|
|
31673
|
+
signDidAuthVp,
|
|
31674
|
+
void 0,
|
|
31675
|
+
void 0,
|
|
31676
|
+
true,
|
|
31677
|
+
storage2.assertCurrent,
|
|
31678
|
+
params.options?.recoveryEmail,
|
|
31679
|
+
params.options?.recoveryLanguage
|
|
31680
|
+
);
|
|
31681
|
+
if (activeStorageId !== storageId || storageGeneration !== generation) {
|
|
31682
|
+
throw new Error("This recovery request was cancelled.");
|
|
31683
|
+
}
|
|
31684
|
+
if (!enrolled) {
|
|
31685
|
+
unenrolledRotation = {
|
|
31686
|
+
shares,
|
|
31687
|
+
shareVersion,
|
|
31688
|
+
primaryDid: pending.primaryDid,
|
|
31689
|
+
clearPin: true
|
|
31690
|
+
};
|
|
31691
|
+
}
|
|
31692
|
+
return {
|
|
31693
|
+
privateKey: pending.privateKey,
|
|
31694
|
+
did: pending.primaryDid,
|
|
31695
|
+
escrowEnrolled: enrolled,
|
|
31696
|
+
escrowRecovery: pending.fromEscrow
|
|
31697
|
+
};
|
|
31698
|
+
}, "completeIdentityRecoveryWith");
|
|
31294
31699
|
return {
|
|
31295
31700
|
name: "sss",
|
|
31296
31701
|
capabilities: {
|
|
@@ -31592,6 +31997,8 @@ function createSSSStrategy(config) {
|
|
|
31592
31997
|
escrowOptedOut: data.escrowOptedOut === true,
|
|
31593
31998
|
escrowPin: data.escrowPin,
|
|
31594
31999
|
escrowStale: data.escrowStale,
|
|
32000
|
+
...typeof data.escrowBlobIdentity === "string" ? { escrowBlobIdentity: data.escrowBlobIdentity } : {},
|
|
32001
|
+
...data.escrowUpdating === true ? { escrowUpdating: true } : {},
|
|
31595
32002
|
sssActivationState: data.sssActivationState ?? "active",
|
|
31596
32003
|
...typeof data.priorEscrowEnrollment === "boolean" ? { priorEscrowEnrollment: data.priorEscrowEnrollment } : {}
|
|
31597
32004
|
};
|
|
@@ -31966,9 +32373,20 @@ function createSSSStrategy(config) {
|
|
|
31966
32373
|
if (input.method === "escrow" || input.method === "escrow-pin") {
|
|
31967
32374
|
if (!didFromPrivateKey)
|
|
31968
32375
|
throw new Error("DID verification is required for escrow recovery");
|
|
31969
|
-
|
|
32376
|
+
if (input.method === "escrow-pin" && !token && !input.recoverySessionToken)
|
|
32377
|
+
throw new Error("PIN recovery requires signing in first.");
|
|
32378
|
+
const pinBefore = input.method === "escrow-pin" && token ? await fetchAuthShareRaw(
|
|
32379
|
+
serverUrl,
|
|
31970
32380
|
token,
|
|
31971
32381
|
providerType,
|
|
32382
|
+
void 0,
|
|
32383
|
+
tenantId
|
|
32384
|
+
).then(
|
|
32385
|
+
(current) => current?.escrowPin,
|
|
32386
|
+
() => void 0
|
|
32387
|
+
) : void 0;
|
|
32388
|
+
const start = input.method === "escrow-pin" ? await this.startEscrowRecovery({
|
|
32389
|
+
...token ? { token, providerType } : { recoverySessionToken: input.recoverySessionToken },
|
|
31972
32390
|
expectedDid: input.expectedDid,
|
|
31973
32391
|
options: { releasePolicy: "pin" }
|
|
31974
32392
|
}) : input;
|
|
@@ -32020,16 +32438,32 @@ function createSSSStrategy(config) {
|
|
|
32020
32438
|
"Recovery produced an incorrect key. Try another recovery method."
|
|
32021
32439
|
);
|
|
32022
32440
|
}
|
|
32441
|
+
if (input.method === "escrow-pin" && token) {
|
|
32442
|
+
return completePinSignInKeep(
|
|
32443
|
+
{ token, providerType, signDidAuthVp, options: params.options },
|
|
32444
|
+
{
|
|
32445
|
+
privateKey: privateKey2,
|
|
32446
|
+
primaryDid: material.primaryDid,
|
|
32447
|
+
recoveryShare: plaintext.recoveryShare,
|
|
32448
|
+
authShare,
|
|
32449
|
+
shareVersion: material.shareVersion
|
|
32450
|
+
},
|
|
32451
|
+
input.pin,
|
|
32452
|
+
pinBefore
|
|
32453
|
+
);
|
|
32454
|
+
}
|
|
32023
32455
|
pendingIdentityRecovery = {
|
|
32024
32456
|
privateKey: privateKey2,
|
|
32025
32457
|
primaryDid: material.primaryDid,
|
|
32026
32458
|
recoveryShare: plaintext.recoveryShare,
|
|
32027
32459
|
authShare,
|
|
32028
32460
|
rebindSessionToken: material.rebindSessionToken,
|
|
32029
|
-
fromEscrow: true
|
|
32461
|
+
fromEscrow: true,
|
|
32462
|
+
// The PIN itself is not kept across the new-login step.
|
|
32463
|
+
...input.method === "escrow-pin" ? { pinKeep: { shareVersion: material.shareVersion } } : {}
|
|
32030
32464
|
};
|
|
32031
32465
|
if (!token) return { privateKey: privateKey2, did: material.primaryDid };
|
|
32032
|
-
return
|
|
32466
|
+
return completeIdentityRecoveryWith({
|
|
32033
32467
|
token,
|
|
32034
32468
|
providerType,
|
|
32035
32469
|
signDidAuthVp,
|
|
@@ -32604,125 +33038,128 @@ function createSSSStrategy(config) {
|
|
|
32604
33038
|
pendingIdentityRecovery = void 0;
|
|
32605
33039
|
},
|
|
32606
33040
|
async completeIdentityRecovery(params) {
|
|
32607
|
-
|
|
32608
|
-
|
|
32609
|
-
|
|
32610
|
-
|
|
32611
|
-
|
|
32612
|
-
|
|
32613
|
-
|
|
33041
|
+
return completeIdentityRecoveryWith(params);
|
|
33042
|
+
},
|
|
33043
|
+
/**
|
|
33044
|
+
* "Secure my account": one atomic server update re-splits the key and drops all
|
|
33045
|
+
* older auth share versions, signing out other devices and turning off the old
|
|
33046
|
+
* phrase/backup/passkey/email key. Staged through the pending-candidate slot, so
|
|
33047
|
+
* a lost acknowledgement is promoted by the usual reconciliation. Escrow is then
|
|
33048
|
+
* re-enrolled with `pin` (fresh salt) or, for `null`, without a PIN. The PIN is
|
|
33049
|
+
* used in this call only and never kept in retry state.
|
|
33050
|
+
*/
|
|
33051
|
+
async revokeOtherDevices(params) {
|
|
33052
|
+
const { token, providerType, privateKey, did, signDidAuthVp, pin } = params;
|
|
33053
|
+
if (!signDidAuthVp) throw new Error("DID proof signing is required");
|
|
33054
|
+
if (pin !== null) {
|
|
33055
|
+
const validation = validatePin(pin);
|
|
33056
|
+
if (!validation.ok)
|
|
33057
|
+
throw new Error(
|
|
33058
|
+
validation.reason === "length" ? "PIN must contain 6\u201312 digits." : "Choose a PIN without trivial or sequential patterns."
|
|
33059
|
+
);
|
|
32614
33060
|
}
|
|
33061
|
+
const storage2 = guardedStorage();
|
|
32615
33062
|
const storageId = activeStorageId;
|
|
33063
|
+
const assertUser = captureActiveUserGuard();
|
|
33064
|
+
const assertActive = /* @__PURE__ */ __name(() => {
|
|
33065
|
+
assertUser();
|
|
33066
|
+
storage2.assertCurrent();
|
|
33067
|
+
}, "assertActive");
|
|
33068
|
+
const before = await fetchAuthShareRaw(
|
|
33069
|
+
serverUrl,
|
|
33070
|
+
token,
|
|
33071
|
+
providerType,
|
|
33072
|
+
void 0,
|
|
33073
|
+
tenantId
|
|
33074
|
+
);
|
|
33075
|
+
if (!before || before.primaryDid !== did) {
|
|
33076
|
+
throw new Error("This account could not be found. Sign in again.");
|
|
33077
|
+
}
|
|
33078
|
+
const hadEscrow = !before.escrowOptedOut && (before.priorEscrowEnrollment === true || Array.isArray(before.recoveryMethods) && before.recoveryMethods.some(
|
|
33079
|
+
(method) => method?.type === "escrow"
|
|
33080
|
+
));
|
|
33081
|
+
const didFromKey = /* @__PURE__ */ __name(async (key) => key === privateKey ? did : `${did}:mismatch`, "didFromKey");
|
|
32616
33082
|
const { shares, shareVersion } = await withShareUpdateLock(
|
|
32617
33083
|
storage2,
|
|
32618
33084
|
storageId,
|
|
32619
33085
|
() => withRotationLock(async () => {
|
|
32620
|
-
const
|
|
32621
|
-
|
|
32622
|
-
|
|
32623
|
-
|
|
32624
|
-
|
|
32625
|
-
|
|
32626
|
-
|
|
32627
|
-
|
|
32628
|
-
|
|
32629
|
-
|
|
32630
|
-
if (candidates.some(
|
|
32631
|
-
(candidate) => candidate.share !== pending.rebind?.shares.deviceShare
|
|
32632
|
-
)) {
|
|
32633
|
-
throw new Error(
|
|
32634
|
-
"Reconcile the pending share update before binding a sign-in"
|
|
32635
|
-
);
|
|
32636
|
-
}
|
|
32637
|
-
let shareVersion2;
|
|
32638
|
-
const retry = pending.rebind?.attempted ?? false;
|
|
32639
|
-
if (retry && pending.rebind) {
|
|
32640
|
-
const current = await fetchAuthShareRaw(
|
|
32641
|
-
serverUrl,
|
|
32642
|
-
params.token,
|
|
32643
|
-
params.providerType,
|
|
32644
|
-
void 0,
|
|
32645
|
-
tenantId
|
|
32646
|
-
);
|
|
32647
|
-
const authShare = authShareToString(current?.authShare);
|
|
32648
|
-
if (authShare && current?.shareVersion != null) {
|
|
32649
|
-
const health = await verifyStoredShares(
|
|
32650
|
-
{
|
|
32651
|
-
getDevice: /* @__PURE__ */ __name(async () => pending.rebind.shares.deviceShare, "getDevice"),
|
|
32652
|
-
getAuth: /* @__PURE__ */ __name(async () => authShare, "getAuth")
|
|
32653
|
-
},
|
|
32654
|
-
pending.primaryDid,
|
|
32655
|
-
async (key) => key === pending.privateKey ? pending.primaryDid : ""
|
|
33086
|
+
const { snapshot } = await resolvePendingShareCandidates(
|
|
33087
|
+
storage2,
|
|
33088
|
+
storageId,
|
|
33089
|
+
async () => {
|
|
33090
|
+
const data = await fetchAuthShareRaw(
|
|
33091
|
+
serverUrl,
|
|
33092
|
+
token,
|
|
33093
|
+
providerType,
|
|
33094
|
+
void 0,
|
|
33095
|
+
tenantId
|
|
32656
33096
|
);
|
|
32657
|
-
|
|
32658
|
-
|
|
33097
|
+
return {
|
|
33098
|
+
currentVersion: data?.shareVersion ?? null,
|
|
33099
|
+
authShare: authShareToString(data?.authShare)
|
|
33100
|
+
};
|
|
33101
|
+
},
|
|
33102
|
+
did,
|
|
33103
|
+
didFromKey,
|
|
33104
|
+
assertActive,
|
|
33105
|
+
recordPromotion(did),
|
|
33106
|
+
async (version) => authShareToString(
|
|
33107
|
+
(await fetchAuthShareRaw(
|
|
33108
|
+
serverUrl,
|
|
33109
|
+
token,
|
|
33110
|
+
providerType,
|
|
33111
|
+
version,
|
|
33112
|
+
tenantId
|
|
33113
|
+
))?.authShare
|
|
33114
|
+
)
|
|
33115
|
+
);
|
|
33116
|
+
if (!snapshot.currentVersion) {
|
|
33117
|
+
throw new Error("This account could not be found. Sign in again.");
|
|
32659
33118
|
}
|
|
33119
|
+
const expectedShareVersion = snapshot.currentVersion;
|
|
33120
|
+
const didAuthVp = await requestFreshDidAuthVp(
|
|
33121
|
+
serverUrl,
|
|
33122
|
+
privateKey,
|
|
33123
|
+
did,
|
|
33124
|
+
signDidAuthVp,
|
|
33125
|
+
tenantId
|
|
33126
|
+
);
|
|
32660
33127
|
assertActive();
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
|
|
32665
|
-
|
|
32666
|
-
|
|
32667
|
-
tenantId
|
|
32668
|
-
);
|
|
32669
|
-
assertActive();
|
|
32670
|
-
pending.rebind ??= {
|
|
32671
|
-
shares: (await splitAndVerify(pending.privateKey)).shares,
|
|
32672
|
-
storageId,
|
|
32673
|
-
attempted: false
|
|
32674
|
-
};
|
|
32675
|
-
const rebind = pending.rebind;
|
|
32676
|
-
if (!candidates.some(
|
|
32677
|
-
(candidate) => candidate.share === rebind.shares.deviceShare
|
|
32678
|
-
)) {
|
|
33128
|
+
let shareVersion2;
|
|
33129
|
+
let stagedShare = "";
|
|
33130
|
+
const shares2 = await atomicShareUpdate(privateKey, {
|
|
33131
|
+
storeDevice: /* @__PURE__ */ __name(async (share) => {
|
|
33132
|
+
assertActive();
|
|
33133
|
+
stagedShare = share;
|
|
32679
33134
|
await addPendingShareCandidate(
|
|
32680
33135
|
storage2,
|
|
32681
|
-
{ share
|
|
33136
|
+
{ share, createdAt: Date.now() },
|
|
32682
33137
|
storageId
|
|
32683
33138
|
);
|
|
32684
|
-
}
|
|
32685
|
-
|
|
32686
|
-
|
|
32687
|
-
|
|
33139
|
+
}, "storeDevice"),
|
|
33140
|
+
clearDevice: /* @__PURE__ */ __name(() => removePendingShareCandidate(storage2, stagedShare, storageId), "clearDevice"),
|
|
33141
|
+
storeAuth: /* @__PURE__ */ __name(async (share) => {
|
|
33142
|
+
assertActive();
|
|
32688
33143
|
const response = await postJson(
|
|
32689
|
-
`${serverUrl}/keys/
|
|
33144
|
+
`${serverUrl}/keys/revoke-other-devices`,
|
|
32690
33145
|
{
|
|
32691
|
-
|
|
32692
|
-
providerType
|
|
32693
|
-
primaryDid:
|
|
32694
|
-
authShare: {
|
|
32695
|
-
|
|
32696
|
-
encryptedDek: "",
|
|
32697
|
-
iv: ""
|
|
32698
|
-
}
|
|
33146
|
+
authToken: token,
|
|
33147
|
+
providerType,
|
|
33148
|
+
primaryDid: did,
|
|
33149
|
+
authShare: { encryptedData: share, encryptedDek: "", iv: "" },
|
|
33150
|
+
expectedShareVersion
|
|
32699
33151
|
},
|
|
32700
|
-
|
|
32701
|
-
...buildHeaders("", didAuthVp, tenantId),
|
|
32702
|
-
"X-Auth-Token": params.token
|
|
32703
|
-
}
|
|
33152
|
+
buildHeaders(token, didAuthVp, tenantId)
|
|
32704
33153
|
);
|
|
32705
33154
|
shareVersion2 = response.shareVersion;
|
|
32706
|
-
}
|
|
32707
|
-
|
|
32708
|
-
|
|
32709
|
-
|
|
32710
|
-
rebind.shares.deviceShare,
|
|
32711
|
-
storageId
|
|
32712
|
-
);
|
|
32713
|
-
pending.rebind = void 0;
|
|
32714
|
-
}
|
|
32715
|
-
throw error;
|
|
32716
|
-
}
|
|
32717
|
-
}
|
|
32718
|
-
if (shareVersion2 === void 0 || !pending.rebind) {
|
|
32719
|
-
throw new Error("Server did not confirm the new share");
|
|
33155
|
+
}, "storeAuth")
|
|
33156
|
+
});
|
|
33157
|
+
if (shareVersion2 === void 0) {
|
|
33158
|
+
throw new Error("Server did not return a share version");
|
|
32720
33159
|
}
|
|
32721
|
-
assertActive();
|
|
32722
|
-
const shares2 = pending.rebind.shares;
|
|
32723
33160
|
await storage2.storeDeviceShare(shares2.deviceShare, storageId);
|
|
32724
33161
|
await storage2.storeShareVersion(shareVersion2, storageId);
|
|
32725
|
-
await
|
|
33162
|
+
await removePendingShareCandidate(storage2, shares2.deviceShare, storageId);
|
|
32726
33163
|
assertActive();
|
|
32727
33164
|
lastEmailShare = shares2.emailShare;
|
|
32728
33165
|
lastShareVersion = shareVersion2;
|
|
@@ -32730,44 +33167,36 @@ function createSSSStrategy(config) {
|
|
|
32730
33167
|
currentVersion: shareVersion2,
|
|
32731
33168
|
resolvedVersion: shareVersion2,
|
|
32732
33169
|
authShare: shares2.authShare,
|
|
32733
|
-
primaryDid:
|
|
33170
|
+
primaryDid: did
|
|
32734
33171
|
};
|
|
32735
|
-
pendingIdentityRecovery = void 0;
|
|
32736
33172
|
return { shares: shares2, shareVersion: shareVersion2 };
|
|
32737
33173
|
})
|
|
32738
33174
|
);
|
|
32739
|
-
|
|
32740
|
-
|
|
32741
|
-
|
|
32742
|
-
|
|
32743
|
-
|
|
33175
|
+
if (!hadEscrow || !config.escrow?.enabled) {
|
|
33176
|
+
return { shareVersion, escrowEnrolled: false, pinKept: false };
|
|
33177
|
+
}
|
|
33178
|
+
const pinMaterial = pin === null ? void 0 : await freshPinMaterial(pin);
|
|
33179
|
+
const enrollArgs = [
|
|
33180
|
+
token,
|
|
33181
|
+
providerType,
|
|
33182
|
+
privateKey,
|
|
33183
|
+
did,
|
|
32744
33184
|
shares,
|
|
32745
33185
|
shareVersion,
|
|
32746
33186
|
signDidAuthVp,
|
|
32747
33187
|
void 0,
|
|
32748
|
-
|
|
32749
|
-
|
|
33188
|
+
pinMaterial,
|
|
33189
|
+
!pinMaterial,
|
|
32750
33190
|
storage2.assertCurrent,
|
|
32751
33191
|
params.options?.recoveryEmail,
|
|
32752
33192
|
params.options?.recoveryLanguage
|
|
32753
|
-
|
|
32754
|
-
|
|
32755
|
-
|
|
32756
|
-
}
|
|
33193
|
+
];
|
|
33194
|
+
const attempt = await attemptEnrollEscrow(...enrollArgs);
|
|
33195
|
+
const enrolled = attempt.enrolled || attempt.error instanceof EscrowRequestError && attempt.error.status === 403 && await tryEnrollEscrow(...enrollArgs);
|
|
32757
33196
|
if (!enrolled) {
|
|
32758
|
-
unenrolledRotation = {
|
|
32759
|
-
shares,
|
|
32760
|
-
shareVersion,
|
|
32761
|
-
primaryDid: pending.primaryDid,
|
|
32762
|
-
clearPin: true
|
|
32763
|
-
};
|
|
33197
|
+
unenrolledRotation = { shares, shareVersion, primaryDid: did, clearPin: true };
|
|
32764
33198
|
}
|
|
32765
|
-
return {
|
|
32766
|
-
privateKey: pending.privateKey,
|
|
32767
|
-
did: pending.primaryDid,
|
|
32768
|
-
escrowEnrolled: enrolled,
|
|
32769
|
-
escrowRecovery: pending.fromEscrow
|
|
32770
|
-
};
|
|
33199
|
+
return { shareVersion, escrowEnrolled: enrolled, pinKept: enrolled && !!pinMaterial };
|
|
32771
33200
|
},
|
|
32772
33201
|
// --- Contact method management ---
|
|
32773
33202
|
async upgradeContactMethod(token, providerType, previousPhone, email, code) {
|