@learncard/sss-key-manager 0.1.23 → 0.2.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 +12 -95
- package/dist/api-client.d.ts +44 -7
- package/dist/api-client.d.ts.map +1 -1
- package/dist/atomic-operations.d.ts +10 -2
- package/dist/atomic-operations.d.ts.map +1 -1
- package/dist/email-relay-crypto.d.ts +49 -0
- package/dist/email-relay-crypto.d.ts.map +1 -0
- package/dist/escrow-attestation.d.ts +8 -0
- package/dist/escrow-attestation.d.ts.map +1 -0
- package/dist/escrow-crypto.d.ts +52 -0
- package/dist/escrow-crypto.d.ts.map +1 -0
- package/dist/escrow-pin.d.ts +22 -0
- package/dist/escrow-pin.d.ts.map +1 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/key-manager.d.ts +2 -0
- package/dist/key-manager.d.ts.map +1 -1
- package/dist/qr-login.d.ts.map +1 -1
- package/dist/recovery-phrase.d.ts +6 -0
- package/dist/recovery-phrase.d.ts.map +1 -1
- package/dist/sss-key-manager.cjs.development.js +3163 -440
- package/dist/sss-key-manager.cjs.development.js.map +4 -4
- package/dist/sss-key-manager.cjs.production.min.js +33 -33
- package/dist/sss-key-manager.cjs.production.min.js.map +4 -4
- package/dist/sss-key-manager.esm.js +3163 -440
- package/dist/sss-key-manager.esm.js.map +4 -4
- package/dist/sss-strategy.d.ts +74 -4
- package/dist/sss-strategy.d.ts.map +1 -1
- package/dist/storage.d.ts +1 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/types.d.ts +87 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api-client.test.ts +122 -0
- package/src/api-client.ts +107 -116
- package/src/atomic-operations.test.ts +56 -16
- package/src/atomic-operations.ts +55 -19
- package/src/critical-paths.test.ts +51 -1
- package/src/email-relay-crypto.test.ts +87 -0
- package/src/email-relay-crypto.ts +293 -0
- package/src/escrow-attestation.test.ts +85 -0
- package/src/escrow-attestation.ts +64 -0
- package/src/escrow-crypto.test.ts +381 -0
- package/src/escrow-crypto.ts +319 -0
- package/src/escrow-pin.test.ts +119 -0
- package/src/escrow-pin.ts +83 -0
- package/src/index.ts +63 -1
- package/src/key-manager.ts +4 -9
- package/src/qr-login.ts +22 -15
- package/src/recovery-phrase.ts +40 -0
- package/src/sss-session-writes.test.ts +109 -0
- package/src/sss-strategy.test.ts +2642 -110
- package/src/sss-strategy.ts +2186 -224
- package/src/storage.test.ts +66 -8
- package/src/storage.ts +41 -29
- package/src/types.ts +107 -6
|
@@ -55,12 +55,15 @@ var require_types_cjs_development = __commonJS({
|
|
|
55
55
|
var __toCommonJS2 = /* @__PURE__ */ __name((mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod), "__toCommonJS");
|
|
56
56
|
var index_exports2 = {};
|
|
57
57
|
__export2(index_exports2, {
|
|
58
|
+
ACT_AS_HEADER: /* @__PURE__ */ __name(() => ACT_AS_HEADER, "ACT_AS_HEADER"),
|
|
58
59
|
AUTH_GRANT_AUDIENCE_DOMAIN_PREFIX: /* @__PURE__ */ __name(() => AUTH_GRANT_AUDIENCE_DOMAIN_PREFIX, "AUTH_GRANT_AUDIENCE_DOMAIN_PREFIX"),
|
|
59
60
|
AchievementCredentialValidator: /* @__PURE__ */ __name(() => AchievementCredentialValidator, "AchievementCredentialValidator"),
|
|
60
61
|
AchievementCriteriaValidator: /* @__PURE__ */ __name(() => AchievementCriteriaValidator, "AchievementCriteriaValidator"),
|
|
61
62
|
AchievementSubjectValidator: /* @__PURE__ */ __name(() => AchievementSubjectValidator, "AchievementSubjectValidator"),
|
|
62
63
|
AchievementTypeValidator: /* @__PURE__ */ __name(() => AchievementTypeValidator, "AchievementTypeValidator"),
|
|
63
64
|
AchievementValidator: /* @__PURE__ */ __name(() => AchievementValidator, "AchievementValidator"),
|
|
65
|
+
AcknowledgeViewInputValidator: /* @__PURE__ */ __name(() => AcknowledgeViewInputValidator, "AcknowledgeViewInputValidator"),
|
|
66
|
+
AcknowledgeViewOutputValidator: /* @__PURE__ */ __name(() => AcknowledgeViewOutputValidator, "AcknowledgeViewOutputValidator"),
|
|
64
67
|
AddTagInputValidator: /* @__PURE__ */ __name(() => AddTagInputValidator, "AddTagInputValidator"),
|
|
65
68
|
AddressValidator: /* @__PURE__ */ __name(() => AddressValidator, "AddressValidator"),
|
|
66
69
|
AgeRatingValidator: /* @__PURE__ */ __name(() => AgeRatingValidator, "AgeRatingValidator"),
|
|
@@ -138,6 +141,7 @@ var require_types_cjs_development = __commonJS({
|
|
|
138
141
|
CreateContactMethodSessionValidator: /* @__PURE__ */ __name(() => CreateContactMethodSessionValidator, "CreateContactMethodSessionValidator"),
|
|
139
142
|
CreateManagedFrameworkBatchInputValidator: /* @__PURE__ */ __name(() => CreateManagedFrameworkBatchInputValidator, "CreateManagedFrameworkBatchInputValidator"),
|
|
140
143
|
CreateManagedFrameworkInputValidator: /* @__PURE__ */ __name(() => CreateManagedFrameworkInputValidator, "CreateManagedFrameworkInputValidator"),
|
|
144
|
+
CreateShareLinkInputValidator: /* @__PURE__ */ __name(() => CreateShareLinkInputValidator, "CreateShareLinkInputValidator"),
|
|
141
145
|
CreateSkillInputValidator: /* @__PURE__ */ __name(() => CreateSkillInputValidator, "CreateSkillInputValidator"),
|
|
142
146
|
CreateSkillsBatchInputValidator: /* @__PURE__ */ __name(() => CreateSkillsBatchInputValidator, "CreateSkillsBatchInputValidator"),
|
|
143
147
|
CredentialActivityEventTypeValidator: /* @__PURE__ */ __name(() => CredentialActivityEventTypeValidator, "CredentialActivityEventTypeValidator"),
|
|
@@ -169,10 +173,14 @@ var require_types_cjs_development = __commonJS({
|
|
|
169
173
|
DeleteSkillInputValidator: /* @__PURE__ */ __name(() => DeleteSkillInputValidator, "DeleteSkillInputValidator"),
|
|
170
174
|
DidDocumentValidator: /* @__PURE__ */ __name(() => DidDocumentValidator, "DidDocumentValidator"),
|
|
171
175
|
EMPTY_IN_APP_MESSAGES_FLAG: /* @__PURE__ */ __name(() => EMPTY_IN_APP_MESSAGES_FLAG, "EMPTY_IN_APP_MESSAGES_FLAG"),
|
|
176
|
+
ESCROW_PIN_LOCKED_MESSAGE: /* @__PURE__ */ __name(() => ESCROW_PIN_LOCKED_MESSAGE2, "ESCROW_PIN_LOCKED_MESSAGE"),
|
|
177
|
+
ESCROW_PIN_MISMATCH_PATTERN: /* @__PURE__ */ __name(() => ESCROW_PIN_MISMATCH_PATTERN2, "ESCROW_PIN_MISMATCH_PATTERN"),
|
|
178
|
+
ESCROW_PIN_UNAVAILABLE_MESSAGE: /* @__PURE__ */ __name(() => ESCROW_PIN_UNAVAILABLE_MESSAGE2, "ESCROW_PIN_UNAVAILABLE_MESSAGE"),
|
|
172
179
|
EncryptedCredentialRecordValidator: /* @__PURE__ */ __name(() => EncryptedCredentialRecordValidator, "EncryptedCredentialRecordValidator"),
|
|
173
180
|
EncryptedRecordValidator: /* @__PURE__ */ __name(() => EncryptedRecordValidator, "EncryptedRecordValidator"),
|
|
174
181
|
EndorsementCredentialValidator: /* @__PURE__ */ __name(() => EndorsementCredentialValidator, "EndorsementCredentialValidator"),
|
|
175
182
|
EndorsementSubjectValidator: /* @__PURE__ */ __name(() => EndorsementSubjectValidator, "EndorsementSubjectValidator"),
|
|
183
|
+
EscrowPinStatusValidator: /* @__PURE__ */ __name(() => EscrowPinStatusValidator, "EscrowPinStatusValidator"),
|
|
176
184
|
EvidenceValidator: /* @__PURE__ */ __name(() => EvidenceValidator, "EvidenceValidator"),
|
|
177
185
|
FlatAuthGrantValidator: /* @__PURE__ */ __name(() => FlatAuthGrantValidator, "FlatAuthGrantValidator"),
|
|
178
186
|
FrameworkWithSkillsValidator: /* @__PURE__ */ __name(() => FrameworkWithSkillsValidator, "FrameworkWithSkillsValidator"),
|
|
@@ -194,10 +202,18 @@ var require_types_cjs_development = __commonJS({
|
|
|
194
202
|
IdentifierTypeValidator: /* @__PURE__ */ __name(() => IdentifierTypeValidator, "IdentifierTypeValidator"),
|
|
195
203
|
IdentityObjectValidator: /* @__PURE__ */ __name(() => IdentityObjectValidator, "IdentityObjectValidator"),
|
|
196
204
|
ImageValidator: /* @__PURE__ */ __name(() => ImageValidator, "ImageValidator"),
|
|
205
|
+
InboxBatchErrorReasonValidator: /* @__PURE__ */ __name(() => InboxBatchErrorReasonValidator, "InboxBatchErrorReasonValidator"),
|
|
206
|
+
InboxBatchReceiptValidator: /* @__PURE__ */ __name(() => InboxBatchReceiptValidator, "InboxBatchReceiptValidator"),
|
|
207
|
+
InboxBatchStatusValidator: /* @__PURE__ */ __name(() => InboxBatchStatusValidator, "InboxBatchStatusValidator"),
|
|
197
208
|
InboxCredentialQueryValidator: /* @__PURE__ */ __name(() => InboxCredentialQueryValidator, "InboxCredentialQueryValidator"),
|
|
209
|
+
InboxCredentialRefreshReceiptValidator: /* @__PURE__ */ __name(() => InboxCredentialRefreshReceiptValidator, "InboxCredentialRefreshReceiptValidator"),
|
|
198
210
|
InboxCredentialValidator: /* @__PURE__ */ __name(() => InboxCredentialValidator, "InboxCredentialValidator"),
|
|
199
211
|
IncrementCounterEventValidator: /* @__PURE__ */ __name(() => IncrementCounterEventValidator, "IncrementCounterEventValidator"),
|
|
200
212
|
InstalledAppValidator: /* @__PURE__ */ __name(() => InstalledAppValidator, "InstalledAppValidator"),
|
|
213
|
+
IssueInboxCredentialBatchItemResultValidator: /* @__PURE__ */ __name(() => IssueInboxCredentialBatchItemResultValidator, "IssueInboxCredentialBatchItemResultValidator"),
|
|
214
|
+
IssueInboxCredentialBatchItemValidator: /* @__PURE__ */ __name(() => IssueInboxCredentialBatchItemValidator, "IssueInboxCredentialBatchItemValidator"),
|
|
215
|
+
IssueInboxCredentialBatchResponseValidator: /* @__PURE__ */ __name(() => IssueInboxCredentialBatchResponseValidator, "IssueInboxCredentialBatchResponseValidator"),
|
|
216
|
+
IssueInboxCredentialBatchValidator: /* @__PURE__ */ __name(() => IssueInboxCredentialBatchValidator, "IssueInboxCredentialBatchValidator"),
|
|
201
217
|
IssueInboxCredentialResponseValidator: /* @__PURE__ */ __name(() => IssueInboxCredentialResponseValidator, "IssueInboxCredentialResponseValidator"),
|
|
202
218
|
IssueInboxCredentialValidator: /* @__PURE__ */ __name(() => IssueInboxCredentialValidator, "IssueInboxCredentialValidator"),
|
|
203
219
|
IssueInboxSigningAuthorityValidator: /* @__PURE__ */ __name(() => IssueInboxSigningAuthorityValidator, "IssueInboxSigningAuthorityValidator"),
|
|
@@ -246,6 +262,15 @@ var require_types_cjs_development = __commonJS({
|
|
|
246
262
|
LaunchTypeValidator: /* @__PURE__ */ __name(() => LaunchTypeValidator, "LaunchTypeValidator"),
|
|
247
263
|
LearnCardRefreshAuthorizationValidator: /* @__PURE__ */ __name(() => LearnCardRefreshAuthorizationValidator, "LearnCardRefreshAuthorizationValidator"),
|
|
248
264
|
LinkProviderFrameworkInputValidator: /* @__PURE__ */ __name(() => LinkProviderFrameworkInputValidator, "LinkProviderFrameworkInputValidator"),
|
|
265
|
+
ListShareLinksInputValidator: /* @__PURE__ */ __name(() => ListShareLinksInputValidator, "ListShareLinksInputValidator"),
|
|
266
|
+
MAX_ENDORSEMENTS: /* @__PURE__ */ __name(() => MAX_ENDORSEMENTS, "MAX_ENDORSEMENTS"),
|
|
267
|
+
MAX_SELECTED_CREDENTIALS: /* @__PURE__ */ __name(() => MAX_SELECTED_CREDENTIALS, "MAX_SELECTED_CREDENTIALS"),
|
|
268
|
+
MAX_SHARE_CIPHERTEXT_BYTES: /* @__PURE__ */ __name(() => MAX_SHARE_CIPHERTEXT_BYTES, "MAX_SHARE_CIPHERTEXT_BYTES"),
|
|
269
|
+
MAX_SHARE_LINK_REQUEST_BYTES: /* @__PURE__ */ __name(() => MAX_SHARE_LINK_REQUEST_BYTES, "MAX_SHARE_LINK_REQUEST_BYTES"),
|
|
270
|
+
MAX_SHARE_RECOVERY_JWE_BYTES: /* @__PURE__ */ __name(() => MAX_SHARE_RECOVERY_JWE_BYTES, "MAX_SHARE_RECOVERY_JWE_BYTES"),
|
|
271
|
+
MAX_SHARE_SOURCE_URI_CHARS: /* @__PURE__ */ __name(() => MAX_SHARE_SOURCE_URI_CHARS, "MAX_SHARE_SOURCE_URI_CHARS"),
|
|
272
|
+
MAX_VP_MEMBERS: /* @__PURE__ */ __name(() => MAX_VP_MEMBERS, "MAX_VP_MEMBERS"),
|
|
273
|
+
ManagedCredentialRefreshReceiptValidator: /* @__PURE__ */ __name(() => ManagedCredentialRefreshReceiptValidator, "ManagedCredentialRefreshReceiptValidator"),
|
|
249
274
|
ManagedCredentialRefreshServiceValidator: /* @__PURE__ */ __name(() => ManagedCredentialRefreshServiceValidator, "ManagedCredentialRefreshServiceValidator"),
|
|
250
275
|
PaginatedAppStoreListingsValidator: /* @__PURE__ */ __name(() => PaginatedAppStoreListingsValidator, "PaginatedAppStoreListingsValidator"),
|
|
251
276
|
PaginatedBoostRecipientsValidator: /* @__PURE__ */ __name(() => PaginatedBoostRecipientsValidator, "PaginatedBoostRecipientsValidator"),
|
|
@@ -267,11 +292,14 @@ var require_types_cjs_development = __commonJS({
|
|
|
267
292
|
PaginatedLCNProfileManagersValidator: /* @__PURE__ */ __name(() => PaginatedLCNProfileManagersValidator, "PaginatedLCNProfileManagersValidator"),
|
|
268
293
|
PaginatedLCNProfilesAndManagersValidator: /* @__PURE__ */ __name(() => PaginatedLCNProfilesAndManagersValidator, "PaginatedLCNProfilesAndManagersValidator"),
|
|
269
294
|
PaginatedLCNProfilesValidator: /* @__PURE__ */ __name(() => PaginatedLCNProfilesValidator, "PaginatedLCNProfilesValidator"),
|
|
295
|
+
PaginatedShareLinksValidator: /* @__PURE__ */ __name(() => PaginatedShareLinksValidator, "PaginatedShareLinksValidator"),
|
|
270
296
|
PaginatedSkillFrameworksValidator: /* @__PURE__ */ __name(() => PaginatedSkillFrameworksValidator, "PaginatedSkillFrameworksValidator"),
|
|
271
297
|
PaginatedSkillTreeValidator: /* @__PURE__ */ __name(() => PaginatedSkillTreeValidator, "PaginatedSkillTreeValidator"),
|
|
272
298
|
PaginatedVisibleLCNProfilesValidator: /* @__PURE__ */ __name(() => PaginatedVisibleLCNProfilesValidator, "PaginatedVisibleLCNProfilesValidator"),
|
|
273
299
|
PaginationOptionsValidator: /* @__PURE__ */ __name(() => PaginationOptionsValidator, "PaginationOptionsValidator"),
|
|
274
300
|
PaginationResponseValidator: /* @__PURE__ */ __name(() => PaginationResponseValidator, "PaginationResponseValidator"),
|
|
301
|
+
PrepareRefreshableSendInputValidator: /* @__PURE__ */ __name(() => PrepareRefreshableSendInputValidator, "PrepareRefreshableSendInputValidator"),
|
|
302
|
+
PrepareRefreshableSendResultValidator: /* @__PURE__ */ __name(() => PrepareRefreshableSendResultValidator, "PrepareRefreshableSendResultValidator"),
|
|
275
303
|
ProfileValidator: /* @__PURE__ */ __name(() => ProfileValidator, "ProfileValidator"),
|
|
276
304
|
ProfileVisibilityEnum: /* @__PURE__ */ __name(() => ProfileVisibilityEnum, "ProfileVisibilityEnum"),
|
|
277
305
|
PromotionLevelValidator: /* @__PURE__ */ __name(() => PromotionLevelValidator, "PromotionLevelValidator"),
|
|
@@ -289,14 +317,24 @@ var require_types_cjs_development = __commonJS({
|
|
|
289
317
|
ReplaceSkillFrameworkSkillsInputValidator: /* @__PURE__ */ __name(() => ReplaceSkillFrameworkSkillsInputValidator, "ReplaceSkillFrameworkSkillsInputValidator"),
|
|
290
318
|
ReplaceSkillFrameworkSkillsResultValidator: /* @__PURE__ */ __name(() => ReplaceSkillFrameworkSkillsResultValidator, "ReplaceSkillFrameworkSkillsResultValidator"),
|
|
291
319
|
RequestLearnerContextEventValidator: /* @__PURE__ */ __name(() => RequestLearnerContextEventValidator, "RequestLearnerContextEventValidator"),
|
|
320
|
+
ResolveShareLinkInputValidator: /* @__PURE__ */ __name(() => ResolveShareLinkInputValidator, "ResolveShareLinkInputValidator"),
|
|
292
321
|
ResultDescriptionValidator: /* @__PURE__ */ __name(() => ResultDescriptionValidator, "ResultDescriptionValidator"),
|
|
293
322
|
ResultStatusTypeValidator: /* @__PURE__ */ __name(() => ResultStatusTypeValidator, "ResultStatusTypeValidator"),
|
|
294
323
|
ResultTypeValidator: /* @__PURE__ */ __name(() => ResultTypeValidator, "ResultTypeValidator"),
|
|
295
324
|
ResultValidator: /* @__PURE__ */ __name(() => ResultValidator, "ResultValidator"),
|
|
296
325
|
RubricCriterionValidator: /* @__PURE__ */ __name(() => RubricCriterionValidator, "RubricCriterionValidator"),
|
|
326
|
+
SHARE_AAD_PREFIX: /* @__PURE__ */ __name(() => SHARE_AAD_PREFIX, "SHARE_AAD_PREFIX"),
|
|
327
|
+
SHARE_CONTENT_KEY_BYTES: /* @__PURE__ */ __name(() => SHARE_CONTENT_KEY_BYTES, "SHARE_CONTENT_KEY_BYTES"),
|
|
328
|
+
SHARE_IV_BYTES: /* @__PURE__ */ __name(() => SHARE_IV_BYTES, "SHARE_IV_BYTES"),
|
|
329
|
+
SHARE_LINK_ID_BYTES: /* @__PURE__ */ __name(() => SHARE_LINK_ID_BYTES, "SHARE_LINK_ID_BYTES"),
|
|
330
|
+
SHARE_LINK_PROTOCOL: /* @__PURE__ */ __name(() => SHARE_LINK_PROTOCOL, "SHARE_LINK_PROTOCOL"),
|
|
331
|
+
SHARE_RECOVERY_PROTOCOL: /* @__PURE__ */ __name(() => SHARE_RECOVERY_PROTOCOL, "SHARE_RECOVERY_PROTOCOL"),
|
|
332
|
+
SHARE_TAG_BYTES: /* @__PURE__ */ __name(() => SHARE_TAG_BYTES, "SHARE_TAG_BYTES"),
|
|
333
|
+
SHARE_VIEW_RECEIPT_TTL_SECONDS: /* @__PURE__ */ __name(() => SHARE_VIEW_RECEIPT_TTL_SECONDS, "SHARE_VIEW_RECEIPT_TTL_SECONDS"),
|
|
297
334
|
SendAiSessionCredentialEventValidator: /* @__PURE__ */ __name(() => SendAiSessionCredentialEventValidator, "SendAiSessionCredentialEventValidator"),
|
|
298
335
|
SendBoostInputValidator: /* @__PURE__ */ __name(() => SendBoostInputValidator, "SendBoostInputValidator"),
|
|
299
336
|
SendBoostResponseValidator: /* @__PURE__ */ __name(() => SendBoostResponseValidator, "SendBoostResponseValidator"),
|
|
337
|
+
SendBoostTemplateValidator: /* @__PURE__ */ __name(() => SendBoostTemplateValidator, "SendBoostTemplateValidator"),
|
|
300
338
|
SendBrandingOptionsValidator: /* @__PURE__ */ __name(() => SendBrandingOptionsValidator, "SendBrandingOptionsValidator"),
|
|
301
339
|
SendCredentialEventValidator: /* @__PURE__ */ __name(() => SendCredentialEventValidator, "SendCredentialEventValidator"),
|
|
302
340
|
SendInboxResponseValidator: /* @__PURE__ */ __name(() => SendInboxResponseValidator, "SendInboxResponseValidator"),
|
|
@@ -307,6 +345,32 @@ var require_types_cjs_development = __commonJS({
|
|
|
307
345
|
SentCredentialInfoValidator: /* @__PURE__ */ __name(() => SentCredentialInfoValidator, "SentCredentialInfoValidator"),
|
|
308
346
|
ServiceValidator: /* @__PURE__ */ __name(() => ServiceValidator, "ServiceValidator"),
|
|
309
347
|
SetPrimaryContactMethodValidator: /* @__PURE__ */ __name(() => SetPrimaryContactMethodValidator, "SetPrimaryContactMethodValidator"),
|
|
348
|
+
ShareCiphertextValidator: /* @__PURE__ */ __name(() => ShareCiphertextValidator, "ShareCiphertextValidator"),
|
|
349
|
+
ShareContentKeyValidator: /* @__PURE__ */ __name(() => ShareContentKeyValidator, "ShareContentKeyValidator"),
|
|
350
|
+
ShareContentStateValidator: /* @__PURE__ */ __name(() => ShareContentStateValidator, "ShareContentStateValidator"),
|
|
351
|
+
ShareEndorsementEntryValidator: /* @__PURE__ */ __name(() => ShareEndorsementEntryValidator, "ShareEndorsementEntryValidator"),
|
|
352
|
+
ShareEnvelopeValidator: /* @__PURE__ */ __name(() => ShareEnvelopeValidator, "ShareEnvelopeValidator"),
|
|
353
|
+
ShareIvValidator: /* @__PURE__ */ __name(() => ShareIvValidator, "ShareIvValidator"),
|
|
354
|
+
ShareLinkIdInputValidator: /* @__PURE__ */ __name(() => ShareLinkIdInputValidator, "ShareLinkIdInputValidator"),
|
|
355
|
+
ShareLinkIdValidator: /* @__PURE__ */ __name(() => ShareLinkIdValidator, "ShareLinkIdValidator"),
|
|
356
|
+
ShareLinkOperationKeyInputValidator: /* @__PURE__ */ __name(() => ShareLinkOperationKeyInputValidator, "ShareLinkOperationKeyInputValidator"),
|
|
357
|
+
ShareLinkOwnerCommitOutputValidator: /* @__PURE__ */ __name(() => ShareLinkOwnerCommitOutputValidator, "ShareLinkOwnerCommitOutputValidator"),
|
|
358
|
+
ShareLinkOwnerContentOutputValidator: /* @__PURE__ */ __name(() => ShareLinkOwnerContentOutputValidator, "ShareLinkOwnerContentOutputValidator"),
|
|
359
|
+
ShareLinkOwnerRecoveryOutputValidator: /* @__PURE__ */ __name(() => ShareLinkOwnerRecoveryOutputValidator, "ShareLinkOwnerRecoveryOutputValidator"),
|
|
360
|
+
ShareLinkOwnerStatusOutputValidator: /* @__PURE__ */ __name(() => ShareLinkOwnerStatusOutputValidator, "ShareLinkOwnerStatusOutputValidator"),
|
|
361
|
+
ShareLinkPublicContentViewValidator: /* @__PURE__ */ __name(() => ShareLinkPublicContentViewValidator, "ShareLinkPublicContentViewValidator"),
|
|
362
|
+
ShareLinkPublicStateValidator: /* @__PURE__ */ __name(() => ShareLinkPublicStateValidator, "ShareLinkPublicStateValidator"),
|
|
363
|
+
ShareLinkStatusValidator: /* @__PURE__ */ __name(() => ShareLinkStatusValidator, "ShareLinkStatusValidator"),
|
|
364
|
+
ShareLinkValidator: /* @__PURE__ */ __name(() => ShareLinkValidator, "ShareLinkValidator"),
|
|
365
|
+
ShareManifestPresentationValidator: /* @__PURE__ */ __name(() => ShareManifestPresentationValidator, "ShareManifestPresentationValidator"),
|
|
366
|
+
ShareOwnerRecoveryValidator: /* @__PURE__ */ __name(() => ShareOwnerRecoveryValidator, "ShareOwnerRecoveryValidator"),
|
|
367
|
+
SharePayloadValidator: /* @__PURE__ */ __name(() => SharePayloadValidator, "SharePayloadValidator"),
|
|
368
|
+
SharePayloadValidatorShape: /* @__PURE__ */ __name(() => SharePayloadValidatorShape, "SharePayloadValidatorShape"),
|
|
369
|
+
ShareRecoveryEndorsementValidator: /* @__PURE__ */ __name(() => ShareRecoveryEndorsementValidator, "ShareRecoveryEndorsementValidator"),
|
|
370
|
+
ShareRecoveryPlaintextValidator: /* @__PURE__ */ __name(() => ShareRecoveryPlaintextValidator, "ShareRecoveryPlaintextValidator"),
|
|
371
|
+
ShareRecoverySelectionValidator: /* @__PURE__ */ __name(() => ShareRecoverySelectionValidator, "ShareRecoverySelectionValidator"),
|
|
372
|
+
ShareSelectionEntryValidator: /* @__PURE__ */ __name(() => ShareSelectionEntryValidator, "ShareSelectionEntryValidator"),
|
|
373
|
+
ShareSharerValidator: /* @__PURE__ */ __name(() => ShareSharerValidator, "ShareSharerValidator"),
|
|
310
374
|
SkillDeletionStrategyValidator: /* @__PURE__ */ __name(() => SkillDeletionStrategyValidator, "SkillDeletionStrategyValidator"),
|
|
311
375
|
SkillFrameworkAdminInputValidator: /* @__PURE__ */ __name(() => SkillFrameworkAdminInputValidator, "SkillFrameworkAdminInputValidator"),
|
|
312
376
|
SkillFrameworkAdminsValidator: /* @__PURE__ */ __name(() => SkillFrameworkAdminsValidator, "SkillFrameworkAdminsValidator"),
|
|
@@ -338,6 +402,7 @@ var require_types_cjs_development = __commonJS({
|
|
|
338
402
|
UnsignedVCValidator: /* @__PURE__ */ __name(() => UnsignedVCValidator, "UnsignedVCValidator"),
|
|
339
403
|
UnsignedVPValidator: /* @__PURE__ */ __name(() => UnsignedVPValidator, "UnsignedVPValidator"),
|
|
340
404
|
UpdateFrameworkInputValidator: /* @__PURE__ */ __name(() => UpdateFrameworkInputValidator, "UpdateFrameworkInputValidator"),
|
|
405
|
+
UpdateShareLinkInputValidator: /* @__PURE__ */ __name(() => UpdateShareLinkInputValidator, "UpdateShareLinkInputValidator"),
|
|
341
406
|
UpdateSkillInputValidator: /* @__PURE__ */ __name(() => UpdateSkillInputValidator, "UpdateSkillInputValidator"),
|
|
342
407
|
VC2EvidenceValidator: /* @__PURE__ */ __name(() => VC2EvidenceValidator, "VC2EvidenceValidator"),
|
|
343
408
|
VCValidator: /* @__PURE__ */ __name(() => VCValidator, "VCValidator"),
|
|
@@ -349,7 +414,12 @@ var require_types_cjs_development = __commonJS({
|
|
|
349
414
|
VerificationStatusValidator: /* @__PURE__ */ __name(() => VerificationStatusValidator, "VerificationStatusValidator"),
|
|
350
415
|
appStoreActionValidator: /* @__PURE__ */ __name(() => appStoreActionValidator, "appStoreActionValidator"),
|
|
351
416
|
capgoUpdateActionValidator: /* @__PURE__ */ __name(() => capgoUpdateActionValidator, "capgoUpdateActionValidator"),
|
|
417
|
+
classifyShareManifest: /* @__PURE__ */ __name(() => classifyShareManifest, "classifyShareManifest"),
|
|
418
|
+
decodeBase64Url: /* @__PURE__ */ __name(() => decodeBase64Url, "decodeBase64Url"),
|
|
419
|
+
decodedBase64UrlByteLength: /* @__PURE__ */ __name(() => decodedBase64UrlByteLength, "decodedBase64UrlByteLength"),
|
|
352
420
|
dismissActionValidator: /* @__PURE__ */ __name(() => dismissActionValidator, "dismissActionValidator"),
|
|
421
|
+
encodeBase64Url: /* @__PURE__ */ __name(() => encodeBase64Url, "encodeBase64Url"),
|
|
422
|
+
escrowPinMismatchMessage: /* @__PURE__ */ __name(() => escrowPinMismatchMessage, "escrowPinMismatchMessage"),
|
|
353
423
|
externalLinkActionValidator: /* @__PURE__ */ __name(() => externalLinkActionValidator, "externalLinkActionValidator"),
|
|
354
424
|
inAppMessageActionStyleValidator: /* @__PURE__ */ __name(() => inAppMessageActionStyleValidator, "inAppMessageActionStyleValidator"),
|
|
355
425
|
inAppMessageActionTargetValidator: /* @__PURE__ */ __name(() => inAppMessageActionTargetValidator, "inAppMessageActionTargetValidator"),
|
|
@@ -364,10 +434,12 @@ var require_types_cjs_development = __commonJS({
|
|
|
364
434
|
inAppMessageVersionSourceValidator: /* @__PURE__ */ __name(() => inAppMessageVersionSourceValidator, "inAppMessageVersionSourceValidator"),
|
|
365
435
|
inAppMessagesFlagValidator: /* @__PURE__ */ __name(() => inAppMessagesFlagValidator, "inAppMessagesFlagValidator"),
|
|
366
436
|
internalLinkActionValidator: /* @__PURE__ */ __name(() => internalLinkActionValidator, "internalLinkActionValidator"),
|
|
437
|
+
isCanonicalBase64Url: /* @__PURE__ */ __name(() => isCanonicalBase64Url, "isCanonicalBase64Url"),
|
|
367
438
|
isStoredCredentialEnvelope: /* @__PURE__ */ __name(() => isStoredCredentialEnvelope, "isStoredCredentialEnvelope"),
|
|
368
439
|
parseInAppMessagesFlag: /* @__PURE__ */ __name(() => parseInAppMessagesFlag, "parseInAppMessagesFlag"),
|
|
369
440
|
platformPredicateValidator: /* @__PURE__ */ __name(() => platformPredicateValidator, "platformPredicateValidator"),
|
|
370
441
|
rolePredicateValidator: /* @__PURE__ */ __name(() => rolePredicateValidator, "rolePredicateValidator"),
|
|
442
|
+
utf8ByteLength: /* @__PURE__ */ __name(() => utf8ByteLength, "utf8ByteLength"),
|
|
371
443
|
versionPredicateValidator: /* @__PURE__ */ __name(() => versionPredicateValidator, "versionPredicateValidator")
|
|
372
444
|
});
|
|
373
445
|
module2.exports = __toCommonJS2(index_exports2);
|
|
@@ -16221,6 +16293,204 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16221
16293
|
$or: BaseStringQuery.array()
|
|
16222
16294
|
})
|
|
16223
16295
|
]);
|
|
16296
|
+
var LearnCardRefreshAuthorizationValidator = external_exports.object({ type: external_exports.literal("LearnCardDIDAuth") }).catchall(external_exports.any());
|
|
16297
|
+
var ManagedCredentialRefreshServiceValidator = external_exports.object({
|
|
16298
|
+
id: external_exports.string().min(1),
|
|
16299
|
+
type: external_exports.literal("LearnCardCredentialRefresh2026"),
|
|
16300
|
+
authorization: LearnCardRefreshAuthorizationValidator.optional()
|
|
16301
|
+
}).catchall(external_exports.any());
|
|
16302
|
+
var StandardCredentialRefreshServiceValidator = external_exports.object({
|
|
16303
|
+
id: external_exports.string().min(1),
|
|
16304
|
+
type: external_exports.literal("1EdTechCredentialRefresh")
|
|
16305
|
+
}).catchall(external_exports.any());
|
|
16306
|
+
var SupportedCredentialRefreshServiceValidator = external_exports.union([
|
|
16307
|
+
ManagedCredentialRefreshServiceValidator,
|
|
16308
|
+
StandardCredentialRefreshServiceValidator
|
|
16309
|
+
]);
|
|
16310
|
+
var AllocateCredentialRefreshInputValidator = external_exports.object({
|
|
16311
|
+
holder: external_exports.object({
|
|
16312
|
+
profileId: external_exports.string().optional(),
|
|
16313
|
+
did: external_exports.string().min(1)
|
|
16314
|
+
}),
|
|
16315
|
+
credentialId: external_exports.string().min(1)
|
|
16316
|
+
});
|
|
16317
|
+
var AllocateCredentialRefreshResultValidator = external_exports.object({
|
|
16318
|
+
refreshId: external_exports.string().min(1),
|
|
16319
|
+
refreshService: ManagedCredentialRefreshServiceValidator.extend({
|
|
16320
|
+
authorization: LearnCardRefreshAuthorizationValidator
|
|
16321
|
+
})
|
|
16322
|
+
});
|
|
16323
|
+
var ManagedCredentialRefreshReceiptValidator = external_exports.object({
|
|
16324
|
+
refreshId: external_exports.string().min(1),
|
|
16325
|
+
refreshService: ManagedCredentialRefreshServiceValidator,
|
|
16326
|
+
credentialId: external_exports.string().min(1),
|
|
16327
|
+
issuerDid: external_exports.string().min(1),
|
|
16328
|
+
holderDid: external_exports.string().min(1),
|
|
16329
|
+
credentialStatus: CredentialStatusValidator.or(
|
|
16330
|
+
CredentialStatusValidator.array()
|
|
16331
|
+
).optional()
|
|
16332
|
+
}).strip();
|
|
16333
|
+
var InboxCredentialRefreshReceiptValidator = ManagedCredentialRefreshReceiptValidator.omit(
|
|
16334
|
+
{ holderDid: true }
|
|
16335
|
+
).extend({ holderDid: external_exports.string().min(1).optional() });
|
|
16336
|
+
var CredentialRefreshSigningModeValidator = external_exports.enum(["issuer-signed", "signing-authority"]);
|
|
16337
|
+
var PublishCredentialRefreshBaseFields = {
|
|
16338
|
+
refreshId: external_exports.string().min(1),
|
|
16339
|
+
notifyHolder: external_exports.boolean().optional(),
|
|
16340
|
+
updateSummary: external_exports.string().optional(),
|
|
16341
|
+
idempotencyKey: external_exports.string().optional()
|
|
16342
|
+
};
|
|
16343
|
+
var PublishIssuerSignedRefreshValidator = external_exports.object({
|
|
16344
|
+
...PublishCredentialRefreshBaseFields,
|
|
16345
|
+
mode: external_exports.literal("issuer-signed"),
|
|
16346
|
+
signedCredential: VCValidator
|
|
16347
|
+
});
|
|
16348
|
+
var PublishSigningAuthorityRefreshValidator = external_exports.object({
|
|
16349
|
+
...PublishCredentialRefreshBaseFields,
|
|
16350
|
+
mode: external_exports.literal("signing-authority"),
|
|
16351
|
+
credential: UnsignedVCValidator,
|
|
16352
|
+
signingAuthority: external_exports.object({
|
|
16353
|
+
type: external_exports.string().min(1)
|
|
16354
|
+
}).catchall(external_exports.any())
|
|
16355
|
+
});
|
|
16356
|
+
var PublishCredentialRefreshInputValidator = external_exports.object({
|
|
16357
|
+
...PublishCredentialRefreshBaseFields,
|
|
16358
|
+
mode: CredentialRefreshSigningModeValidator,
|
|
16359
|
+
signedCredential: VCValidator.optional(),
|
|
16360
|
+
credential: UnsignedVCValidator.optional(),
|
|
16361
|
+
signingAuthority: external_exports.object({
|
|
16362
|
+
type: external_exports.string().min(1)
|
|
16363
|
+
}).catchall(external_exports.any()).optional()
|
|
16364
|
+
}).superRefine((input, ctx) => {
|
|
16365
|
+
if (input.mode === "issuer-signed" && !input.signedCredential) {
|
|
16366
|
+
ctx.addIssue({
|
|
16367
|
+
code: "custom",
|
|
16368
|
+
path: ["signedCredential"],
|
|
16369
|
+
message: "signedCredential is required for issuer-signed publication"
|
|
16370
|
+
});
|
|
16371
|
+
}
|
|
16372
|
+
if (input.mode === "issuer-signed" && (input.credential !== void 0 || input.signingAuthority !== void 0)) {
|
|
16373
|
+
ctx.addIssue({
|
|
16374
|
+
code: "custom",
|
|
16375
|
+
path: ["mode"],
|
|
16376
|
+
message: "issuer-signed publication cannot include signing-authority fields"
|
|
16377
|
+
});
|
|
16378
|
+
}
|
|
16379
|
+
if (input.mode === "signing-authority") {
|
|
16380
|
+
if (input.signedCredential !== void 0) {
|
|
16381
|
+
ctx.addIssue({
|
|
16382
|
+
code: "custom",
|
|
16383
|
+
path: ["signedCredential"],
|
|
16384
|
+
message: "signing-authority publication cannot include signedCredential"
|
|
16385
|
+
});
|
|
16386
|
+
}
|
|
16387
|
+
if (!input.credential) {
|
|
16388
|
+
ctx.addIssue({
|
|
16389
|
+
code: "custom",
|
|
16390
|
+
path: ["credential"],
|
|
16391
|
+
message: "credential is required for signing-authority publication"
|
|
16392
|
+
});
|
|
16393
|
+
}
|
|
16394
|
+
if (!input.signingAuthority) {
|
|
16395
|
+
ctx.addIssue({
|
|
16396
|
+
code: "custom",
|
|
16397
|
+
path: ["signingAuthority"],
|
|
16398
|
+
message: "signingAuthority is required for signing-authority publication"
|
|
16399
|
+
});
|
|
16400
|
+
}
|
|
16401
|
+
}
|
|
16402
|
+
});
|
|
16403
|
+
var PublishCredentialRefreshNotificationValidator = external_exports.enum([
|
|
16404
|
+
"queued",
|
|
16405
|
+
"suppressed",
|
|
16406
|
+
"not-applicable",
|
|
16407
|
+
/** Publication succeeded, but the post-commit notification enqueue must be retried. */
|
|
16408
|
+
"delivery-failed"
|
|
16409
|
+
]);
|
|
16410
|
+
var PublishCredentialRefreshResultValidator = external_exports.object({
|
|
16411
|
+
refreshId: external_exports.string().min(1),
|
|
16412
|
+
version: external_exports.number().int().positive(),
|
|
16413
|
+
publishedAt: external_exports.string().min(1),
|
|
16414
|
+
notification: PublishCredentialRefreshNotificationValidator
|
|
16415
|
+
});
|
|
16416
|
+
var CredentialRefreshVersionMetadataValidator = external_exports.object({
|
|
16417
|
+
version: external_exports.number().int().positive(),
|
|
16418
|
+
publishedAt: external_exports.string().min(1),
|
|
16419
|
+
effectiveAt: external_exports.string().optional(),
|
|
16420
|
+
etag: external_exports.string().optional(),
|
|
16421
|
+
signingMode: CredentialRefreshSigningModeValidator.optional(),
|
|
16422
|
+
updateSummary: external_exports.string().optional()
|
|
16423
|
+
});
|
|
16424
|
+
var GetCredentialRefreshHistoryInputValidator = external_exports.object({
|
|
16425
|
+
refreshId: external_exports.string().min(1),
|
|
16426
|
+
cursor: external_exports.string().optional(),
|
|
16427
|
+
limit: external_exports.number().int().positive().optional()
|
|
16428
|
+
});
|
|
16429
|
+
var GetCredentialRefreshHistoryResultValidator = external_exports.object({
|
|
16430
|
+
records: CredentialRefreshVersionMetadataValidator.array(),
|
|
16431
|
+
hasMore: external_exports.boolean(),
|
|
16432
|
+
cursor: external_exports.string().optional()
|
|
16433
|
+
});
|
|
16434
|
+
var CredentialRefreshChallengeValidator = external_exports.object({
|
|
16435
|
+
challenge: external_exports.string().min(1),
|
|
16436
|
+
expiresAt: external_exports.string().min(1),
|
|
16437
|
+
domain: external_exports.string().optional(),
|
|
16438
|
+
scheme: external_exports.literal("LearnCardDIDAuth").optional()
|
|
16439
|
+
});
|
|
16440
|
+
var PublicCredentialRefreshEnvelopeValidator = external_exports.object({
|
|
16441
|
+
format: external_exports.literal("vc"),
|
|
16442
|
+
credential: VCValidator,
|
|
16443
|
+
etag: external_exports.string().optional()
|
|
16444
|
+
});
|
|
16445
|
+
var JweCredentialRefreshEnvelopeValidator = external_exports.object({
|
|
16446
|
+
format: external_exports.literal("jwe"),
|
|
16447
|
+
jwe: JWEValidator,
|
|
16448
|
+
etag: external_exports.string().optional(),
|
|
16449
|
+
/** Present on LearnCard-managed endpoints; optional for interoperable JWE services. */
|
|
16450
|
+
version: external_exports.number().int().positive().optional()
|
|
16451
|
+
});
|
|
16452
|
+
var CredentialRefreshResponseEnvelopeValidator = external_exports.discriminatedUnion("format", [
|
|
16453
|
+
PublicCredentialRefreshEnvelopeValidator,
|
|
16454
|
+
JweCredentialRefreshEnvelopeValidator
|
|
16455
|
+
]);
|
|
16456
|
+
var CredentialRefreshFailureCodeValidator = external_exports.enum([
|
|
16457
|
+
"UNAVAILABLE",
|
|
16458
|
+
"TIMEOUT",
|
|
16459
|
+
"UNSUPPORTED_SERVICE",
|
|
16460
|
+
"UNAUTHORIZED",
|
|
16461
|
+
"MALFORMED_RESPONSE",
|
|
16462
|
+
"INVALID_PROOF",
|
|
16463
|
+
"ISSUER_MISMATCH",
|
|
16464
|
+
"ID_MISMATCH",
|
|
16465
|
+
"ROLLBACK",
|
|
16466
|
+
"REVOKED",
|
|
16467
|
+
"UNSAFE_ENDPOINT"
|
|
16468
|
+
]);
|
|
16469
|
+
var CredentialRefreshUpdatedResultValidator = external_exports.object({
|
|
16470
|
+
status: external_exports.literal("updated"),
|
|
16471
|
+
credential: VCValidator,
|
|
16472
|
+
etag: external_exports.string().optional(),
|
|
16473
|
+
managedVersion: external_exports.number().int().positive().optional()
|
|
16474
|
+
});
|
|
16475
|
+
var CredentialRefreshUnchangedResultValidator = external_exports.object({
|
|
16476
|
+
status: external_exports.literal("unchanged"),
|
|
16477
|
+
checkedAt: external_exports.string().min(1),
|
|
16478
|
+
etag: external_exports.string().optional()
|
|
16479
|
+
});
|
|
16480
|
+
var CredentialRefreshUnsupportedResultValidator = external_exports.object({
|
|
16481
|
+
status: external_exports.literal("unsupported")
|
|
16482
|
+
});
|
|
16483
|
+
var CredentialRefreshFailedResultValidator = external_exports.object({
|
|
16484
|
+
status: external_exports.literal("failed"),
|
|
16485
|
+
code: CredentialRefreshFailureCodeValidator,
|
|
16486
|
+
retryable: external_exports.boolean()
|
|
16487
|
+
});
|
|
16488
|
+
var CredentialRefreshResultValidator = external_exports.discriminatedUnion("status", [
|
|
16489
|
+
CredentialRefreshUpdatedResultValidator,
|
|
16490
|
+
CredentialRefreshUnchangedResultValidator,
|
|
16491
|
+
CredentialRefreshUnsupportedResultValidator,
|
|
16492
|
+
CredentialRefreshFailedResultValidator
|
|
16493
|
+
]);
|
|
16224
16494
|
var LCNProfileDisplayValidator = external_exports.object({
|
|
16225
16495
|
backgroundColor: external_exports.string().optional(),
|
|
16226
16496
|
backgroundImage: external_exports.string().optional(),
|
|
@@ -16556,7 +16826,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16556
16826
|
"Options for email/phone recipients (Universal Inbox)"
|
|
16557
16827
|
),
|
|
16558
16828
|
templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
16559
|
-
integrationId: external_exports.string().optional().describe("Integration ID for activity tracking")
|
|
16829
|
+
integrationId: external_exports.string().optional().describe("Integration ID for activity tracking"),
|
|
16830
|
+
refresh: external_exports.boolean().optional().describe(
|
|
16831
|
+
"Request managed credential refresh for this send. Profile/DID recipients use immediate issuance; email/phone recipients use deferred Universal Inbox signing and bind the holder at claim."
|
|
16832
|
+
),
|
|
16833
|
+
idempotencyKey: external_exports.string().min(1).max(200).optional().describe(
|
|
16834
|
+
"Caller-chosen key that makes a managed refresh send (refresh: true) safe to retry as a whole: retries with the same key reuse the same boost, refresh allocation and result. Reusing a key for a different request is rejected. With signedCredential, requires prior tRPC prepareRefreshableSend; direct REST callers omit the key and retry the exact signed credential and templateUri."
|
|
16835
|
+
)
|
|
16560
16836
|
}).refine((data) => data.templateUri || data.template || data.signedCredential, {
|
|
16561
16837
|
message: "Either templateUri, template, or signedCredential must be provided.",
|
|
16562
16838
|
path: ["templateUri"]
|
|
@@ -16571,8 +16847,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16571
16847
|
message: "guardianEmail must differ from recipient (self-approval not allowed)",
|
|
16572
16848
|
path: ["options", "guardianEmail"]
|
|
16573
16849
|
}
|
|
16574
|
-
)
|
|
16850
|
+
).refine((data) => !data.idempotencyKey || data.refresh === true, {
|
|
16851
|
+
message: "idempotencyKey is only supported with refresh: true.",
|
|
16852
|
+
path: ["idempotencyKey"]
|
|
16853
|
+
});
|
|
16575
16854
|
var SendInboxResponseValidator = external_exports.object({
|
|
16855
|
+
refresh: InboxCredentialRefreshReceiptValidator.optional(),
|
|
16576
16856
|
issuanceId: external_exports.string(),
|
|
16577
16857
|
status: external_exports.enum(["PENDING", "ISSUED", "EXPIRED", "DELIVERED", "CLAIMED"]),
|
|
16578
16858
|
claimUrl: external_exports.string().url().optional().describe("Present when suppressDelivery=true"),
|
|
@@ -16587,8 +16867,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16587
16867
|
activityId: external_exports.string().describe("Links to the activity lifecycle for this issuance"),
|
|
16588
16868
|
inbox: SendInboxResponseValidator.optional().describe(
|
|
16589
16869
|
"Present when sent via email/phone (Universal Inbox)"
|
|
16870
|
+
),
|
|
16871
|
+
refresh: ManagedCredentialRefreshReceiptValidator.optional().describe(
|
|
16872
|
+
"Present when managed refresh was requested: issuance metadata the issuer keeps to publish future updates"
|
|
16590
16873
|
)
|
|
16591
16874
|
});
|
|
16875
|
+
var PrepareRefreshableSendInputValidator = external_exports.object({
|
|
16876
|
+
recipient: external_exports.string(),
|
|
16877
|
+
templateUri: external_exports.string().optional(),
|
|
16878
|
+
template: SendBoostTemplateValidator.optional(),
|
|
16879
|
+
contractUri: external_exports.string().optional(),
|
|
16880
|
+
templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
16881
|
+
integrationId: external_exports.string().optional(),
|
|
16882
|
+
/** Credential ID to allocate for; generated server-side when omitted. */
|
|
16883
|
+
credentialId: external_exports.string().min(1).optional(),
|
|
16884
|
+
idempotencyKey: external_exports.string().min(1).max(200).optional()
|
|
16885
|
+
}).refine((data) => Boolean(data.templateUri) !== Boolean(data.template), {
|
|
16886
|
+
message: "Provide exactly one of templateUri or template."
|
|
16887
|
+
});
|
|
16888
|
+
var PrepareRefreshableSendResultValidator = external_exports.object({
|
|
16889
|
+
boostUri: external_exports.string(),
|
|
16890
|
+
credentialId: external_exports.string(),
|
|
16891
|
+
refreshId: external_exports.string(),
|
|
16892
|
+
refreshService: ManagedCredentialRefreshServiceValidator,
|
|
16893
|
+
/** The DID to use as credentialSubject.id (recipient DID, or the profile's did:web). */
|
|
16894
|
+
holderDid: external_exports.string(),
|
|
16895
|
+
/** Present when this idempotencyKey already completed: return it without signing. */
|
|
16896
|
+
completed: SendBoostResponseValidator.optional()
|
|
16897
|
+
});
|
|
16592
16898
|
var SendInputValidator = external_exports.discriminatedUnion("type", [SendBoostInputValidator]);
|
|
16593
16899
|
var SendResponseValidator = external_exports.discriminatedUnion("type", [SendBoostResponseValidator]);
|
|
16594
16900
|
var ConsentFlowTermsStatusValidator = external_exports.enum(["live", "stale", "withdrawn"]);
|
|
@@ -16907,6 +17213,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16907
17213
|
webhookUrl: external_exports.string().optional()
|
|
16908
17214
|
});
|
|
16909
17215
|
var AUTH_GRANT_AUDIENCE_DOMAIN_PREFIX = "auth-grant:";
|
|
17216
|
+
var ACT_AS_HEADER = "X-LearnCard-Act-As";
|
|
16910
17217
|
var AuthGrantValidator = external_exports.object({
|
|
16911
17218
|
id: external_exports.string(),
|
|
16912
17219
|
name: external_exports.string(),
|
|
@@ -16919,6 +17226,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16919
17226
|
}, "error")
|
|
16920
17227
|
}),
|
|
16921
17228
|
scope: external_exports.string(),
|
|
17229
|
+
actAs: external_exports.string().optional(),
|
|
16922
17230
|
createdAt: external_exports.iso.datetime({ error: "createdAt must be a valid ISO 8601 datetime string" }),
|
|
16923
17231
|
expiresAt: external_exports.iso.datetime({ error: "expiresAt must be a valid ISO 8601 datetime string" }).nullish().optional()
|
|
16924
17232
|
});
|
|
@@ -16990,6 +17298,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16990
17298
|
sessionJwt: external_exports.string()
|
|
16991
17299
|
});
|
|
16992
17300
|
var InboxCredentialValidator = external_exports.object({
|
|
17301
|
+
refresh: InboxCredentialRefreshReceiptValidator.optional(),
|
|
17302
|
+
refreshId: external_exports.string().optional(),
|
|
16993
17303
|
id: external_exports.string(),
|
|
16994
17304
|
credential: external_exports.string().optional(),
|
|
16995
17305
|
isSigned: external_exports.boolean(),
|
|
@@ -17044,12 +17354,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17044
17354
|
templateUri: external_exports.string().optional().describe(
|
|
17045
17355
|
"URI of a boost template to use for issuance. The boost credential will be resolved and used. Mutually exclusive with credential field."
|
|
17046
17356
|
),
|
|
17357
|
+
refresh: external_exports.boolean().optional().describe(
|
|
17358
|
+
"Allocate managed refresh before signing. Requires unsigned content and a registered signing authority; binds the holder on claim."
|
|
17359
|
+
),
|
|
17360
|
+
idempotencyKey: external_exports.string().min(1).max(200).optional(),
|
|
17047
17361
|
// === OPTIONAL FEATURES ===
|
|
17048
17362
|
// Add major, distinct features at the top level.
|
|
17049
17363
|
//consentRequest: ConsentRequestValidator.optional(),
|
|
17050
17364
|
// === PROCESS CONFIGURATION (Optional) ===
|
|
17051
17365
|
// HOW should this issuance be handled?
|
|
17052
17366
|
configuration: external_exports.object({
|
|
17367
|
+
guardianEmail: external_exports.string().email().optional().describe(
|
|
17368
|
+
"Require approval from this guardian before the recipient can claim. Must differ from the recipient email."
|
|
17369
|
+
),
|
|
17053
17370
|
signingAuthority: IssueInboxSigningAuthorityValidator.optional().describe(
|
|
17054
17371
|
"The signing authority to use for the credential. If not provided, the users default signing authority will be used if the credential is not signed."
|
|
17055
17372
|
),
|
|
@@ -17103,17 +17420,130 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17103
17420
|
}).optional().describe(
|
|
17104
17421
|
"Configuration for the credential issuance. If not provided, the default configuration will be used."
|
|
17105
17422
|
)
|
|
17423
|
+
}).refine((data) => !data.idempotencyKey || data.refresh === true, {
|
|
17424
|
+
message: "idempotencyKey requires refresh: true."
|
|
17106
17425
|
}).refine((data) => data.credential || data.templateUri, {
|
|
17107
17426
|
message: "Either credential or templateUri must be provided.",
|
|
17108
17427
|
path: ["credential"]
|
|
17109
|
-
})
|
|
17428
|
+
}).refine(
|
|
17429
|
+
(data) => !data.configuration?.guardianEmail || data.recipient.type !== "email" || data.configuration.guardianEmail.toLowerCase() !== data.recipient.value.toLowerCase(),
|
|
17430
|
+
{
|
|
17431
|
+
message: "guardianEmail must differ from recipient (self-approval not allowed)",
|
|
17432
|
+
path: ["configuration", "guardianEmail"]
|
|
17433
|
+
}
|
|
17434
|
+
);
|
|
17110
17435
|
var IssueInboxCredentialResponseValidator = external_exports.object({
|
|
17436
|
+
refresh: InboxCredentialRefreshReceiptValidator.optional(),
|
|
17111
17437
|
issuanceId: external_exports.string(),
|
|
17112
17438
|
status: LCNInboxStatusEnumValidator,
|
|
17113
17439
|
recipient: ContactMethodQueryValidator,
|
|
17114
17440
|
claimUrl: external_exports.string().url().optional(),
|
|
17115
17441
|
recipientDid: external_exports.string().optional()
|
|
17116
17442
|
});
|
|
17443
|
+
var InboxBatchConfigurationValidator = IssueInboxCredentialValidator.shape.configuration.unwrap().extend({
|
|
17444
|
+
refresh: IssueInboxCredentialValidator.shape.refresh.describe(
|
|
17445
|
+
"Enable managed refresh by default. An item configuration.refresh overrides this value, including false."
|
|
17446
|
+
),
|
|
17447
|
+
delivery: IssueInboxCredentialValidator.shape.configuration.unwrap().shape.delivery.unwrap().extend({ suppress: external_exports.boolean().optional() }).optional()
|
|
17448
|
+
});
|
|
17449
|
+
var InboxBatchItemConfigurationValidator = InboxBatchConfigurationValidator.extend({
|
|
17450
|
+
guardianEmail: InboxBatchConfigurationValidator.shape.guardianEmail.nullable().describe(
|
|
17451
|
+
"Require guardian approval, or set null to clear a batch-level guardianEmail for this item."
|
|
17452
|
+
)
|
|
17453
|
+
});
|
|
17454
|
+
var IssueInboxCredentialBatchItemValidator = external_exports.object({
|
|
17455
|
+
...IssueInboxCredentialValidator.shape,
|
|
17456
|
+
configuration: InboxBatchItemConfigurationValidator.optional(),
|
|
17457
|
+
idempotencyKey: external_exports.string().max(256).optional()
|
|
17458
|
+
}).refine((data) => data.credential || data.templateUri, {
|
|
17459
|
+
message: "Either credential or templateUri must be provided.",
|
|
17460
|
+
path: ["credential"]
|
|
17461
|
+
}).describe(
|
|
17462
|
+
"One issuance: provide credential or templateUri. Invalid input is rejected at submission with its item index."
|
|
17463
|
+
);
|
|
17464
|
+
var IssueInboxCredentialBatchValidator = external_exports.object({
|
|
17465
|
+
requestId: external_exports.string().min(1).max(256).optional(),
|
|
17466
|
+
items: external_exports.array(IssueInboxCredentialBatchItemValidator).min(1).max(100),
|
|
17467
|
+
configuration: InboxBatchConfigurationValidator.optional()
|
|
17468
|
+
}).superRefine((batch, ctx) => {
|
|
17469
|
+
batch.items.forEach((item, index) => {
|
|
17470
|
+
const itemGuardian = item.configuration?.guardianEmail;
|
|
17471
|
+
const guardian = itemGuardian === null ? void 0 : itemGuardian ?? batch.configuration?.guardianEmail;
|
|
17472
|
+
if (guardian && item.recipient.type === "email" && guardian.toLowerCase() === item.recipient.value.toLowerCase()) {
|
|
17473
|
+
ctx.addIssue({
|
|
17474
|
+
code: "custom",
|
|
17475
|
+
path: ["items", index, "configuration", "guardianEmail"],
|
|
17476
|
+
message: "guardianEmail must differ from recipient (self-approval not allowed)"
|
|
17477
|
+
});
|
|
17478
|
+
}
|
|
17479
|
+
});
|
|
17480
|
+
});
|
|
17481
|
+
var InboxBatchErrorReasonValidator = external_exports.enum([
|
|
17482
|
+
"DUPLICATE_KEY",
|
|
17483
|
+
"IDEMPOTENCY_MISMATCH",
|
|
17484
|
+
"IN_PROGRESS",
|
|
17485
|
+
"UNCONFIRMED"
|
|
17486
|
+
]);
|
|
17487
|
+
var IssueInboxCredentialBatchItemResultValidator = external_exports.discriminatedUnion("success", [
|
|
17488
|
+
IssueInboxCredentialResponseValidator.extend({
|
|
17489
|
+
success: external_exports.literal(true),
|
|
17490
|
+
index: external_exports.number().int().nonnegative(),
|
|
17491
|
+
deduplicated: external_exports.boolean().optional(),
|
|
17492
|
+
guardianStatus: GuardianStatusValidator.optional(),
|
|
17493
|
+
idempotencyKey: external_exports.string().optional()
|
|
17494
|
+
}),
|
|
17495
|
+
external_exports.object({
|
|
17496
|
+
success: external_exports.literal(false),
|
|
17497
|
+
index: external_exports.number().int().nonnegative(),
|
|
17498
|
+
idempotencyKey: external_exports.string().optional(),
|
|
17499
|
+
recipient: ContactMethodQueryValidator.optional(),
|
|
17500
|
+
error: external_exports.object({
|
|
17501
|
+
code: external_exports.string(),
|
|
17502
|
+
message: external_exports.string(),
|
|
17503
|
+
reason: InboxBatchErrorReasonValidator.optional()
|
|
17504
|
+
}),
|
|
17505
|
+
issuanceId: external_exports.string().optional().describe(
|
|
17506
|
+
"Present when issuance completed but replay storage could not be confirmed. Reconcile this issuance; do not issue again with a new key."
|
|
17507
|
+
),
|
|
17508
|
+
claimUrl: external_exports.string().url().optional().describe(
|
|
17509
|
+
"Claim URL of the completed issuance, if available, when replay storage could not be confirmed."
|
|
17510
|
+
)
|
|
17511
|
+
})
|
|
17512
|
+
]);
|
|
17513
|
+
var IssueInboxCredentialBatchResponseValidator = external_exports.object({
|
|
17514
|
+
results: external_exports.array(IssueInboxCredentialBatchItemResultValidator),
|
|
17515
|
+
summary: external_exports.object({
|
|
17516
|
+
total: external_exports.number(),
|
|
17517
|
+
succeeded: external_exports.number(),
|
|
17518
|
+
failed: external_exports.number(),
|
|
17519
|
+
deduplicated: external_exports.number()
|
|
17520
|
+
})
|
|
17521
|
+
});
|
|
17522
|
+
var InboxBatchReceiptValidator = external_exports.object({
|
|
17523
|
+
batchId: external_exports.string(),
|
|
17524
|
+
status: external_exports.enum(["QUEUED", "PROCESSING", "COMPLETED", "NEEDS_RECONCILIATION"]),
|
|
17525
|
+
createdAt: external_exports.string()
|
|
17526
|
+
});
|
|
17527
|
+
var InboxBatchStatusValidator = external_exports.object({
|
|
17528
|
+
batchId: external_exports.string(),
|
|
17529
|
+
createdAt: external_exports.string(),
|
|
17530
|
+
done: external_exports.boolean().describe(
|
|
17531
|
+
"True when no queued or processing items remain, including unconfirmed outcomes."
|
|
17532
|
+
),
|
|
17533
|
+
status: external_exports.enum(["QUEUED", "PROCESSING", "COMPLETED", "NEEDS_RECONCILIATION"]),
|
|
17534
|
+
items: external_exports.array(
|
|
17535
|
+
external_exports.object({
|
|
17536
|
+
index: external_exports.number().int().nonnegative(),
|
|
17537
|
+
state: external_exports.enum(["QUEUED", "PROCESSING", "COMPLETED", "NEEDS_RECONCILIATION"]),
|
|
17538
|
+
result: IssueInboxCredentialBatchItemResultValidator.optional()
|
|
17539
|
+
})
|
|
17540
|
+
),
|
|
17541
|
+
summary: IssueInboxCredentialBatchResponseValidator.shape.summary.extend({
|
|
17542
|
+
completed: external_exports.number(),
|
|
17543
|
+
pending: external_exports.number(),
|
|
17544
|
+
unconfirmed: external_exports.number()
|
|
17545
|
+
})
|
|
17546
|
+
});
|
|
17117
17547
|
var CredentialNameRefValidator = external_exports.object({ name: external_exports.string() }).passthrough();
|
|
17118
17548
|
var ClaimInboxCredentialValidator = external_exports.object({
|
|
17119
17549
|
credential: VCValidator.or(VPValidator).or(UnsignedVCValidator).or(CredentialNameRefValidator).describe("The credential to issue, or a { name } reference to resolve a boost template."),
|
|
@@ -17599,6 +18029,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17599
18029
|
eventType: CredentialActivityEventTypeValidator,
|
|
17600
18030
|
timestamp: external_exports.string(),
|
|
17601
18031
|
actorProfileId: external_exports.string().optional(),
|
|
18032
|
+
onBehalfOf: external_exports.string().optional(),
|
|
17602
18033
|
recipientType: CredentialActivityRecipientTypeValidator,
|
|
17603
18034
|
recipientIdentifier: external_exports.string(),
|
|
17604
18035
|
boostUri: external_exports.string().optional(),
|
|
@@ -17645,6 +18076,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17645
18076
|
__name2(this, "AuthSessionError");
|
|
17646
18077
|
}
|
|
17647
18078
|
};
|
|
18079
|
+
var ESCROW_PIN_LOCKED_MESSAGE2 = "Too many incorrect PIN attempts. You can still recover by waiting.";
|
|
18080
|
+
var ESCROW_PIN_UNAVAILABLE_MESSAGE2 = "PIN recovery is not available for this account.";
|
|
18081
|
+
var ESCROW_PIN_MISMATCH_PATTERN2 = /^Incorrect PIN\. (\d+) attempts left\.$/;
|
|
18082
|
+
var escrowPinMismatchMessage = /* @__PURE__ */ __name2((attemptsRemaining) => `Incorrect PIN. ${attemptsRemaining} attempts left.`, "escrowPinMismatchMessage");
|
|
18083
|
+
var EscrowPinStatusValidator = external_exports.object({
|
|
18084
|
+
state: external_exports.enum(["none", "enabled", "locked", "stale"]),
|
|
18085
|
+
enabled: external_exports.boolean(),
|
|
18086
|
+
attemptsRemaining: external_exports.number().int().nonnegative(),
|
|
18087
|
+
salt: external_exports.string().optional()
|
|
18088
|
+
});
|
|
17648
18089
|
var BITSTRING_STATUS_PURPOSES = ["revocation", "suspension"];
|
|
17649
18090
|
var DEFAULT_BITSTRING_STATUS_LIST_SIZE = 131072;
|
|
17650
18091
|
var BitstringStatusPurposeValidator = external_exports.enum(BITSTRING_STATUS_PURPOSES);
|
|
@@ -17759,191 +18200,458 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
17759
18200
|
const result = inAppMessagesFlagValidator.safeParse(raw);
|
|
17760
18201
|
return result.success ? result.data : EMPTY_IN_APP_MESSAGES_FLAG;
|
|
17761
18202
|
}, "parseInAppMessagesFlag");
|
|
17762
|
-
var
|
|
17763
|
-
var
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17768
|
-
var
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
var
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
var
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
|
|
17806
|
-
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
}
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
18203
|
+
var SHARE_LINK_ID_BYTES = 16;
|
|
18204
|
+
var SHARE_CONTENT_KEY_BYTES = 32;
|
|
18205
|
+
var SHARE_IV_BYTES = 12;
|
|
18206
|
+
var SHARE_TAG_BYTES = 16;
|
|
18207
|
+
var MAX_SHARE_CIPHERTEXT_BYTES = 512 * 1024;
|
|
18208
|
+
var MAX_SHARE_RECOVERY_JWE_BYTES = 64 * 1024;
|
|
18209
|
+
var MAX_SELECTED_CREDENTIALS = 50;
|
|
18210
|
+
var MAX_ENDORSEMENTS = 200;
|
|
18211
|
+
var MAX_VP_MEMBERS = MAX_SELECTED_CREDENTIALS + MAX_ENDORSEMENTS;
|
|
18212
|
+
var MAX_SHARE_SOURCE_URI_CHARS = 2048;
|
|
18213
|
+
var MAX_SHARE_LINK_REQUEST_BYTES = 1024 * 1024;
|
|
18214
|
+
var SHARE_LINK_PROTOCOL = "lc-share/v1";
|
|
18215
|
+
var SHARE_RECOVERY_PROTOCOL = "lc-share-recovery/v1";
|
|
18216
|
+
var SHARE_AAD_PREFIX = "lc-share:v1";
|
|
18217
|
+
var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
18218
|
+
var BASE64URL_LOOKUP = (() => {
|
|
18219
|
+
const table = new Int16Array(128).fill(-1);
|
|
18220
|
+
for (let index = 0; index < BASE64URL_ALPHABET.length; index += 1) {
|
|
18221
|
+
table[BASE64URL_ALPHABET.charCodeAt(index)] = index;
|
|
18222
|
+
}
|
|
18223
|
+
return table;
|
|
18224
|
+
})();
|
|
18225
|
+
var encodeBase64Url = /* @__PURE__ */ __name2((bytes) => {
|
|
18226
|
+
let output = "";
|
|
18227
|
+
for (let index = 0; index < bytes.length; index += 3) {
|
|
18228
|
+
const first = bytes[index];
|
|
18229
|
+
const second = bytes[index + 1];
|
|
18230
|
+
const third = bytes[index + 2];
|
|
18231
|
+
output += BASE64URL_ALPHABET[first >> 2];
|
|
18232
|
+
output += BASE64URL_ALPHABET[(first & 3) << 4 | (second ?? 0) >> 4];
|
|
18233
|
+
if (second === void 0) break;
|
|
18234
|
+
output += BASE64URL_ALPHABET[(second & 15) << 2 | (third ?? 0) >> 6];
|
|
18235
|
+
if (third === void 0) break;
|
|
18236
|
+
output += BASE64URL_ALPHABET[third & 63];
|
|
18237
|
+
}
|
|
18238
|
+
return output;
|
|
18239
|
+
}, "encodeBase64Url");
|
|
18240
|
+
var decodeBase64Url = /* @__PURE__ */ __name2((value) => {
|
|
18241
|
+
if (typeof value !== "string") return null;
|
|
18242
|
+
const output = new Uint8Array(Math.floor(value.length * 6 / 8));
|
|
18243
|
+
let bits = 0;
|
|
18244
|
+
let bitCount = 0;
|
|
18245
|
+
let outputIndex = 0;
|
|
18246
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
18247
|
+
const code = value.charCodeAt(index);
|
|
18248
|
+
const digit = code < 128 ? BASE64URL_LOOKUP[code] : -1;
|
|
18249
|
+
if (digit < 0) return null;
|
|
18250
|
+
bits = bits << 6 | digit;
|
|
18251
|
+
bitCount += 6;
|
|
18252
|
+
if (bitCount >= 8) {
|
|
18253
|
+
bitCount -= 8;
|
|
18254
|
+
output[outputIndex] = bits >> bitCount & 255;
|
|
18255
|
+
outputIndex += 1;
|
|
18256
|
+
}
|
|
18257
|
+
}
|
|
18258
|
+
return output;
|
|
18259
|
+
}, "decodeBase64Url");
|
|
18260
|
+
var isCanonicalBase64Url = /* @__PURE__ */ __name2((value, expectedBytes) => {
|
|
18261
|
+
if (typeof value !== "string") return false;
|
|
18262
|
+
if (expectedBytes !== void 0 && value.length !== Math.ceil(expectedBytes * 4 / 3))
|
|
18263
|
+
return false;
|
|
18264
|
+
const decoded = decodeBase64Url(value);
|
|
18265
|
+
if (decoded === null) return false;
|
|
18266
|
+
if (expectedBytes !== void 0 && decoded.length !== expectedBytes) return false;
|
|
18267
|
+
return encodeBase64Url(decoded) === value;
|
|
18268
|
+
}, "isCanonicalBase64Url");
|
|
18269
|
+
var decodedBase64UrlByteLength = /* @__PURE__ */ __name2((value) => decodeBase64Url(value)?.length ?? null, "decodedBase64UrlByteLength");
|
|
18270
|
+
var utf8ByteLength = /* @__PURE__ */ __name2((value) => new TextEncoder().encode(value).length, "utf8ByteLength");
|
|
18271
|
+
var canonicalBase64Url = /* @__PURE__ */ __name2((bytes) => external_exports.string().refine((value) => isCanonicalBase64Url(value, bytes), {
|
|
18272
|
+
message: `must be canonical base64url for exactly ${bytes} bytes`
|
|
18273
|
+
}), "canonicalBase64Url");
|
|
18274
|
+
var ShareLinkIdValidator = canonicalBase64Url(SHARE_LINK_ID_BYTES);
|
|
18275
|
+
var ShareContentKeyValidator = canonicalBase64Url(SHARE_CONTENT_KEY_BYTES);
|
|
18276
|
+
var ShareIvValidator = canonicalBase64Url(SHARE_IV_BYTES);
|
|
18277
|
+
var ShareCiphertextValidator = external_exports.string().refine(
|
|
18278
|
+
(value) => {
|
|
18279
|
+
if (value.length > Math.ceil(MAX_SHARE_CIPHERTEXT_BYTES * 4 / 3)) return false;
|
|
18280
|
+
const decoded = decodeBase64Url(value);
|
|
18281
|
+
if (decoded === null) return false;
|
|
18282
|
+
if (encodeBase64Url(decoded) !== value) return false;
|
|
18283
|
+
return decoded.length >= SHARE_TAG_BYTES && decoded.length <= MAX_SHARE_CIPHERTEXT_BYTES;
|
|
18284
|
+
},
|
|
18285
|
+
{
|
|
18286
|
+
message: `must be canonical base64url decoding to ${SHARE_TAG_BYTES}..${MAX_SHARE_CIPHERTEXT_BYTES} bytes`
|
|
17824
18287
|
}
|
|
17825
|
-
|
|
18288
|
+
);
|
|
18289
|
+
var ShareEnvelopeValidator = external_exports.object({
|
|
18290
|
+
v: external_exports.literal(1),
|
|
18291
|
+
alg: external_exports.literal("A256GCM"),
|
|
18292
|
+
iv: ShareIvValidator,
|
|
18293
|
+
ct: ShareCiphertextValidator
|
|
18294
|
+
}).strict();
|
|
18295
|
+
var safeVersion = external_exports.number().int().min(1).max(2 ** 31 - 1);
|
|
18296
|
+
var safeCredentialIndex = external_exports.number().int().min(0).max(MAX_VP_MEMBERS - 1);
|
|
18297
|
+
var ShareSelectionEntryValidator = external_exports.object({ credentialIndex: safeCredentialIndex }).strict();
|
|
18298
|
+
var ShareEndorsementEntryValidator = external_exports.object({ credentialIndex: safeCredentialIndex, targetCredentialIndex: safeCredentialIndex }).strict();
|
|
18299
|
+
var ShareSharerValidator = external_exports.object({
|
|
18300
|
+
profileId: external_exports.string().min(1).max(128),
|
|
18301
|
+
displayName: external_exports.string().min(1).max(120),
|
|
18302
|
+
avatar: external_exports.string().max(2048).optional()
|
|
18303
|
+
}).strict();
|
|
18304
|
+
var presentationShape = VPValidator.extend({
|
|
18305
|
+
verifiableCredential: VCValidator.array().min(1).max(MAX_VP_MEMBERS)
|
|
18306
|
+
});
|
|
18307
|
+
var hasBoundedPresentation = /* @__PURE__ */ __name2((value) => {
|
|
18308
|
+
let credentialCount = 0;
|
|
18309
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
18310
|
+
const visit = /* @__PURE__ */ __name2((node, depth) => {
|
|
18311
|
+
if (node === null || typeof node !== "object") return true;
|
|
18312
|
+
if (depth > 32 || ancestors.has(node)) return false;
|
|
18313
|
+
ancestors.add(node);
|
|
18314
|
+
try {
|
|
18315
|
+
for (const [key, child] of Object.entries(node)) {
|
|
18316
|
+
if (key === "verifiableCredential") {
|
|
18317
|
+
credentialCount += Array.isArray(child) ? child.length : 1;
|
|
18318
|
+
if (credentialCount > MAX_VP_MEMBERS) return false;
|
|
18319
|
+
}
|
|
18320
|
+
if (!visit(child, depth + 1)) return false;
|
|
18321
|
+
}
|
|
18322
|
+
return true;
|
|
18323
|
+
} finally {
|
|
18324
|
+
ancestors.delete(node);
|
|
18325
|
+
}
|
|
18326
|
+
}, "visit");
|
|
18327
|
+
return visit(value, 0);
|
|
18328
|
+
}, "hasBoundedPresentation");
|
|
18329
|
+
var ShareManifestPresentationValidator = external_exports.custom(
|
|
18330
|
+
(value) => hasBoundedPresentation(value) && presentationShape.safeParse(value).success,
|
|
18331
|
+
{ message: "invalid share presentation" }
|
|
18332
|
+
);
|
|
18333
|
+
var SharePayloadValidatorShape = external_exports.object({
|
|
18334
|
+
protocol: external_exports.literal(SHARE_LINK_PROTOCOL),
|
|
18335
|
+
shareId: ShareLinkIdValidator,
|
|
18336
|
+
contentVersion: safeVersion,
|
|
18337
|
+
createdAt: external_exports.iso.datetime(),
|
|
18338
|
+
sharer: ShareSharerValidator,
|
|
18339
|
+
presentation: ShareManifestPresentationValidator,
|
|
18340
|
+
selection: ShareSelectionEntryValidator.array().min(1).max(MAX_SELECTED_CREDENTIALS),
|
|
18341
|
+
endorsements: ShareEndorsementEntryValidator.array().max(MAX_ENDORSEMENTS).default([])
|
|
18342
|
+
}).strict();
|
|
18343
|
+
var classificationFailure = /* @__PURE__ */ __name2((code, message, path = []) => ({ ok: false, code, message, path }), "classificationFailure");
|
|
18344
|
+
var readSignedClaimId = /* @__PURE__ */ __name2((member) => {
|
|
18345
|
+
if (member === null || typeof member !== "object") return void 0;
|
|
18346
|
+
const record2 = member;
|
|
18347
|
+
const claim = record2.credentialSubject ?? record2.endorsement;
|
|
18348
|
+
if (claim === null || typeof claim !== "object") return void 0;
|
|
18349
|
+
const claimRecord = claim;
|
|
18350
|
+
const nested = claimRecord.endorsement;
|
|
18351
|
+
const nestedId = nested !== null && typeof nested === "object" ? nested.id : void 0;
|
|
18352
|
+
return claimRecord.id ?? nestedId;
|
|
18353
|
+
}, "readSignedClaimId");
|
|
18354
|
+
var classifyShareManifest = /* @__PURE__ */ __name2((payload) => {
|
|
18355
|
+
const members = payload.presentation?.verifiableCredential;
|
|
18356
|
+
if (!Array.isArray(members) || members.length === 0) {
|
|
18357
|
+
return classificationFailure("NO_PRESENTATION_MEMBERS", "presentation has no credentials");
|
|
18358
|
+
}
|
|
18359
|
+
if (members.length > MAX_VP_MEMBERS) {
|
|
18360
|
+
return classificationFailure(
|
|
18361
|
+
"TOO_MANY_VP_MEMBERS",
|
|
18362
|
+
`presentation has ${members.length} members, over the ${MAX_VP_MEMBERS} bound`
|
|
18363
|
+
);
|
|
18364
|
+
}
|
|
18365
|
+
const selection = payload.selection;
|
|
18366
|
+
const endorsements = payload.endorsements ?? [];
|
|
18367
|
+
if (!Array.isArray(selection) || selection.length < 1 || selection.length > MAX_SELECTED_CREDENTIALS) {
|
|
18368
|
+
return classificationFailure("BAD_SELECTION", "selection must contain 1..50 entries");
|
|
18369
|
+
}
|
|
18370
|
+
if (!Array.isArray(endorsements) || endorsements.length > MAX_ENDORSEMENTS) {
|
|
18371
|
+
return classificationFailure(
|
|
18372
|
+
"BAD_ENDORSEMENTS",
|
|
18373
|
+
"endorsements must contain at most 200 entries"
|
|
18374
|
+
);
|
|
18375
|
+
}
|
|
18376
|
+
const selectedIndices = [];
|
|
18377
|
+
const selected = /* @__PURE__ */ new Set();
|
|
18378
|
+
for (let index = 0; index < selection.length; index += 1) {
|
|
18379
|
+
const entry = selection[index];
|
|
18380
|
+
const credentialIndex = entry?.credentialIndex;
|
|
18381
|
+
if (!Number.isSafeInteger(credentialIndex) || credentialIndex < 0 || credentialIndex >= members.length) {
|
|
18382
|
+
return classificationFailure(
|
|
18383
|
+
"SELECTION_INDEX_OUT_OF_BOUNDS",
|
|
18384
|
+
`selection[${index}].credentialIndex is out of bounds`,
|
|
18385
|
+
["selection", index, "credentialIndex"]
|
|
18386
|
+
);
|
|
18387
|
+
}
|
|
18388
|
+
if (selected.has(credentialIndex)) {
|
|
18389
|
+
return classificationFailure(
|
|
18390
|
+
"SELECTION_INDEX_DUPLICATE",
|
|
18391
|
+
`selection[${index}].credentialIndex is duplicated`,
|
|
18392
|
+
["selection", index, "credentialIndex"]
|
|
18393
|
+
);
|
|
18394
|
+
}
|
|
18395
|
+
selected.add(credentialIndex);
|
|
18396
|
+
selectedIndices.push(credentialIndex);
|
|
18397
|
+
}
|
|
18398
|
+
const classified = new Set(selected);
|
|
18399
|
+
const endorsementIndices = [];
|
|
18400
|
+
for (let index = 0; index < endorsements.length; index += 1) {
|
|
18401
|
+
const entry = endorsements[index];
|
|
18402
|
+
const credentialIndex = entry?.credentialIndex;
|
|
18403
|
+
const targetIndex = entry?.targetCredentialIndex;
|
|
18404
|
+
if (!Number.isSafeInteger(credentialIndex) || credentialIndex < 0 || credentialIndex >= members.length) {
|
|
18405
|
+
return classificationFailure(
|
|
18406
|
+
"ENDORSEMENT_INDEX_OUT_OF_BOUNDS",
|
|
18407
|
+
`endorsements[${index}].credentialIndex is out of bounds`,
|
|
18408
|
+
["endorsements", index, "credentialIndex"]
|
|
18409
|
+
);
|
|
18410
|
+
}
|
|
18411
|
+
if (classified.has(credentialIndex)) {
|
|
18412
|
+
return classificationFailure(
|
|
18413
|
+
"VP_MEMBER_CLASSIFIED_TWICE",
|
|
18414
|
+
`endorsements[${index}].credentialIndex was already classified`,
|
|
18415
|
+
["endorsements", index, "credentialIndex"]
|
|
18416
|
+
);
|
|
18417
|
+
}
|
|
18418
|
+
if (!selected.has(targetIndex)) {
|
|
18419
|
+
return classificationFailure(
|
|
18420
|
+
"ENDORSEMENT_TARGET_NOT_SELECTED",
|
|
18421
|
+
`endorsements[${index}].targetCredentialIndex must reference a selected credential`,
|
|
18422
|
+
["endorsements", index, "targetCredentialIndex"]
|
|
18423
|
+
);
|
|
18424
|
+
}
|
|
18425
|
+
const referenced = readSignedClaimId(members[credentialIndex]);
|
|
18426
|
+
const target = members[targetIndex];
|
|
18427
|
+
const targetId = target !== null && typeof target === "object" ? target.id : void 0;
|
|
18428
|
+
if (typeof referenced !== "string" || typeof targetId !== "string" || referenced !== targetId) {
|
|
18429
|
+
return classificationFailure(
|
|
18430
|
+
"ENDORSEMENT_TARGET_UNBOUND",
|
|
18431
|
+
`endorsements[${index}] claim does not reference the target credential id`,
|
|
18432
|
+
["endorsements", index]
|
|
18433
|
+
);
|
|
18434
|
+
}
|
|
18435
|
+
classified.add(credentialIndex);
|
|
18436
|
+
endorsementIndices.push(credentialIndex);
|
|
18437
|
+
}
|
|
18438
|
+
if (classified.size !== members.length) {
|
|
18439
|
+
return classificationFailure(
|
|
18440
|
+
"UNCLASSIFIED_VP_MEMBER",
|
|
18441
|
+
"every presentation member must be classified exactly once",
|
|
18442
|
+
["presentation", "verifiableCredential"]
|
|
18443
|
+
);
|
|
18444
|
+
}
|
|
18445
|
+
return {
|
|
18446
|
+
ok: true,
|
|
18447
|
+
classification: { selectedIndices, endorsementIndices, memberCount: members.length }
|
|
18448
|
+
};
|
|
18449
|
+
}, "classifyShareManifest");
|
|
18450
|
+
var SharePayloadValidator = SharePayloadValidatorShape.superRefine((payload, ctx) => {
|
|
18451
|
+
const result = classifyShareManifest(payload);
|
|
18452
|
+
if (!result.ok) {
|
|
17826
18453
|
ctx.addIssue({
|
|
17827
18454
|
code: "custom",
|
|
17828
|
-
|
|
17829
|
-
|
|
18455
|
+
message: result.message,
|
|
18456
|
+
path: result.path,
|
|
18457
|
+
params: { shareLinkCode: result.code }
|
|
17830
18458
|
});
|
|
17831
18459
|
}
|
|
17832
|
-
|
|
17833
|
-
|
|
17834
|
-
|
|
17835
|
-
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
17839
|
-
|
|
17840
|
-
|
|
18460
|
+
});
|
|
18461
|
+
var ShareRecoverySelectionValidator = external_exports.object({
|
|
18462
|
+
ref: external_exports.string().min(1).max(MAX_SHARE_SOURCE_URI_CHARS),
|
|
18463
|
+
order: external_exports.number().int().min(0).max(MAX_SELECTED_CREDENTIALS - 1)
|
|
18464
|
+
}).strict();
|
|
18465
|
+
var ShareRecoveryEndorsementValidator = external_exports.object({ targetRef: external_exports.string().min(1).max(MAX_SHARE_SOURCE_URI_CHARS) }).strict();
|
|
18466
|
+
var ShareRecoveryPlaintextValidator = external_exports.object({
|
|
18467
|
+
protocol: external_exports.literal(SHARE_RECOVERY_PROTOCOL),
|
|
18468
|
+
shareId: ShareLinkIdValidator,
|
|
18469
|
+
ownerProfileId: external_exports.string().min(1).max(128),
|
|
18470
|
+
createdAt: external_exports.iso.datetime(),
|
|
18471
|
+
latest: external_exports.object({ contentVersion: safeVersion, key: ShareContentKeyValidator }).strict(),
|
|
18472
|
+
selection: ShareRecoverySelectionValidator.array().min(1).max(MAX_SELECTED_CREDENTIALS),
|
|
18473
|
+
endorsements: ShareRecoveryEndorsementValidator.array().max(MAX_ENDORSEMENTS).default([])
|
|
18474
|
+
}).strict().superRefine((recovery, ctx) => {
|
|
18475
|
+
const orders = /* @__PURE__ */ new Set();
|
|
18476
|
+
recovery.selection.forEach((entry, index) => {
|
|
18477
|
+
if (entry.order >= recovery.selection.length) {
|
|
17841
18478
|
ctx.addIssue({
|
|
17842
18479
|
code: "custom",
|
|
17843
|
-
|
|
17844
|
-
|
|
18480
|
+
message: "order must be within the selection bounds",
|
|
18481
|
+
path: ["selection", index, "order"]
|
|
17845
18482
|
});
|
|
17846
18483
|
}
|
|
17847
|
-
if (
|
|
18484
|
+
if (orders.has(entry.order)) {
|
|
17848
18485
|
ctx.addIssue({
|
|
17849
18486
|
code: "custom",
|
|
17850
|
-
|
|
17851
|
-
|
|
18487
|
+
message: "order must be unique within the selection",
|
|
18488
|
+
path: ["selection", index, "order"]
|
|
17852
18489
|
});
|
|
17853
18490
|
}
|
|
18491
|
+
orders.add(entry.order);
|
|
18492
|
+
});
|
|
18493
|
+
});
|
|
18494
|
+
var ShareOwnerRecoveryValidator = JWEValidator.refine(
|
|
18495
|
+
(value) => utf8ByteLength(JSON.stringify(value)) <= MAX_SHARE_RECOVERY_JWE_BYTES,
|
|
18496
|
+
{ message: `serialized recovery JWE must be at most ${MAX_SHARE_RECOVERY_JWE_BYTES} bytes` }
|
|
18497
|
+
);
|
|
18498
|
+
var ShareLinkStatusValidator = external_exports.enum(["pending", "active", "stopped"]);
|
|
18499
|
+
var ShareContentStateValidator = external_exports.enum(["staging", "finalized", "content_missing"]);
|
|
18500
|
+
var ShareLinkValidator = external_exports.object({
|
|
18501
|
+
id: ShareLinkIdValidator,
|
|
18502
|
+
title: external_exports.string().min(1).max(120),
|
|
18503
|
+
note: external_exports.string().max(500).optional(),
|
|
18504
|
+
selectedCount: external_exports.number().int().min(1).max(MAX_SELECTED_CREDENTIALS),
|
|
18505
|
+
version: safeVersion,
|
|
18506
|
+
contentVersion: safeVersion,
|
|
18507
|
+
status: ShareLinkStatusValidator,
|
|
18508
|
+
contentState: ShareContentStateValidator,
|
|
18509
|
+
createdAt: external_exports.iso.datetime(),
|
|
18510
|
+
updatedAt: external_exports.iso.datetime(),
|
|
18511
|
+
expiresAt: external_exports.iso.datetime().nullable(),
|
|
18512
|
+
stoppedAt: external_exports.iso.datetime().nullable(),
|
|
18513
|
+
lastViewedAt: external_exports.iso.datetime().nullable(),
|
|
18514
|
+
viewCount: external_exports.number().int().min(0).optional(),
|
|
18515
|
+
passcodeProtected: external_exports.boolean(),
|
|
18516
|
+
notifyOnView: external_exports.boolean(),
|
|
18517
|
+
minorPolicy: external_exports.object({
|
|
18518
|
+
isMinor: external_exports.boolean().nullable(),
|
|
18519
|
+
policyResolved: external_exports.boolean(),
|
|
18520
|
+
defaultExpiryDays: external_exports.union([external_exports.literal(30), external_exports.literal(365)]),
|
|
18521
|
+
viewCountingEnabled: external_exports.boolean()
|
|
18522
|
+
}),
|
|
18523
|
+
/**
|
|
18524
|
+
* Guarded application/API URL for the content. Optional because owner APIs in
|
|
18525
|
+
* D1 have no public resolve/content endpoint yet; it must never be a
|
|
18526
|
+
* LearnCloud object URL or carry a fabricated decryption key.
|
|
18527
|
+
*/
|
|
18528
|
+
contentUrl: external_exports.string().optional()
|
|
18529
|
+
});
|
|
18530
|
+
var clientRequestId = external_exports.string().uuid();
|
|
18531
|
+
var CreateShareLinkInputValidator = external_exports.object({
|
|
18532
|
+
id: ShareLinkIdValidator,
|
|
18533
|
+
clientRequestId,
|
|
18534
|
+
title: external_exports.string().min(1).max(120),
|
|
18535
|
+
note: external_exports.string().max(500).optional(),
|
|
18536
|
+
expiresAt: external_exports.iso.datetime().nullable().optional(),
|
|
18537
|
+
passcode: external_exports.string().min(8).max(64).optional(),
|
|
18538
|
+
notifyOnView: external_exports.boolean().default(false),
|
|
18539
|
+
selectedCount: external_exports.number().int().min(1).max(MAX_SELECTED_CREDENTIALS),
|
|
18540
|
+
contentVersion: external_exports.literal(1),
|
|
18541
|
+
envelope: ShareEnvelopeValidator,
|
|
18542
|
+
ownerEncryptedRecovery: ShareOwnerRecoveryValidator
|
|
18543
|
+
}).strict();
|
|
18544
|
+
var UpdateShareLinkInputValidator = external_exports.object({
|
|
18545
|
+
id: ShareLinkIdValidator,
|
|
18546
|
+
expectedVersion: safeVersion,
|
|
18547
|
+
clientRequestId,
|
|
18548
|
+
title: external_exports.string().min(1).max(120).optional(),
|
|
18549
|
+
note: external_exports.string().max(500).nullable().optional(),
|
|
18550
|
+
expiresAt: external_exports.iso.datetime().nullable().optional(),
|
|
18551
|
+
/** Omitted preserves the current passcode, null removes it, and a string replaces it. */
|
|
18552
|
+
passcode: external_exports.string().min(8).max(64).nullable().optional(),
|
|
18553
|
+
notifyOnView: external_exports.boolean().optional(),
|
|
18554
|
+
contentVersion: safeVersion.optional(),
|
|
18555
|
+
selectedCount: external_exports.number().int().min(1).max(MAX_SELECTED_CREDENTIALS).optional(),
|
|
18556
|
+
envelope: ShareEnvelopeValidator.optional(),
|
|
18557
|
+
ownerEncryptedRecovery: ShareOwnerRecoveryValidator.optional()
|
|
18558
|
+
}).strict().superRefine((value, ctx) => {
|
|
18559
|
+
const provided = [
|
|
18560
|
+
value.contentVersion,
|
|
18561
|
+
value.selectedCount,
|
|
18562
|
+
value.envelope,
|
|
18563
|
+
value.ownerEncryptedRecovery
|
|
18564
|
+
].filter((field) => field !== void 0).length;
|
|
18565
|
+
if (provided !== 0 && provided !== 4) {
|
|
18566
|
+
ctx.addIssue({
|
|
18567
|
+
code: "custom",
|
|
18568
|
+
message: "content replacement requires contentVersion, selectedCount, envelope and ownerEncryptedRecovery together"
|
|
18569
|
+
});
|
|
17854
18570
|
}
|
|
17855
18571
|
});
|
|
17856
|
-
var
|
|
17857
|
-
|
|
17858
|
-
|
|
17859
|
-
"
|
|
17860
|
-
|
|
17861
|
-
|
|
18572
|
+
var ShareLinkIdInputValidator = external_exports.object({ id: ShareLinkIdValidator }).strict();
|
|
18573
|
+
var ShareLinkOperationKeyInputValidator = external_exports.object({ id: ShareLinkIdValidator, operationId: external_exports.string().uuid() }).strict();
|
|
18574
|
+
var ShareLinkOwnerCommitOutputValidator = external_exports.discriminatedUnion("status", [
|
|
18575
|
+
external_exports.object({ status: external_exports.literal("completed"), share: ShareLinkValidator }).strict(),
|
|
18576
|
+
external_exports.object({
|
|
18577
|
+
status: external_exports.literal("pending"),
|
|
18578
|
+
id: ShareLinkIdValidator,
|
|
18579
|
+
operationId: external_exports.string().uuid()
|
|
18580
|
+
}).strict()
|
|
17862
18581
|
]);
|
|
17863
|
-
var
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17868
|
-
|
|
17869
|
-
|
|
17870
|
-
|
|
17871
|
-
publishedAt: external_exports.string().min(1),
|
|
17872
|
-
effectiveAt: external_exports.string().optional(),
|
|
17873
|
-
etag: external_exports.string().optional(),
|
|
17874
|
-
signingMode: CredentialRefreshSigningModeValidator.optional(),
|
|
17875
|
-
updateSummary: external_exports.string().optional()
|
|
17876
|
-
});
|
|
17877
|
-
var GetCredentialRefreshHistoryInputValidator = external_exports.object({
|
|
17878
|
-
refreshId: external_exports.string().min(1),
|
|
17879
|
-
cursor: external_exports.string().optional(),
|
|
17880
|
-
limit: external_exports.number().int().positive().optional()
|
|
17881
|
-
});
|
|
17882
|
-
var GetCredentialRefreshHistoryResultValidator = external_exports.object({
|
|
17883
|
-
records: CredentialRefreshVersionMetadataValidator.array(),
|
|
17884
|
-
hasMore: external_exports.boolean(),
|
|
17885
|
-
cursor: external_exports.string().optional()
|
|
17886
|
-
});
|
|
17887
|
-
var CredentialRefreshChallengeValidator = external_exports.object({
|
|
17888
|
-
challenge: external_exports.string().min(1),
|
|
17889
|
-
expiresAt: external_exports.string().min(1),
|
|
17890
|
-
domain: external_exports.string().optional(),
|
|
17891
|
-
scheme: external_exports.literal("LearnCardDIDAuth").optional()
|
|
17892
|
-
});
|
|
17893
|
-
var PublicCredentialRefreshEnvelopeValidator = external_exports.object({
|
|
17894
|
-
format: external_exports.literal("vc"),
|
|
17895
|
-
credential: VCValidator,
|
|
17896
|
-
etag: external_exports.string().optional()
|
|
17897
|
-
});
|
|
17898
|
-
var JweCredentialRefreshEnvelopeValidator = external_exports.object({
|
|
17899
|
-
format: external_exports.literal("jwe"),
|
|
17900
|
-
jwe: JWEValidator,
|
|
17901
|
-
etag: external_exports.string().optional(),
|
|
17902
|
-
/** Present on LearnCard-managed endpoints; optional for interoperable JWE services. */
|
|
17903
|
-
version: external_exports.number().int().positive().optional()
|
|
17904
|
-
});
|
|
17905
|
-
var CredentialRefreshResponseEnvelopeValidator = external_exports.discriminatedUnion("format", [
|
|
17906
|
-
PublicCredentialRefreshEnvelopeValidator,
|
|
17907
|
-
JweCredentialRefreshEnvelopeValidator
|
|
18582
|
+
var ShareLinkOwnerStatusOutputValidator = external_exports.discriminatedUnion("status", [
|
|
18583
|
+
external_exports.object({ status: external_exports.literal("found"), share: ShareLinkValidator }).strict(),
|
|
18584
|
+
external_exports.object({
|
|
18585
|
+
status: external_exports.literal("pending"),
|
|
18586
|
+
id: ShareLinkIdValidator,
|
|
18587
|
+
operationId: external_exports.string().uuid()
|
|
18588
|
+
}).strict(),
|
|
18589
|
+
external_exports.object({ status: external_exports.literal("not_found"), id: ShareLinkIdValidator }).strict()
|
|
17908
18590
|
]);
|
|
17909
|
-
var
|
|
17910
|
-
|
|
17911
|
-
|
|
17912
|
-
|
|
17913
|
-
|
|
17914
|
-
|
|
17915
|
-
|
|
17916
|
-
|
|
17917
|
-
|
|
17918
|
-
|
|
17919
|
-
|
|
17920
|
-
|
|
18591
|
+
var ShareLinkOwnerRecoveryOutputValidator = external_exports.object({ recovery: ShareOwnerRecoveryValidator }).strict();
|
|
18592
|
+
var ShareLinkOwnerContentOutputValidator = external_exports.object({
|
|
18593
|
+
id: ShareLinkIdValidator,
|
|
18594
|
+
contentVersion: safeVersion,
|
|
18595
|
+
envelope: ShareEnvelopeValidator
|
|
18596
|
+
}).strict();
|
|
18597
|
+
var ResolveShareLinkInputValidator = external_exports.object({
|
|
18598
|
+
id: ShareLinkIdValidator,
|
|
18599
|
+
/** Sent only in a POST body; it must never be placed in a share URL. */
|
|
18600
|
+
passcode: external_exports.string().min(4).max(64).optional()
|
|
18601
|
+
}).strict();
|
|
18602
|
+
var ShareLinkPublicStateValidator = external_exports.discriminatedUnion("state", [
|
|
18603
|
+
external_exports.object({
|
|
18604
|
+
state: external_exports.literal("passcode_required"),
|
|
18605
|
+
id: ShareLinkIdValidator
|
|
18606
|
+
}).strict(),
|
|
18607
|
+
external_exports.object({ state: external_exports.literal("try_later"), id: ShareLinkIdValidator }).strict(),
|
|
18608
|
+
external_exports.object({
|
|
18609
|
+
state: external_exports.literal("active"),
|
|
18610
|
+
id: ShareLinkIdValidator,
|
|
18611
|
+
title: external_exports.string().max(120),
|
|
18612
|
+
note: external_exports.string().max(500).optional(),
|
|
18613
|
+
selectedCount: external_exports.number().int().min(1).max(MAX_SELECTED_CREDENTIALS),
|
|
18614
|
+
contentVersion: safeVersion,
|
|
18615
|
+
contentUrl: external_exports.string(),
|
|
18616
|
+
sharer: external_exports.object({
|
|
18617
|
+
displayName: external_exports.string().max(120),
|
|
18618
|
+
avatar: external_exports.string().max(2048).optional()
|
|
18619
|
+
}),
|
|
18620
|
+
createdAt: external_exports.iso.datetime(),
|
|
18621
|
+
updatedAt: external_exports.iso.datetime(),
|
|
18622
|
+
expiresAt: external_exports.iso.datetime().nullable()
|
|
18623
|
+
}).strict(),
|
|
18624
|
+
external_exports.object({
|
|
18625
|
+
state: external_exports.literal("expired"),
|
|
18626
|
+
id: ShareLinkIdValidator,
|
|
18627
|
+
expiresAt: external_exports.iso.datetime()
|
|
18628
|
+
}).strict(),
|
|
18629
|
+
external_exports.object({
|
|
18630
|
+
state: external_exports.literal("stopped"),
|
|
18631
|
+
id: ShareLinkIdValidator,
|
|
18632
|
+
stoppedAt: external_exports.iso.datetime()
|
|
18633
|
+
}).strict(),
|
|
18634
|
+
external_exports.object({ state: external_exports.literal("not_found"), id: ShareLinkIdValidator }).strict()
|
|
17921
18635
|
]);
|
|
17922
|
-
var
|
|
17923
|
-
|
|
17924
|
-
|
|
17925
|
-
|
|
17926
|
-
|
|
17927
|
-
|
|
17928
|
-
var CredentialRefreshUnchangedResultValidator = external_exports.object({
|
|
17929
|
-
status: external_exports.literal("unchanged"),
|
|
17930
|
-
checkedAt: external_exports.string().min(1),
|
|
17931
|
-
etag: external_exports.string().optional()
|
|
17932
|
-
});
|
|
17933
|
-
var CredentialRefreshUnsupportedResultValidator = external_exports.object({
|
|
17934
|
-
status: external_exports.literal("unsupported")
|
|
17935
|
-
});
|
|
17936
|
-
var CredentialRefreshFailedResultValidator = external_exports.object({
|
|
17937
|
-
status: external_exports.literal("failed"),
|
|
17938
|
-
code: CredentialRefreshFailureCodeValidator,
|
|
17939
|
-
retryable: external_exports.boolean()
|
|
18636
|
+
var ListShareLinksInputValidator = external_exports.object({
|
|
18637
|
+
limit: external_exports.number().int().min(1).max(50).default(25),
|
|
18638
|
+
cursor: external_exports.string().max(512).optional()
|
|
18639
|
+
}).strict();
|
|
18640
|
+
var PaginatedShareLinksValidator = PaginationResponseValidator.extend({
|
|
18641
|
+
records: ShareLinkValidator.array()
|
|
17940
18642
|
});
|
|
17941
|
-
var
|
|
17942
|
-
|
|
17943
|
-
|
|
17944
|
-
|
|
17945
|
-
|
|
17946
|
-
|
|
18643
|
+
var AcknowledgeViewInputValidator = external_exports.object({
|
|
18644
|
+
receipt: external_exports.string().min(22).max(128).regex(/^[A-Za-z0-9_-]+$/)
|
|
18645
|
+
}).strict();
|
|
18646
|
+
var AcknowledgeViewOutputValidator = external_exports.object({ ok: external_exports.literal(true) }).strict();
|
|
18647
|
+
var SHARE_VIEW_RECEIPT_TTL_SECONDS = 10 * 60;
|
|
18648
|
+
var ShareLinkPublicContentViewValidator = external_exports.object({
|
|
18649
|
+
id: ShareLinkIdValidator,
|
|
18650
|
+
contentVersion: safeVersion,
|
|
18651
|
+
envelope: ShareEnvelopeValidator,
|
|
18652
|
+
contentUrl: external_exports.string(),
|
|
18653
|
+
receipt: external_exports.string().min(22).max(128).regex(/^[A-Za-z0-9_-]+$/)
|
|
18654
|
+
}).strict();
|
|
17947
18655
|
}
|
|
17948
18656
|
});
|
|
17949
18657
|
|
|
@@ -17965,35 +18673,63 @@ __export(index_exports, {
|
|
|
17965
18673
|
AtomicUpdateError: () => AtomicUpdateError,
|
|
17966
18674
|
AuthSessionError: () => import_types.AuthSessionError,
|
|
17967
18675
|
DEFAULT_KDF_PARAMS: () => DEFAULT_KDF_PARAMS,
|
|
18676
|
+
EMAIL_RELAY_ALGORITHM: () => EMAIL_RELAY_ALGORITHM,
|
|
18677
|
+
EMAIL_RELAY_ENVELOPE_VERSION: () => EMAIL_RELAY_ENVELOPE_VERSION,
|
|
18678
|
+
ESCROW_ALGORITHM: () => ESCROW_ALGORITHM,
|
|
18679
|
+
ESCROW_BLOB_INFO: () => ESCROW_BLOB_INFO,
|
|
18680
|
+
ESCROW_CLIENT_KEY_ID: () => ESCROW_CLIENT_KEY_ID,
|
|
18681
|
+
ESCROW_ENVELOPE_VERSION: () => ESCROW_ENVELOPE_VERSION,
|
|
18682
|
+
ESCROW_PIN_MAX_ATTEMPTS: () => ESCROW_PIN_MAX_ATTEMPTS,
|
|
18683
|
+
ESCROW_RELEASE_INFO: () => ESCROW_RELEASE_INFO,
|
|
18684
|
+
EscrowHoldRestartThrottledError: () => EscrowHoldRestartThrottledError,
|
|
18685
|
+
EscrowPinLockedError: () => EscrowPinLockedError,
|
|
18686
|
+
EscrowPinMismatchError: () => EscrowPinMismatchError,
|
|
18687
|
+
EscrowPinThrottledError: () => EscrowPinThrottledError,
|
|
18688
|
+
EscrowPinUnavailableError: () => EscrowPinUnavailableError,
|
|
18689
|
+
EscrowRequestError: () => EscrowRequestError,
|
|
18690
|
+
IdentityRecoverySessionConsumedError: () => IdentityRecoverySessionConsumedError,
|
|
18691
|
+
PIN_MAX_LENGTH: () => PIN_MAX_LENGTH,
|
|
18692
|
+
PIN_MIN_LENGTH: () => PIN_MIN_LENGTH,
|
|
17968
18693
|
SSSApiClient: () => SSSApiClient,
|
|
17969
18694
|
SSSKeyManager: () => SSSKeyManager,
|
|
17970
18695
|
SSS_THRESHOLD: () => SSS_THRESHOLD,
|
|
17971
18696
|
SSS_TOTAL_SHARES: () => SSS_TOTAL_SHARES,
|
|
17972
18697
|
ShareVerificationError: () => ShareVerificationError,
|
|
18698
|
+
ShareWriteRejectedError: () => ShareWriteRejectedError,
|
|
17973
18699
|
approveQrLoginSession: () => approveQrLoginSession,
|
|
17974
18700
|
atomicRecovery: () => atomicRecovery,
|
|
17975
18701
|
atomicShareUpdate: () => atomicShareUpdate,
|
|
17976
18702
|
base64ToBuffer: () => base64ToBuffer,
|
|
17977
18703
|
bufferToBase64: () => bufferToBase64,
|
|
18704
|
+
buildRecoveryPhraseChallengeOptions: () => buildRecoveryPhraseChallengeOptions,
|
|
17978
18705
|
bytesToHex: () => bytesToHex,
|
|
17979
18706
|
clearAllShares: () => clearAllShares,
|
|
18707
|
+
constantTimeEqualHex: () => constantTimeEqualHex,
|
|
17980
18708
|
countWords: () => countWords,
|
|
17981
18709
|
createAdaptiveStorage: () => createAdaptiveStorage,
|
|
17982
18710
|
createPasskeyCredential: () => createPasskeyCredential,
|
|
17983
18711
|
createQrLoginSession: () => createQrLoginSession,
|
|
17984
18712
|
createSSSKeyManager: () => createSSSKeyManager,
|
|
17985
18713
|
createSSSStrategy: () => createSSSStrategy,
|
|
18714
|
+
decryptEmailRelayPayload: () => decryptEmailRelayPayload,
|
|
18715
|
+
decryptEscrowBlob: () => decryptEscrowBlob,
|
|
17986
18716
|
decryptShareFromTransfer: () => decryptShareFromTransfer,
|
|
17987
18717
|
decryptShareWithPasskey: () => decryptShareWithPasskey,
|
|
17988
18718
|
decryptWithPassword: () => decryptWithPassword,
|
|
17989
18719
|
deleteDeviceShare: () => deleteDeviceShare,
|
|
17990
18720
|
deriveKeyFromPasskey: () => deriveKeyFromPasskey,
|
|
17991
18721
|
deriveKeyFromPassword: () => deriveKeyFromPassword,
|
|
18722
|
+
derivePinProof: () => derivePinProof,
|
|
18723
|
+
encryptEmailRelayPayload: () => encryptEmailRelayPayload,
|
|
18724
|
+
encryptEscrowBlob: () => encryptEscrowBlob,
|
|
17992
18725
|
encryptShareForTransfer: () => encryptShareForTransfer,
|
|
17993
18726
|
encryptShareWithPasskey: () => encryptShareWithPasskey,
|
|
17994
18727
|
encryptWithPassword: () => encryptWithPassword,
|
|
17995
18728
|
generateEd25519PrivateKey: () => generateEd25519PrivateKey,
|
|
18729
|
+
generateEmailRelayConfirmationCode: () => generateEmailRelayConfirmationCode,
|
|
17996
18730
|
generateEphemeralKeypair: () => generateEphemeralKeypair,
|
|
18731
|
+
generateEscrowKeyPair: () => generateEscrowKeyPair,
|
|
18732
|
+
generatePinSalt: () => generatePinSalt,
|
|
17997
18733
|
generateRecoveryPhrase: () => generateRecoveryPhrase,
|
|
17998
18734
|
getDeviceShare: () => getDeviceShare,
|
|
17999
18735
|
getQrLoginSessionInfo: () => getQrLoginSessionInfo,
|
|
@@ -18004,24 +18740,38 @@ __export(index_exports, {
|
|
|
18004
18740
|
isPublicComputerMode: () => isPublicComputerMode,
|
|
18005
18741
|
isWebAuthnSupported: () => isWebAuthnSupported,
|
|
18006
18742
|
listAllDeviceShares: () => listAllDeviceShares,
|
|
18743
|
+
normalizePin: () => normalizePin,
|
|
18007
18744
|
notifyDevicesForQrSession: () => notifyDevicesForQrSession,
|
|
18745
|
+
openEscrowRelease: () => openEscrowRelease,
|
|
18746
|
+
parseEmailRelayEnvelope: () => parseEmailRelayEnvelope,
|
|
18747
|
+
parseEmailRelayPlaintext: () => parseEmailRelayPlaintext,
|
|
18748
|
+
parseEscrowBlobPlaintext: () => parseEscrowBlobPlaintext,
|
|
18749
|
+
parseEscrowEnvelope: () => parseEscrowEnvelope,
|
|
18750
|
+
parseEscrowReleasePlaintext: () => parseEscrowReleasePlaintext,
|
|
18008
18751
|
pollQrLoginSession: () => pollQrLoginSession,
|
|
18009
18752
|
pollUntilApproved: () => pollUntilApproved,
|
|
18010
18753
|
reconstructFromShares: () => reconstructFromShares,
|
|
18011
18754
|
reconstructPrivateKey: () => reconstructPrivateKey,
|
|
18012
18755
|
recoveryPhraseToShare: () => recoveryPhraseToShare,
|
|
18756
|
+
sealEscrowRelease: () => sealEscrowRelease,
|
|
18013
18757
|
setPublicComputerMode: () => setPublicComputerMode,
|
|
18014
18758
|
shareToRecoveryPhrase: () => shareToRecoveryPhrase,
|
|
18015
18759
|
splitAndVerify: () => splitAndVerify,
|
|
18016
18760
|
splitPrivateKey: () => splitPrivateKey,
|
|
18017
18761
|
storeDeviceShare: () => storeDeviceShare,
|
|
18018
18762
|
storeShareVersion: () => storeShareVersion,
|
|
18763
|
+
validatePin: () => validatePin,
|
|
18019
18764
|
validateRecoveryPhrase: () => validateRecoveryPhrase,
|
|
18765
|
+
verifyEnclaveAttestation: () => verifyEnclaveAttestation,
|
|
18020
18766
|
verifyStoredShares: () => verifyStoredShares
|
|
18021
18767
|
});
|
|
18022
18768
|
module.exports = __toCommonJS(index_exports);
|
|
18023
18769
|
|
|
18024
18770
|
// src/api-client.ts
|
|
18771
|
+
var DID_CHALLENGE_REQUIRED_ERROR = "This operation requires the DID-challenge flow; use createSSSStrategy";
|
|
18772
|
+
var throwDidChallengeRequired = /* @__PURE__ */ __name(() => {
|
|
18773
|
+
throw new Error(DID_CHALLENGE_REQUIRED_ERROR);
|
|
18774
|
+
}, "throwDidChallengeRequired");
|
|
18025
18775
|
var SSSApiClient = class {
|
|
18026
18776
|
static {
|
|
18027
18777
|
__name(this, "SSSApiClient");
|
|
@@ -18034,20 +18784,9 @@ var SSSApiClient = class {
|
|
|
18034
18784
|
const token = await this.authProvider.getIdToken();
|
|
18035
18785
|
return {
|
|
18036
18786
|
"Content-Type": "application/json",
|
|
18037
|
-
|
|
18787
|
+
"X-Auth-Token": token
|
|
18038
18788
|
};
|
|
18039
18789
|
}
|
|
18040
|
-
async getContactMethodFromUser() {
|
|
18041
|
-
const user = await this.authProvider.getCurrentUser();
|
|
18042
|
-
if (!user) return null;
|
|
18043
|
-
if (user.email) {
|
|
18044
|
-
return { type: "email", value: user.email.toLowerCase() };
|
|
18045
|
-
}
|
|
18046
|
-
if (user.phone) {
|
|
18047
|
-
return { type: "phone", value: user.phone };
|
|
18048
|
-
}
|
|
18049
|
-
return null;
|
|
18050
|
-
}
|
|
18051
18790
|
async getAuthShare() {
|
|
18052
18791
|
const headers = await this.getAuthHeaders();
|
|
18053
18792
|
const providerType = this.authProvider.getProviderType();
|
|
@@ -18064,52 +18803,24 @@ var SSSApiClient = class {
|
|
|
18064
18803
|
}
|
|
18065
18804
|
return response.json();
|
|
18066
18805
|
}
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
|
|
18070
|
-
const response = await fetch(`${this.serverUrl}/keys/auth-share`, {
|
|
18071
|
-
method: "PUT",
|
|
18072
|
-
headers,
|
|
18073
|
-
body: JSON.stringify({
|
|
18074
|
-
...input,
|
|
18075
|
-
providerType
|
|
18076
|
-
})
|
|
18077
|
-
});
|
|
18078
|
-
if (!response.ok) {
|
|
18079
|
-
throw new Error(`Failed to store auth share: ${response.statusText}`);
|
|
18080
|
-
}
|
|
18806
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18807
|
+
async storeAuthShare(_input) {
|
|
18808
|
+
throwDidChallengeRequired();
|
|
18081
18809
|
}
|
|
18082
|
-
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
const response = await fetch(`${this.serverUrl}/keys/recovery`, {
|
|
18086
|
-
method: "POST",
|
|
18087
|
-
headers,
|
|
18088
|
-
body: JSON.stringify({
|
|
18089
|
-
...input,
|
|
18090
|
-
providerType
|
|
18091
|
-
})
|
|
18092
|
-
});
|
|
18093
|
-
if (!response.ok) {
|
|
18094
|
-
throw new Error(`Failed to add recovery method: ${response.statusText}`);
|
|
18095
|
-
}
|
|
18810
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18811
|
+
async addRecoveryMethod(_input) {
|
|
18812
|
+
throwDidChallengeRequired();
|
|
18096
18813
|
}
|
|
18097
18814
|
async getRecoveryShare(type, credentialId) {
|
|
18098
18815
|
const token = await this.authProvider.getIdToken();
|
|
18099
18816
|
const providerType = this.authProvider.getProviderType();
|
|
18100
|
-
const params = new URLSearchParams({
|
|
18101
|
-
type,
|
|
18102
|
-
providerType,
|
|
18103
|
-
authToken: token
|
|
18104
|
-
});
|
|
18817
|
+
const params = new URLSearchParams({ type, providerType });
|
|
18105
18818
|
if (credentialId) {
|
|
18106
18819
|
params.append("credentialId", credentialId);
|
|
18107
18820
|
}
|
|
18108
18821
|
const response = await fetch(`${this.serverUrl}/keys/recovery?${params}`, {
|
|
18109
18822
|
method: "GET",
|
|
18110
|
-
headers: {
|
|
18111
|
-
"Content-Type": "application/json"
|
|
18112
|
-
}
|
|
18823
|
+
headers: { "Content-Type": "application/json", "X-Auth-Token": token }
|
|
18113
18824
|
});
|
|
18114
18825
|
if (!response.ok) {
|
|
18115
18826
|
if (response.status === 404) {
|
|
@@ -18119,81 +18830,81 @@ var SSSApiClient = class {
|
|
|
18119
18830
|
}
|
|
18120
18831
|
return response.json();
|
|
18121
18832
|
}
|
|
18122
|
-
async
|
|
18123
|
-
const
|
|
18124
|
-
const providerType = this.authProvider.getProviderType();
|
|
18125
|
-
const response = await fetch(`${this.serverUrl}/keys/migrate`, {
|
|
18833
|
+
async startIdentityRecovery(email) {
|
|
18834
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/start`, {
|
|
18126
18835
|
method: "POST",
|
|
18127
|
-
headers,
|
|
18128
|
-
body: JSON.stringify({
|
|
18836
|
+
headers: { "Content-Type": "application/json" },
|
|
18837
|
+
body: JSON.stringify({ email })
|
|
18129
18838
|
});
|
|
18130
18839
|
if (!response.ok) {
|
|
18131
|
-
throw new Error(`Failed to
|
|
18840
|
+
throw new Error(`Failed to send recovery code: ${response.statusText}`);
|
|
18132
18841
|
}
|
|
18133
18842
|
}
|
|
18134
|
-
async
|
|
18135
|
-
const
|
|
18136
|
-
const providerType = this.authProvider.getProviderType();
|
|
18137
|
-
let email = overrideEmail;
|
|
18138
|
-
if (!email) {
|
|
18139
|
-
const user = await this.authProvider.getCurrentUser();
|
|
18140
|
-
email = user?.email;
|
|
18141
|
-
}
|
|
18142
|
-
if (!email) {
|
|
18143
|
-
console.warn("Cannot send email backup share: no email address available");
|
|
18144
|
-
return;
|
|
18145
|
-
}
|
|
18146
|
-
const response = await fetch(`${this.serverUrl}/keys/email-backup`, {
|
|
18843
|
+
async verifyIdentityRecovery(email, code) {
|
|
18844
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/verify`, {
|
|
18147
18845
|
method: "POST",
|
|
18148
|
-
headers,
|
|
18149
|
-
body: JSON.stringify({
|
|
18150
|
-
providerType,
|
|
18151
|
-
emailShare,
|
|
18152
|
-
email
|
|
18153
|
-
})
|
|
18846
|
+
headers: { "Content-Type": "application/json" },
|
|
18847
|
+
body: JSON.stringify({ email, code })
|
|
18154
18848
|
});
|
|
18155
18849
|
if (!response.ok) {
|
|
18156
|
-
|
|
18850
|
+
throw new Error(`Failed to verify recovery code: ${response.statusText}`);
|
|
18157
18851
|
}
|
|
18852
|
+
return response.json();
|
|
18158
18853
|
}
|
|
18159
|
-
async
|
|
18160
|
-
const
|
|
18161
|
-
const providerType = this.authProvider.getProviderType();
|
|
18162
|
-
const response = await fetch(`${this.serverUrl}/keys/recovery-email/add`, {
|
|
18854
|
+
async useIdentityRecoverySession(recoverySessionToken, type, credentialId) {
|
|
18855
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/recover`, {
|
|
18163
18856
|
method: "POST",
|
|
18164
18857
|
headers: { "Content-Type": "application/json" },
|
|
18165
|
-
body: JSON.stringify({
|
|
18858
|
+
body: JSON.stringify({ recoverySessionToken, type, credentialId })
|
|
18166
18859
|
});
|
|
18167
18860
|
if (!response.ok) {
|
|
18168
|
-
|
|
18169
|
-
throw new Error(data?.message || `Failed to add recovery email: ${response.statusText}`);
|
|
18861
|
+
throw new Error(`Failed to retrieve recovery data: ${response.statusText}`);
|
|
18170
18862
|
}
|
|
18863
|
+
return response.json();
|
|
18171
18864
|
}
|
|
18172
|
-
async
|
|
18173
|
-
const
|
|
18174
|
-
const providerType = this.authProvider.getProviderType();
|
|
18175
|
-
const response = await fetch(`${this.serverUrl}/keys/recovery-email/verify`, {
|
|
18865
|
+
async completeIdentityRecoveryRebind(input) {
|
|
18866
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/rebind`, {
|
|
18176
18867
|
method: "POST",
|
|
18177
|
-
headers: {
|
|
18178
|
-
|
|
18868
|
+
headers: {
|
|
18869
|
+
"Content-Type": "application/json",
|
|
18870
|
+
Authorization: `Bearer ${input.didAuthVp}`,
|
|
18871
|
+
"X-Auth-Token": input.newAuthToken
|
|
18872
|
+
},
|
|
18873
|
+
body: JSON.stringify({
|
|
18874
|
+
recoverySessionToken: input.recoverySessionToken,
|
|
18875
|
+
providerType: input.providerType,
|
|
18876
|
+
primaryDid: input.primaryDid,
|
|
18877
|
+
authShare: input.authShare
|
|
18878
|
+
})
|
|
18179
18879
|
});
|
|
18180
18880
|
if (!response.ok) {
|
|
18181
|
-
|
|
18182
|
-
throw new Error(data?.message || `Failed to verify recovery email: ${response.statusText}`);
|
|
18881
|
+
throw new Error(`Failed to bind the new sign-in: ${response.statusText}`);
|
|
18183
18882
|
}
|
|
18184
18883
|
return response.json();
|
|
18185
18884
|
}
|
|
18885
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18886
|
+
async markMigrated() {
|
|
18887
|
+
throwDidChallengeRequired();
|
|
18888
|
+
}
|
|
18889
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18890
|
+
async activate() {
|
|
18891
|
+
throwDidChallengeRequired();
|
|
18892
|
+
}
|
|
18893
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18894
|
+
async sendEmailBackupShare(_emailShare, _overrideEmail) {
|
|
18895
|
+
throwDidChallengeRequired();
|
|
18896
|
+
}
|
|
18897
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18898
|
+
async addRecoveryEmail(_email) {
|
|
18899
|
+
throwDidChallengeRequired();
|
|
18900
|
+
}
|
|
18901
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18902
|
+
async verifyRecoveryEmail(_code) {
|
|
18903
|
+
return throwDidChallengeRequired();
|
|
18904
|
+
}
|
|
18905
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
18186
18906
|
async deleteUserKey() {
|
|
18187
|
-
|
|
18188
|
-
const providerType = this.authProvider.getProviderType();
|
|
18189
|
-
const response = await fetch(`${this.serverUrl}/keys`, {
|
|
18190
|
-
method: "DELETE",
|
|
18191
|
-
headers,
|
|
18192
|
-
body: JSON.stringify({ providerType })
|
|
18193
|
-
});
|
|
18194
|
-
if (!response.ok) {
|
|
18195
|
-
throw new Error(`Failed to delete user key: ${response.statusText}`);
|
|
18196
|
-
}
|
|
18907
|
+
throwDidChallengeRequired();
|
|
18197
18908
|
}
|
|
18198
18909
|
};
|
|
18199
18910
|
|
|
@@ -18388,11 +19099,10 @@ async function getOrCreateMasterKey() {
|
|
|
18388
19099
|
(s) => s.get("master-key")
|
|
18389
19100
|
);
|
|
18390
19101
|
if (existing) return existing;
|
|
18391
|
-
const key = await crypto.subtle.generateKey(
|
|
18392
|
-
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
);
|
|
19102
|
+
const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
|
|
19103
|
+
"encrypt",
|
|
19104
|
+
"decrypt"
|
|
19105
|
+
]);
|
|
18396
19106
|
await tx(db, KEYS_STORE, "readwrite", (s) => s.put(key, "master-key"));
|
|
18397
19107
|
return key;
|
|
18398
19108
|
} finally {
|
|
@@ -18493,12 +19203,7 @@ __name(getShareVersion, "getShareVersion");
|
|
|
18493
19203
|
async function getDeviceShare(id = DEFAULT_DEVICE_SHARE_ID) {
|
|
18494
19204
|
const db = await openDB();
|
|
18495
19205
|
try {
|
|
18496
|
-
const raw = await tx(
|
|
18497
|
-
db,
|
|
18498
|
-
SHARES_STORE,
|
|
18499
|
-
"readonly",
|
|
18500
|
-
(s) => s.get(id)
|
|
18501
|
-
);
|
|
19206
|
+
const raw = await tx(db, SHARES_STORE, "readonly", (s) => s.get(id));
|
|
18502
19207
|
if (raw == null) {
|
|
18503
19208
|
return null;
|
|
18504
19209
|
}
|
|
@@ -18557,7 +19262,9 @@ async function listAllDeviceShares() {
|
|
|
18557
19262
|
try {
|
|
18558
19263
|
const share = await getDeviceShare(id);
|
|
18559
19264
|
if (!share) {
|
|
18560
|
-
console.warn(
|
|
19265
|
+
console.warn(
|
|
19266
|
+
`SSS Storage: removing orphaned entry that failed to decrypt: ${id}`
|
|
19267
|
+
);
|
|
18561
19268
|
await deleteDeviceShare(id);
|
|
18562
19269
|
continue;
|
|
18563
19270
|
}
|
|
@@ -18641,6 +19348,7 @@ function createAdaptiveStorage() {
|
|
|
18641
19348
|
getDeviceShare: /* @__PURE__ */ __name((id) => isPublicComputerMode() ? sessionGetDeviceShare(id) : getDeviceShare(id), "getDeviceShare"),
|
|
18642
19349
|
hasDeviceShare: /* @__PURE__ */ __name((id) => isPublicComputerMode() ? sessionHasDeviceShare(id) : hasDeviceShare(id), "hasDeviceShare"),
|
|
18643
19350
|
clearAllShares: /* @__PURE__ */ __name((id) => isPublicComputerMode() ? sessionClearAllShares(id) : clearAllShares(id), "clearAllShares"),
|
|
19351
|
+
deleteDeviceShare: /* @__PURE__ */ __name((id) => isPublicComputerMode() ? sessionDeleteDeviceShare(id) : deleteDeviceShare(id), "deleteDeviceShare"),
|
|
18644
19352
|
storeShareVersion: /* @__PURE__ */ __name((version, id) => isPublicComputerMode() ? sessionStoreShareVersion(version, id) : storeShareVersion(version, id), "storeShareVersion"),
|
|
18645
19353
|
getShareVersion: /* @__PURE__ */ __name((id) => isPublicComputerMode() ? sessionGetShareVersion(id) : getShareVersion(id), "getShareVersion")
|
|
18646
19354
|
};
|
|
@@ -18921,6 +19629,33 @@ function countWords(phrase) {
|
|
|
18921
19629
|
return phrase.trim().split(/\s+/).filter((w) => w.length > 0).length;
|
|
18922
19630
|
}
|
|
18923
19631
|
__name(countWords, "countWords");
|
|
19632
|
+
function randomIndex(max) {
|
|
19633
|
+
const limit = 4294967296 - 4294967296 % max;
|
|
19634
|
+
for (; ; ) {
|
|
19635
|
+
const value = crypto.getRandomValues(new Uint32Array(1))[0];
|
|
19636
|
+
if (value === void 0) throw new Error("Failed to choose recovery phrase words");
|
|
19637
|
+
if (value < limit) return value % max;
|
|
19638
|
+
}
|
|
19639
|
+
}
|
|
19640
|
+
__name(randomIndex, "randomIndex");
|
|
19641
|
+
async function buildRecoveryPhraseChallengeOptions(phrase, challengeWordIndices, optionCount = 6) {
|
|
19642
|
+
const words = phrase.trim().split(/\s+/);
|
|
19643
|
+
const phraseWords = new Set(words);
|
|
19644
|
+
const decoyPool = (await getWordlist()).filter((word) => !phraseWords.has(word));
|
|
19645
|
+
return challengeWordIndices.map((wordIndex) => {
|
|
19646
|
+
const answer = words[wordIndex];
|
|
19647
|
+
if (!answer) throw new Error("Recovery phrase challenge index is out of range");
|
|
19648
|
+
const options = /* @__PURE__ */ new Set([answer]);
|
|
19649
|
+
while (options.size < optionCount) options.add(decoyPool[randomIndex(decoyPool.length)]);
|
|
19650
|
+
const shuffled = [...options];
|
|
19651
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
19652
|
+
const j = randomIndex(i + 1);
|
|
19653
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
19654
|
+
}
|
|
19655
|
+
return shuffled;
|
|
19656
|
+
});
|
|
19657
|
+
}
|
|
19658
|
+
__name(buildRecoveryPhraseChallengeOptions, "buildRecoveryPhraseChallengeOptions");
|
|
18924
19659
|
|
|
18925
19660
|
// src/key-manager.ts
|
|
18926
19661
|
var SSSKeyManager = class {
|
|
@@ -19129,22 +19864,548 @@ var SSSKeyManager = class {
|
|
|
19129
19864
|
}
|
|
19130
19865
|
};
|
|
19131
19866
|
}
|
|
19132
|
-
async clearLocalData() {
|
|
19133
|
-
await deleteDeviceShare(this.config.deviceStorageKey);
|
|
19134
|
-
this.currentPrivateKey = null;
|
|
19135
|
-
this.initialized = false;
|
|
19867
|
+
async clearLocalData() {
|
|
19868
|
+
await deleteDeviceShare(this.config.deviceStorageKey);
|
|
19869
|
+
this.currentPrivateKey = null;
|
|
19870
|
+
this.initialized = false;
|
|
19871
|
+
}
|
|
19872
|
+
async deleteAccount() {
|
|
19873
|
+
await this.apiClient.deleteUserKey();
|
|
19874
|
+
await clearAllShares();
|
|
19875
|
+
this.currentPrivateKey = null;
|
|
19876
|
+
this.initialized = false;
|
|
19877
|
+
}
|
|
19878
|
+
};
|
|
19879
|
+
function createSSSKeyManager(config) {
|
|
19880
|
+
return new SSSKeyManager(config);
|
|
19881
|
+
}
|
|
19882
|
+
__name(createSSSKeyManager, "createSSSKeyManager");
|
|
19883
|
+
|
|
19884
|
+
// src/email-relay-crypto.ts
|
|
19885
|
+
var EMAIL_RELAY_ENVELOPE_VERSION = 1;
|
|
19886
|
+
var EMAIL_RELAY_ALGORITHM = "P-256-HKDF-SHA256-AES-256-GCM";
|
|
19887
|
+
var encodeUtf8 = /* @__PURE__ */ __name((value) => {
|
|
19888
|
+
const bytes = new TextEncoder().encode(value);
|
|
19889
|
+
const copy = new Uint8Array(new ArrayBuffer(bytes.byteLength));
|
|
19890
|
+
copy.set(bytes);
|
|
19891
|
+
return copy;
|
|
19892
|
+
}, "encodeUtf8");
|
|
19893
|
+
var EMAIL_RELAY_INFO = encodeUtf8("learncard-email-relay-v1");
|
|
19894
|
+
var SIX_DIGIT_CODE_RANGE = 9e5;
|
|
19895
|
+
var UINT32_RANGE = 4294967296;
|
|
19896
|
+
var MAX_UNBIASED_UINT32 = Math.floor(UINT32_RANGE / SIX_DIGIT_CODE_RANGE) * SIX_DIGIT_CODE_RANGE;
|
|
19897
|
+
var isRecord = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
|
|
19898
|
+
var requireString = /* @__PURE__ */ __name((record, key, options) => {
|
|
19899
|
+
const value = record[key];
|
|
19900
|
+
if (typeof value !== "string" || value.length < (options.min ?? 1) || value.length > options.max || options.pattern && !options.pattern.test(value)) {
|
|
19901
|
+
throw new Error(`Invalid email relay ${key}`);
|
|
19902
|
+
}
|
|
19903
|
+
return value;
|
|
19904
|
+
}, "requireString");
|
|
19905
|
+
var parseBranding = /* @__PURE__ */ __name((value) => {
|
|
19906
|
+
if (value === void 0) return void 0;
|
|
19907
|
+
if (!isRecord(value)) throw new Error("Invalid email relay branding");
|
|
19908
|
+
const branding = {};
|
|
19909
|
+
const keys = [
|
|
19910
|
+
"brandName",
|
|
19911
|
+
"logoUrl",
|
|
19912
|
+
"logoAlt",
|
|
19913
|
+
"primaryColor",
|
|
19914
|
+
"primaryTextColor",
|
|
19915
|
+
"supportEmail",
|
|
19916
|
+
"websiteUrl",
|
|
19917
|
+
"appUrl",
|
|
19918
|
+
"fromDomain",
|
|
19919
|
+
"copyrightHolder"
|
|
19920
|
+
];
|
|
19921
|
+
for (const key of keys) {
|
|
19922
|
+
const field = value[key];
|
|
19923
|
+
if (field === void 0) continue;
|
|
19924
|
+
if (typeof field !== "string" || field.length > 2048) {
|
|
19925
|
+
throw new Error(`Invalid email relay branding.${key}`);
|
|
19926
|
+
}
|
|
19927
|
+
branding[key] = field;
|
|
19928
|
+
}
|
|
19929
|
+
return branding;
|
|
19930
|
+
}, "parseBranding");
|
|
19931
|
+
var parseEmailRelayEnvelope = /* @__PURE__ */ __name((value) => {
|
|
19932
|
+
if (!isRecord(value)) throw new Error("Invalid email relay envelope");
|
|
19933
|
+
if (value.version !== EMAIL_RELAY_ENVELOPE_VERSION) {
|
|
19934
|
+
throw new Error("Unsupported email relay envelope version");
|
|
19935
|
+
}
|
|
19936
|
+
if (value.algorithm !== EMAIL_RELAY_ALGORITHM) {
|
|
19937
|
+
throw new Error("Unsupported email relay algorithm");
|
|
19938
|
+
}
|
|
19939
|
+
return {
|
|
19940
|
+
version: EMAIL_RELAY_ENVELOPE_VERSION,
|
|
19941
|
+
algorithm: EMAIL_RELAY_ALGORITHM,
|
|
19942
|
+
keyId: requireString(value, "keyId", {
|
|
19943
|
+
max: 128,
|
|
19944
|
+
pattern: /^[A-Za-z0-9._-]+$/
|
|
19945
|
+
}),
|
|
19946
|
+
ephemeralPublicKey: requireString(value, "ephemeralPublicKey", { max: 256 }),
|
|
19947
|
+
salt: requireString(value, "salt", { max: 128 }),
|
|
19948
|
+
iv: requireString(value, "iv", { max: 64 }),
|
|
19949
|
+
ciphertext: requireString(value, "ciphertext", { max: 16384 })
|
|
19950
|
+
};
|
|
19951
|
+
}, "parseEmailRelayEnvelope");
|
|
19952
|
+
var parseEmailRelayPlaintext = /* @__PURE__ */ __name((value) => {
|
|
19953
|
+
if (!isRecord(value) || value.version !== EMAIL_RELAY_ENVELOPE_VERSION) {
|
|
19954
|
+
throw new Error("Invalid email relay plaintext");
|
|
19955
|
+
}
|
|
19956
|
+
return {
|
|
19957
|
+
version: EMAIL_RELAY_ENVELOPE_VERSION,
|
|
19958
|
+
targetEmail: requireString(value, "targetEmail", {
|
|
19959
|
+
max: 320,
|
|
19960
|
+
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
19961
|
+
}).trim().toLowerCase(),
|
|
19962
|
+
recoveryKey: requireString(value, "recoveryKey", {
|
|
19963
|
+
min: 5,
|
|
19964
|
+
max: 4096,
|
|
19965
|
+
pattern: /^[0-9a-f]+$/i
|
|
19966
|
+
}),
|
|
19967
|
+
confirmationCode: requireString(value, "confirmationCode", {
|
|
19968
|
+
max: 6,
|
|
19969
|
+
pattern: /^\d{6}$/
|
|
19970
|
+
}),
|
|
19971
|
+
branding: parseBranding(value.branding)
|
|
19972
|
+
};
|
|
19973
|
+
}, "parseEmailRelayPlaintext");
|
|
19974
|
+
var getAad = /* @__PURE__ */ __name((envelope) => encodeUtf8(`${envelope.version}|${envelope.algorithm}|${envelope.keyId}`), "getAad");
|
|
19975
|
+
var deriveAesKey = /* @__PURE__ */ __name(async (sharedSecret, salt, usage) => {
|
|
19976
|
+
const keyMaterial = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, [
|
|
19977
|
+
"deriveKey"
|
|
19978
|
+
]);
|
|
19979
|
+
return crypto.subtle.deriveKey(
|
|
19980
|
+
{ name: "HKDF", hash: "SHA-256", salt, info: EMAIL_RELAY_INFO },
|
|
19981
|
+
keyMaterial,
|
|
19982
|
+
{ name: "AES-GCM", length: 256 },
|
|
19983
|
+
false,
|
|
19984
|
+
[usage]
|
|
19985
|
+
);
|
|
19986
|
+
}, "deriveAesKey");
|
|
19987
|
+
var generateEmailRelayConfirmationCode = /* @__PURE__ */ __name(() => {
|
|
19988
|
+
const random = new Uint32Array(1);
|
|
19989
|
+
let value;
|
|
19990
|
+
do {
|
|
19991
|
+
crypto.getRandomValues(random);
|
|
19992
|
+
value = random[0] ?? UINT32_RANGE;
|
|
19993
|
+
} while (value >= MAX_UNBIASED_UINT32);
|
|
19994
|
+
return String(1e5 + value % SIX_DIGIT_CODE_RANGE);
|
|
19995
|
+
}, "generateEmailRelayConfirmationCode");
|
|
19996
|
+
var encryptEmailRelayPayload = /* @__PURE__ */ __name(async (input, publicKeyBase64, keyId) => {
|
|
19997
|
+
const plaintext = parseEmailRelayPlaintext({
|
|
19998
|
+
...input,
|
|
19999
|
+
version: EMAIL_RELAY_ENVELOPE_VERSION
|
|
20000
|
+
});
|
|
20001
|
+
const normalizedKeyId = keyId.trim();
|
|
20002
|
+
if (!publicKeyBase64.trim()) throw new Error("Email relay public key is not configured");
|
|
20003
|
+
if (!/^[A-Za-z0-9._-]{1,128}$/.test(normalizedKeyId)) {
|
|
20004
|
+
throw new Error("Email relay key ID is not configured");
|
|
20005
|
+
}
|
|
20006
|
+
const recipientPublicKey = await crypto.subtle.importKey(
|
|
20007
|
+
"spki",
|
|
20008
|
+
base64ToBuffer(publicKeyBase64.trim()).buffer,
|
|
20009
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20010
|
+
false,
|
|
20011
|
+
[]
|
|
20012
|
+
);
|
|
20013
|
+
const ephemeralKeyPair = await crypto.subtle.generateKey(
|
|
20014
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20015
|
+
true,
|
|
20016
|
+
["deriveBits"]
|
|
20017
|
+
);
|
|
20018
|
+
const sharedSecret = await crypto.subtle.deriveBits(
|
|
20019
|
+
{ name: "ECDH", public: recipientPublicKey },
|
|
20020
|
+
ephemeralKeyPair.privateKey,
|
|
20021
|
+
256
|
|
20022
|
+
);
|
|
20023
|
+
const salt = crypto.getRandomValues(new Uint8Array(32));
|
|
20024
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
20025
|
+
const aesKey = await deriveAesKey(sharedSecret, salt, "encrypt");
|
|
20026
|
+
const envelopeMetadata = {
|
|
20027
|
+
version: EMAIL_RELAY_ENVELOPE_VERSION,
|
|
20028
|
+
algorithm: EMAIL_RELAY_ALGORITHM,
|
|
20029
|
+
keyId: normalizedKeyId
|
|
20030
|
+
};
|
|
20031
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
20032
|
+
{ name: "AES-GCM", iv, additionalData: getAad(envelopeMetadata) },
|
|
20033
|
+
aesKey,
|
|
20034
|
+
encodeUtf8(JSON.stringify(plaintext))
|
|
20035
|
+
);
|
|
20036
|
+
const ephemeralPublicKey = await crypto.subtle.exportKey("raw", ephemeralKeyPair.publicKey);
|
|
20037
|
+
return {
|
|
20038
|
+
...envelopeMetadata,
|
|
20039
|
+
ephemeralPublicKey: bufferToBase64(ephemeralPublicKey),
|
|
20040
|
+
salt: bufferToBase64(salt.buffer),
|
|
20041
|
+
iv: bufferToBase64(iv.buffer),
|
|
20042
|
+
ciphertext: bufferToBase64(ciphertext)
|
|
20043
|
+
};
|
|
20044
|
+
}, "encryptEmailRelayPayload");
|
|
20045
|
+
var decryptEmailRelayPayload = /* @__PURE__ */ __name(async (value, privateKeyBase64) => {
|
|
20046
|
+
const envelope = parseEmailRelayEnvelope(value);
|
|
20047
|
+
const privateKey = await crypto.subtle.importKey(
|
|
20048
|
+
"pkcs8",
|
|
20049
|
+
base64ToBuffer(privateKeyBase64.trim()).buffer,
|
|
20050
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20051
|
+
false,
|
|
20052
|
+
["deriveBits"]
|
|
20053
|
+
);
|
|
20054
|
+
const ephemeralPublicKey = await crypto.subtle.importKey(
|
|
20055
|
+
"raw",
|
|
20056
|
+
base64ToBuffer(envelope.ephemeralPublicKey).buffer,
|
|
20057
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20058
|
+
false,
|
|
20059
|
+
[]
|
|
20060
|
+
);
|
|
20061
|
+
const sharedSecret = await crypto.subtle.deriveBits(
|
|
20062
|
+
{ name: "ECDH", public: ephemeralPublicKey },
|
|
20063
|
+
privateKey,
|
|
20064
|
+
256
|
|
20065
|
+
);
|
|
20066
|
+
const salt = base64ToBuffer(envelope.salt);
|
|
20067
|
+
const iv = base64ToBuffer(envelope.iv);
|
|
20068
|
+
const aesKey = await deriveAesKey(sharedSecret, salt, "decrypt");
|
|
20069
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
20070
|
+
{ name: "AES-GCM", iv, additionalData: getAad(envelope) },
|
|
20071
|
+
aesKey,
|
|
20072
|
+
base64ToBuffer(envelope.ciphertext)
|
|
20073
|
+
);
|
|
20074
|
+
const parsed = JSON.parse(new TextDecoder().decode(decrypted));
|
|
20075
|
+
return parseEmailRelayPlaintext(parsed);
|
|
20076
|
+
}, "decryptEmailRelayPayload");
|
|
20077
|
+
|
|
20078
|
+
// src/escrow-crypto.ts
|
|
20079
|
+
var ESCROW_ENVELOPE_VERSION = 1;
|
|
20080
|
+
var ESCROW_ALGORITHM = "P-256-HKDF-SHA256-AES-256-GCM";
|
|
20081
|
+
var ESCROW_BLOB_INFO = "learncard-escrow-blob-v1";
|
|
20082
|
+
var ESCROW_RELEASE_INFO = "learncard-escrow-release-v1";
|
|
20083
|
+
var ESCROW_CLIENT_KEY_ID = "client";
|
|
20084
|
+
var encodeUtf82 = /* @__PURE__ */ __name((value) => {
|
|
20085
|
+
const bytes = new TextEncoder().encode(value);
|
|
20086
|
+
const copy = new Uint8Array(new ArrayBuffer(bytes.byteLength));
|
|
20087
|
+
copy.set(bytes);
|
|
20088
|
+
return copy;
|
|
20089
|
+
}, "encodeUtf8");
|
|
20090
|
+
var isRecord2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
|
|
20091
|
+
var requireString2 = /* @__PURE__ */ __name((record, key, options) => {
|
|
20092
|
+
const value = record[key];
|
|
20093
|
+
if (typeof value !== "string" || value.length < (options.min ?? 1) || value.length > options.max || options.pattern && !options.pattern.test(value)) {
|
|
20094
|
+
throw new Error(`Invalid escrow ${key}`);
|
|
20095
|
+
}
|
|
20096
|
+
return value;
|
|
20097
|
+
}, "requireString");
|
|
20098
|
+
var parseEscrowEnvelope = /* @__PURE__ */ __name((value) => {
|
|
20099
|
+
if (!isRecord2(value)) throw new Error("Invalid escrow envelope");
|
|
20100
|
+
if (value.version !== ESCROW_ENVELOPE_VERSION) {
|
|
20101
|
+
throw new Error("Unsupported escrow envelope version");
|
|
20102
|
+
}
|
|
20103
|
+
if (value.algorithm !== ESCROW_ALGORITHM) {
|
|
20104
|
+
throw new Error("Unsupported escrow algorithm");
|
|
20105
|
+
}
|
|
20106
|
+
return {
|
|
20107
|
+
version: ESCROW_ENVELOPE_VERSION,
|
|
20108
|
+
algorithm: ESCROW_ALGORITHM,
|
|
20109
|
+
keyId: requireString2(value, "keyId", { max: 128, pattern: /^[A-Za-z0-9._-]+$/ }),
|
|
20110
|
+
ephemeralPublicKey: requireString2(value, "ephemeralPublicKey", { max: 256 }),
|
|
20111
|
+
salt: requireString2(value, "salt", { max: 128 }),
|
|
20112
|
+
iv: requireString2(value, "iv", { max: 64 }),
|
|
20113
|
+
ciphertext: requireString2(value, "ciphertext", { max: 16384 })
|
|
20114
|
+
};
|
|
20115
|
+
}, "parseEscrowEnvelope");
|
|
20116
|
+
var parseEscrowBlobPlaintext = /* @__PURE__ */ __name((value) => {
|
|
20117
|
+
if (!isRecord2(value) || value.version !== ESCROW_ENVELOPE_VERSION) {
|
|
20118
|
+
throw new Error("Invalid escrow plaintext");
|
|
20119
|
+
}
|
|
20120
|
+
if (typeof value.shareVersion !== "number" || !Number.isInteger(value.shareVersion) || value.shareVersion <= 0) {
|
|
20121
|
+
throw new Error("Invalid escrow shareVersion");
|
|
20122
|
+
}
|
|
20123
|
+
return {
|
|
20124
|
+
version: ESCROW_ENVELOPE_VERSION,
|
|
20125
|
+
recoveryShare: requireString2(value, "recoveryShare", {
|
|
20126
|
+
min: 5,
|
|
20127
|
+
max: 4096,
|
|
20128
|
+
pattern: /^[0-9a-f]+$/i
|
|
20129
|
+
}).toLowerCase(),
|
|
20130
|
+
did: requireString2(value, "did", { max: 2048, pattern: /^did:/ }),
|
|
20131
|
+
shareVersion: value.shareVersion,
|
|
20132
|
+
...value.pinVerifier !== void 0 ? {
|
|
20133
|
+
pinVerifier: requireString2(value, "pinVerifier", {
|
|
20134
|
+
min: 64,
|
|
20135
|
+
max: 64,
|
|
20136
|
+
pattern: /^[0-9a-f]{64}$/i
|
|
20137
|
+
}).toLowerCase()
|
|
20138
|
+
} : {}
|
|
20139
|
+
};
|
|
20140
|
+
}, "parseEscrowBlobPlaintext");
|
|
20141
|
+
var parseEscrowReleasePlaintext = /* @__PURE__ */ __name((value) => {
|
|
20142
|
+
const plaintext = parseEscrowBlobPlaintext(value);
|
|
20143
|
+
if (!isRecord2(value)) throw new Error("Invalid escrow plaintext");
|
|
20144
|
+
return {
|
|
20145
|
+
...plaintext,
|
|
20146
|
+
holdId: requireString2(value, "holdId", { max: 128, pattern: /^[A-Za-z0-9._-]+$/ })
|
|
20147
|
+
};
|
|
20148
|
+
}, "parseEscrowReleasePlaintext");
|
|
20149
|
+
var getAad2 = /* @__PURE__ */ __name((envelope) => encodeUtf82(`${envelope.version}|${envelope.algorithm}|${envelope.keyId}`), "getAad");
|
|
20150
|
+
var deriveAesKey2 = /* @__PURE__ */ __name(async (sharedSecret, salt, usage, info) => {
|
|
20151
|
+
const keyMaterial = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, [
|
|
20152
|
+
"deriveKey"
|
|
20153
|
+
]);
|
|
20154
|
+
return crypto.subtle.deriveKey(
|
|
20155
|
+
{ name: "HKDF", hash: "SHA-256", salt, info: encodeUtf82(info) },
|
|
20156
|
+
keyMaterial,
|
|
20157
|
+
{ name: "AES-GCM", length: 256 },
|
|
20158
|
+
false,
|
|
20159
|
+
[usage]
|
|
20160
|
+
);
|
|
20161
|
+
}, "deriveAesKey");
|
|
20162
|
+
var generateEscrowKeyPair = /* @__PURE__ */ __name(async () => {
|
|
20163
|
+
const keyPair = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, [
|
|
20164
|
+
"deriveBits"
|
|
20165
|
+
]);
|
|
20166
|
+
const [publicKey, privateKey] = await Promise.all([
|
|
20167
|
+
crypto.subtle.exportKey("spki", keyPair.publicKey),
|
|
20168
|
+
crypto.subtle.exportKey("pkcs8", keyPair.privateKey)
|
|
20169
|
+
]);
|
|
20170
|
+
return { publicKey: bufferToBase64(publicKey), privateKey: bufferToBase64(privateKey) };
|
|
20171
|
+
}, "generateEscrowKeyPair");
|
|
20172
|
+
var encryptToRecipient = /* @__PURE__ */ __name(async (plaintextJson, spki, keyId, info) => {
|
|
20173
|
+
if (typeof spki !== "string" || !spki.trim()) {
|
|
20174
|
+
throw new Error("Escrow public key is not configured");
|
|
20175
|
+
}
|
|
20176
|
+
const recipientKeyId = requireString2({ keyId }, "keyId", {
|
|
20177
|
+
max: 128,
|
|
20178
|
+
pattern: /^[A-Za-z0-9._-]+$/
|
|
20179
|
+
});
|
|
20180
|
+
const recipientPublicKey = await crypto.subtle.importKey(
|
|
20181
|
+
"spki",
|
|
20182
|
+
base64ToBuffer(spki.trim()),
|
|
20183
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20184
|
+
false,
|
|
20185
|
+
[]
|
|
20186
|
+
);
|
|
20187
|
+
const ephemeralKeyPair = await crypto.subtle.generateKey(
|
|
20188
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20189
|
+
true,
|
|
20190
|
+
["deriveBits"]
|
|
20191
|
+
);
|
|
20192
|
+
const sharedSecret = await crypto.subtle.deriveBits(
|
|
20193
|
+
{ name: "ECDH", public: recipientPublicKey },
|
|
20194
|
+
ephemeralKeyPair.privateKey,
|
|
20195
|
+
256
|
|
20196
|
+
);
|
|
20197
|
+
const salt = crypto.getRandomValues(new Uint8Array(32));
|
|
20198
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
20199
|
+
const aesKey = await deriveAesKey2(sharedSecret, salt, "encrypt", info);
|
|
20200
|
+
const metadata = {
|
|
20201
|
+
version: ESCROW_ENVELOPE_VERSION,
|
|
20202
|
+
algorithm: ESCROW_ALGORITHM,
|
|
20203
|
+
keyId: recipientKeyId
|
|
20204
|
+
};
|
|
20205
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
20206
|
+
{ name: "AES-GCM", iv, additionalData: getAad2(metadata) },
|
|
20207
|
+
aesKey,
|
|
20208
|
+
encodeUtf82(plaintextJson)
|
|
20209
|
+
);
|
|
20210
|
+
const ephemeralPublicKey = await crypto.subtle.exportKey("raw", ephemeralKeyPair.publicKey);
|
|
20211
|
+
return {
|
|
20212
|
+
...metadata,
|
|
20213
|
+
ephemeralPublicKey: bufferToBase64(ephemeralPublicKey),
|
|
20214
|
+
salt: bufferToBase64(salt.buffer),
|
|
20215
|
+
iv: bufferToBase64(iv.buffer),
|
|
20216
|
+
ciphertext: bufferToBase64(ciphertext)
|
|
20217
|
+
};
|
|
20218
|
+
}, "encryptToRecipient");
|
|
20219
|
+
var decryptFromEnvelope = /* @__PURE__ */ __name(async (value, pkcs8, info) => {
|
|
20220
|
+
const envelope = parseEscrowEnvelope(value);
|
|
20221
|
+
const privateKey = await crypto.subtle.importKey(
|
|
20222
|
+
"pkcs8",
|
|
20223
|
+
base64ToBuffer(pkcs8.trim()),
|
|
20224
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20225
|
+
false,
|
|
20226
|
+
["deriveBits"]
|
|
20227
|
+
);
|
|
20228
|
+
const ephemeralPublicKey = await crypto.subtle.importKey(
|
|
20229
|
+
"raw",
|
|
20230
|
+
base64ToBuffer(envelope.ephemeralPublicKey),
|
|
20231
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20232
|
+
false,
|
|
20233
|
+
[]
|
|
20234
|
+
);
|
|
20235
|
+
const sharedSecret = await crypto.subtle.deriveBits(
|
|
20236
|
+
{ name: "ECDH", public: ephemeralPublicKey },
|
|
20237
|
+
privateKey,
|
|
20238
|
+
256
|
|
20239
|
+
);
|
|
20240
|
+
const salt = base64ToBuffer(envelope.salt);
|
|
20241
|
+
const iv = base64ToBuffer(envelope.iv);
|
|
20242
|
+
const aesKey = await deriveAesKey2(sharedSecret, salt, "decrypt", info);
|
|
20243
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
20244
|
+
{ name: "AES-GCM", iv, additionalData: getAad2(envelope) },
|
|
20245
|
+
aesKey,
|
|
20246
|
+
base64ToBuffer(envelope.ciphertext)
|
|
20247
|
+
);
|
|
20248
|
+
const parsed = JSON.parse(new TextDecoder().decode(decrypted));
|
|
20249
|
+
return parsed;
|
|
20250
|
+
}, "decryptFromEnvelope");
|
|
20251
|
+
var encryptEscrowBlob = /* @__PURE__ */ __name(async (input, enclavePublicKeySpkiB64, keyId) => {
|
|
20252
|
+
const plaintext = parseEscrowBlobPlaintext({ ...input, version: ESCROW_ENVELOPE_VERSION });
|
|
20253
|
+
return encryptToRecipient(
|
|
20254
|
+
JSON.stringify(plaintext),
|
|
20255
|
+
enclavePublicKeySpkiB64,
|
|
20256
|
+
keyId,
|
|
20257
|
+
ESCROW_BLOB_INFO
|
|
20258
|
+
);
|
|
20259
|
+
}, "encryptEscrowBlob");
|
|
20260
|
+
var decryptEscrowBlob = /* @__PURE__ */ __name(async (envelope, enclavePrivateKeyPkcs8B64) => parseEscrowBlobPlaintext(
|
|
20261
|
+
await decryptFromEnvelope(envelope, enclavePrivateKeyPkcs8B64, ESCROW_BLOB_INFO)
|
|
20262
|
+
), "decryptEscrowBlob");
|
|
20263
|
+
var sealEscrowRelease = /* @__PURE__ */ __name(async (input, clientEphemeralPublicKeySpkiB64) => {
|
|
20264
|
+
const plaintext = parseEscrowReleasePlaintext({ ...input, version: ESCROW_ENVELOPE_VERSION });
|
|
20265
|
+
return encryptToRecipient(
|
|
20266
|
+
JSON.stringify(plaintext),
|
|
20267
|
+
clientEphemeralPublicKeySpkiB64,
|
|
20268
|
+
ESCROW_CLIENT_KEY_ID,
|
|
20269
|
+
ESCROW_RELEASE_INFO
|
|
20270
|
+
);
|
|
20271
|
+
}, "sealEscrowRelease");
|
|
20272
|
+
var openEscrowRelease = /* @__PURE__ */ __name(async (envelope, clientEphemeralPrivateKeyPkcs8B64) => parseEscrowReleasePlaintext(
|
|
20273
|
+
await decryptFromEnvelope(envelope, clientEphemeralPrivateKeyPkcs8B64, ESCROW_RELEASE_INFO)
|
|
20274
|
+
), "openEscrowRelease");
|
|
20275
|
+
|
|
20276
|
+
// src/escrow-attestation.ts
|
|
20277
|
+
var isRecord3 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
|
|
20278
|
+
var normalizePublicKey = /* @__PURE__ */ __name((value) => {
|
|
20279
|
+
const trimmed = value.trim();
|
|
20280
|
+
if (!trimmed || trimmed.length > 512 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(trimmed)) {
|
|
20281
|
+
throw new Error("Invalid escrow attestation public key");
|
|
20282
|
+
}
|
|
20283
|
+
return bufferToBase64(base64ToBuffer(trimmed).buffer);
|
|
20284
|
+
}, "normalizePublicKey");
|
|
20285
|
+
var verifyEnclaveAttestation = /* @__PURE__ */ __name(async (attestation, policy) => {
|
|
20286
|
+
const fields = ["mode", "keyId", "publicKey", "measurements", "document", "issuedAt"];
|
|
20287
|
+
if (!isRecord3(attestation) || Object.keys(attestation).some((key) => !fields.includes(key)) || attestation.mode !== "software" && attestation.mode !== "nitro" || typeof attestation.keyId !== "string" || !/^[A-Za-z0-9._-]{1,128}$/.test(attestation.keyId) || typeof attestation.publicKey !== "string" || !isRecord3(attestation.measurements) || Object.entries(attestation.measurements).some(
|
|
20288
|
+
([key, value]) => !["imageSha384", "pcr0", "pcr1", "pcr2"].includes(key) || typeof value !== "string" || !value || value.length > 1024
|
|
20289
|
+
) || typeof attestation.document !== "string" || !attestation.document || attestation.document.length > 65536 || typeof attestation.issuedAt !== "string" || !Number.isFinite(Date.parse(attestation.issuedAt))) {
|
|
20290
|
+
throw new Error("Invalid escrow attestation");
|
|
20291
|
+
}
|
|
20292
|
+
const publicKey = normalizePublicKey(attestation.publicKey);
|
|
20293
|
+
await crypto.subtle.importKey(
|
|
20294
|
+
"spki",
|
|
20295
|
+
base64ToBuffer(publicKey),
|
|
20296
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
20297
|
+
false,
|
|
20298
|
+
[]
|
|
20299
|
+
);
|
|
20300
|
+
if (policy.mode === "nitro") {
|
|
20301
|
+
throw new Error("Nitro attestation verification is not implemented yet");
|
|
20302
|
+
}
|
|
20303
|
+
if (attestation.mode !== policy.mode) throw new Error("Escrow attestation mode mismatch");
|
|
20304
|
+
if (!policy.pinnedPublicKeys.some((pin) => normalizePublicKey(pin) === publicKey)) {
|
|
20305
|
+
throw new Error("Escrow attestation public key is not trusted");
|
|
20306
|
+
}
|
|
20307
|
+
return { publicKey, keyId: attestation.keyId, mode: attestation.mode };
|
|
20308
|
+
}, "verifyEnclaveAttestation");
|
|
20309
|
+
|
|
20310
|
+
// src/types.ts
|
|
20311
|
+
var import_types = __toESM(require_dist());
|
|
20312
|
+
var EscrowPinUnavailableError = class extends Error {
|
|
20313
|
+
static {
|
|
20314
|
+
__name(this, "EscrowPinUnavailableError");
|
|
20315
|
+
}
|
|
20316
|
+
constructor() {
|
|
20317
|
+
super("PIN recovery is not available for this account.");
|
|
20318
|
+
this.name = "EscrowPinUnavailableError";
|
|
20319
|
+
}
|
|
20320
|
+
};
|
|
20321
|
+
var EscrowPinMismatchError = class extends Error {
|
|
20322
|
+
constructor(attemptsRemaining) {
|
|
20323
|
+
super("Incorrect PIN.");
|
|
20324
|
+
this.attemptsRemaining = attemptsRemaining;
|
|
20325
|
+
this.name = "EscrowPinMismatchError";
|
|
20326
|
+
}
|
|
20327
|
+
static {
|
|
20328
|
+
__name(this, "EscrowPinMismatchError");
|
|
20329
|
+
}
|
|
20330
|
+
};
|
|
20331
|
+
var EscrowPinThrottledError = class extends Error {
|
|
20332
|
+
static {
|
|
20333
|
+
__name(this, "EscrowPinThrottledError");
|
|
20334
|
+
}
|
|
20335
|
+
constructor() {
|
|
20336
|
+
super("Too many tries right now. Wait a minute and try again.");
|
|
20337
|
+
this.name = "EscrowPinThrottledError";
|
|
19136
20338
|
}
|
|
19137
|
-
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
19141
|
-
this.
|
|
20339
|
+
};
|
|
20340
|
+
var EscrowHoldRestartThrottledError = class extends Error {
|
|
20341
|
+
constructor(retryAfter) {
|
|
20342
|
+
super("A recovery request was started recently. Please wait before restarting.");
|
|
20343
|
+
this.retryAfter = retryAfter;
|
|
20344
|
+
this.name = "EscrowHoldRestartThrottledError";
|
|
20345
|
+
}
|
|
20346
|
+
static {
|
|
20347
|
+
__name(this, "EscrowHoldRestartThrottledError");
|
|
19142
20348
|
}
|
|
19143
20349
|
};
|
|
19144
|
-
|
|
19145
|
-
|
|
19146
|
-
|
|
19147
|
-
|
|
20350
|
+
var EscrowPinLockedError = class extends Error {
|
|
20351
|
+
static {
|
|
20352
|
+
__name(this, "EscrowPinLockedError");
|
|
20353
|
+
}
|
|
20354
|
+
constructor() {
|
|
20355
|
+
super("Too many incorrect PIN attempts. You can still recover by waiting.");
|
|
20356
|
+
this.name = "EscrowPinLockedError";
|
|
20357
|
+
}
|
|
20358
|
+
};
|
|
20359
|
+
|
|
20360
|
+
// src/escrow-pin.ts
|
|
20361
|
+
var PIN_MIN_LENGTH = 6;
|
|
20362
|
+
var PIN_MAX_LENGTH = 12;
|
|
20363
|
+
var ESCROW_PIN_MAX_ATTEMPTS = 10;
|
|
20364
|
+
var TRIVIAL_PINS = /* @__PURE__ */ new Set([
|
|
20365
|
+
"123456",
|
|
20366
|
+
"654321",
|
|
20367
|
+
"000000",
|
|
20368
|
+
"111111",
|
|
20369
|
+
"121212",
|
|
20370
|
+
"112233",
|
|
20371
|
+
"123123",
|
|
20372
|
+
"696969",
|
|
20373
|
+
"123321"
|
|
20374
|
+
]);
|
|
20375
|
+
var normalizePin = /* @__PURE__ */ __name((raw) => raw.trim().normalize("NFKD").replace(/\D/g, ""), "normalizePin");
|
|
20376
|
+
var validatePin = /* @__PURE__ */ __name((pin) => {
|
|
20377
|
+
const normalized = normalizePin(pin);
|
|
20378
|
+
if (normalized.length < PIN_MIN_LENGTH || normalized.length > PIN_MAX_LENGTH) {
|
|
20379
|
+
return { ok: false, reason: "length" };
|
|
20380
|
+
}
|
|
20381
|
+
if (/^(\d)\1+$/.test(normalized) || "0123456789".includes(normalized) || "9876543210".includes(normalized) || TRIVIAL_PINS.has(normalized)) {
|
|
20382
|
+
return { ok: false, reason: "trivial" };
|
|
20383
|
+
}
|
|
20384
|
+
return { ok: true };
|
|
20385
|
+
}, "validatePin");
|
|
20386
|
+
var generatePinSalt = /* @__PURE__ */ __name(() => bufferToBase64(crypto.getRandomValues(new Uint8Array(16)).buffer), "generatePinSalt");
|
|
20387
|
+
var derivePinProof = /* @__PURE__ */ __name(async (pin, pinSaltBase64) => {
|
|
20388
|
+
const salt = base64ToBuffer(pinSaltBase64);
|
|
20389
|
+
if (salt.length !== 16) throw new Error("Invalid escrow PIN salt length");
|
|
20390
|
+
const key = await deriveKeyFromPassword(normalizePin(pin), salt);
|
|
20391
|
+
try {
|
|
20392
|
+
return bytesToHex(key);
|
|
20393
|
+
} finally {
|
|
20394
|
+
key.fill(0);
|
|
20395
|
+
}
|
|
20396
|
+
}, "derivePinProof");
|
|
20397
|
+
var constantTimeEqualHex = /* @__PURE__ */ __name((a, b) => {
|
|
20398
|
+
if (a.length !== b.length || !/^(?:[0-9a-f]{2})+$/i.test(a) || !/^(?:[0-9a-f]{2})+$/i.test(b)) {
|
|
20399
|
+
return false;
|
|
20400
|
+
}
|
|
20401
|
+
const aBytes = hexToBytes(a);
|
|
20402
|
+
const bBytes = hexToBytes(b);
|
|
20403
|
+
let difference = 0;
|
|
20404
|
+
for (let i = 0; i < aBytes.length; i++) {
|
|
20405
|
+
difference |= aBytes[i] ^ bBytes[i];
|
|
20406
|
+
}
|
|
20407
|
+
return difference === 0;
|
|
20408
|
+
}, "constantTimeEqualHex");
|
|
19148
20409
|
|
|
19149
20410
|
// src/atomic-operations.ts
|
|
19150
20411
|
var ShareVerificationError = class extends Error {
|
|
@@ -19171,6 +20432,15 @@ var AtomicUpdateError = class extends Error {
|
|
|
19171
20432
|
__name(this, "AtomicUpdateError");
|
|
19172
20433
|
}
|
|
19173
20434
|
};
|
|
20435
|
+
var ShareWriteRejectedError = class extends Error {
|
|
20436
|
+
static {
|
|
20437
|
+
__name(this, "ShareWriteRejectedError");
|
|
20438
|
+
}
|
|
20439
|
+
constructor(message) {
|
|
20440
|
+
super(message);
|
|
20441
|
+
this.name = "ShareWriteRejectedError";
|
|
20442
|
+
}
|
|
20443
|
+
};
|
|
19174
20444
|
async function splitAndVerify(privateKey) {
|
|
19175
20445
|
const shares = await splitPrivateKey(privateKey);
|
|
19176
20446
|
const combinations = [
|
|
@@ -19227,18 +20497,37 @@ async function atomicShareUpdate(privateKey, storage, options = {}) {
|
|
|
19227
20497
|
try {
|
|
19228
20498
|
await storage.storeAuth(newShares.authShare);
|
|
19229
20499
|
} catch (e) {
|
|
19230
|
-
if (
|
|
20500
|
+
if (!(e instanceof ShareWriteRejectedError)) {
|
|
20501
|
+
throw new AtomicUpdateError(
|
|
20502
|
+
"Auth share write outcome unknown; device share preserved for reconciliation",
|
|
20503
|
+
"store_auth",
|
|
20504
|
+
false,
|
|
20505
|
+
e instanceof Error ? e : void 0
|
|
20506
|
+
);
|
|
20507
|
+
}
|
|
20508
|
+
let rolledBack = false;
|
|
20509
|
+
if (deviceStored) {
|
|
19231
20510
|
try {
|
|
19232
|
-
|
|
19233
|
-
|
|
20511
|
+
if (options.previousDeviceShare !== void 0) {
|
|
20512
|
+
await storage.storeDevice(options.previousDeviceShare);
|
|
20513
|
+
options.onRollback?.("Server storage failed, restored previous device share");
|
|
20514
|
+
rolledBack = true;
|
|
20515
|
+
} else if (storage.clearDevice) {
|
|
20516
|
+
await storage.clearDevice();
|
|
20517
|
+
options.onRollback?.("Server storage failed, removed new device share");
|
|
20518
|
+
rolledBack = true;
|
|
20519
|
+
}
|
|
19234
20520
|
} catch (rollbackError) {
|
|
19235
|
-
console.error(
|
|
20521
|
+
console.error(
|
|
20522
|
+
"CRITICAL: Rollback failed after auth share storage failure",
|
|
20523
|
+
rollbackError
|
|
20524
|
+
);
|
|
19236
20525
|
}
|
|
19237
20526
|
}
|
|
19238
20527
|
throw new AtomicUpdateError(
|
|
19239
|
-
"Failed to store auth share on server, rolled back device share",
|
|
20528
|
+
rolledBack ? "Failed to store auth share on server, rolled back device share" : "Failed to store auth share on server; rollback did not complete",
|
|
19240
20529
|
"store_auth",
|
|
19241
|
-
|
|
20530
|
+
rolledBack,
|
|
19242
20531
|
e instanceof Error ? e : void 0
|
|
19243
20532
|
);
|
|
19244
20533
|
}
|
|
@@ -19292,12 +20581,16 @@ async function atomicRecovery(recoveryShare, authShareData, storage, options = {
|
|
|
19292
20581
|
__name(atomicRecovery, "atomicRecovery");
|
|
19293
20582
|
|
|
19294
20583
|
// src/sss-strategy.ts
|
|
20584
|
+
var import_types3 = __toESM(require_dist());
|
|
19295
20585
|
var SSS_DB_NAME = "lcb-sss-keys";
|
|
20586
|
+
var DEFAULT_DEVICE_SHARE_ID2 = "sss-device-share";
|
|
20587
|
+
var MAX_RECONCILIATION_HISTORY = 5;
|
|
19296
20588
|
var defaultStorage = {
|
|
19297
20589
|
storeDeviceShare,
|
|
19298
20590
|
getDeviceShare,
|
|
19299
20591
|
hasDeviceShare,
|
|
19300
20592
|
clearAllShares,
|
|
20593
|
+
deleteDeviceShare,
|
|
19301
20594
|
storeShareVersion,
|
|
19302
20595
|
getShareVersion
|
|
19303
20596
|
};
|
|
@@ -19306,10 +20599,24 @@ var buildHeaders = /* @__PURE__ */ __name((token, didAuthVp, tenantId) => ({
|
|
|
19306
20599
|
...didAuthVp ? { Authorization: `Bearer ${didAuthVp}` } : {},
|
|
19307
20600
|
...tenantId ? { "X-Tenant-Id": tenantId } : {}
|
|
19308
20601
|
}), "buildHeaders");
|
|
20602
|
+
var SERVER_REQUEST_TIMEOUT_MS = 3e4;
|
|
20603
|
+
var requestTimeoutSignal = /* @__PURE__ */ __name((ms = SERVER_REQUEST_TIMEOUT_MS) => typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(ms) : void 0, "requestTimeoutSignal");
|
|
20604
|
+
var describeHttpStatus = /* @__PURE__ */ __name((response) => response.statusText ? `${response.status} ${response.statusText}` : `${response.status}`, "describeHttpStatus");
|
|
20605
|
+
var EscrowRequestError = class extends Error {
|
|
20606
|
+
constructor(status) {
|
|
20607
|
+
super("Escrow request failed");
|
|
20608
|
+
this.status = status;
|
|
20609
|
+
this.name = "EscrowRequestError";
|
|
20610
|
+
}
|
|
20611
|
+
static {
|
|
20612
|
+
__name(this, "EscrowRequestError");
|
|
20613
|
+
}
|
|
20614
|
+
};
|
|
19309
20615
|
var fetchAuthShareRaw = /* @__PURE__ */ __name(async (serverUrl, token, providerType, shareVersion, tenantId) => {
|
|
19310
20616
|
const response = await fetch(`${serverUrl}/keys/auth-share`, {
|
|
19311
20617
|
method: "POST",
|
|
19312
20618
|
headers: buildHeaders(token, void 0, tenantId),
|
|
20619
|
+
signal: requestTimeoutSignal(),
|
|
19313
20620
|
body: JSON.stringify({
|
|
19314
20621
|
authToken: token,
|
|
19315
20622
|
providerType,
|
|
@@ -19318,42 +20625,103 @@ var fetchAuthShareRaw = /* @__PURE__ */ __name(async (serverUrl, token, provider
|
|
|
19318
20625
|
});
|
|
19319
20626
|
if (!response.ok) {
|
|
19320
20627
|
if (response.status === 404) return null;
|
|
19321
|
-
throw new Error(`Failed to fetch key status: ${response
|
|
20628
|
+
throw new Error(`Failed to fetch key status: ${describeHttpStatus(response)}`);
|
|
19322
20629
|
}
|
|
19323
20630
|
return response.json();
|
|
19324
20631
|
}, "fetchAuthShareRaw");
|
|
19325
|
-
var putAuthShare = /* @__PURE__ */ __name(async (serverUrl, token, providerType, authShare, primaryDid, didAuthVp, tenantId) => {
|
|
20632
|
+
var putAuthShare = /* @__PURE__ */ __name(async (serverUrl, token, providerType, authShare, primaryDid, didAuthVp, tenantId, expectedShareVersion) => {
|
|
19326
20633
|
const response = await fetch(`${serverUrl}/keys/auth-share`, {
|
|
19327
20634
|
method: "PUT",
|
|
19328
20635
|
headers: buildHeaders(token, didAuthVp, tenantId),
|
|
20636
|
+
signal: requestTimeoutSignal(),
|
|
19329
20637
|
body: JSON.stringify({
|
|
19330
20638
|
authToken: token,
|
|
19331
20639
|
providerType,
|
|
19332
20640
|
authShare: { encryptedData: authShare, encryptedDek: "", iv: "" },
|
|
19333
20641
|
primaryDid,
|
|
19334
|
-
securityLevel: "basic"
|
|
20642
|
+
securityLevel: "basic",
|
|
20643
|
+
sssActivationState: "provisional",
|
|
20644
|
+
expectedShareVersion
|
|
19335
20645
|
})
|
|
19336
20646
|
});
|
|
19337
20647
|
if (!response.ok) {
|
|
19338
|
-
|
|
20648
|
+
const message = `Failed to store auth share: ${describeHttpStatus(response)}`;
|
|
20649
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 408) {
|
|
20650
|
+
throw new ShareWriteRejectedError(message);
|
|
20651
|
+
}
|
|
20652
|
+
throw new Error(message);
|
|
19339
20653
|
}
|
|
19340
20654
|
const data = await response.json();
|
|
19341
|
-
return {
|
|
20655
|
+
return {
|
|
20656
|
+
shareVersion: data.shareVersion ?? 1,
|
|
20657
|
+
expectedShareVersionChecked: data.expectedShareVersionChecked === true
|
|
20658
|
+
};
|
|
19342
20659
|
}, "putAuthShare");
|
|
20660
|
+
var authShareToString = /* @__PURE__ */ __name((authShare) => {
|
|
20661
|
+
if (typeof authShare === "string") return authShare;
|
|
20662
|
+
if (typeof authShare === "object" && authShare !== null && "encryptedData" in authShare && typeof authShare.encryptedData === "string") {
|
|
20663
|
+
return authShare.encryptedData;
|
|
20664
|
+
}
|
|
20665
|
+
return null;
|
|
20666
|
+
}, "authShareToString");
|
|
20667
|
+
var requestFreshDidAuthVp = /* @__PURE__ */ __name(async (serverUrl, privateKey, did, signDidAuthVp, tenantId) => {
|
|
20668
|
+
const bootstrapVp = await signDidAuthVp(privateKey);
|
|
20669
|
+
const response = await fetch(`${serverUrl}/keys/challenge`, {
|
|
20670
|
+
method: "POST",
|
|
20671
|
+
headers: buildHeaders("", bootstrapVp, tenantId),
|
|
20672
|
+
signal: requestTimeoutSignal(),
|
|
20673
|
+
body: JSON.stringify({ did })
|
|
20674
|
+
});
|
|
20675
|
+
if (!response.ok) {
|
|
20676
|
+
throw new Error(`Failed to request DID challenge: ${describeHttpStatus(response)}`);
|
|
20677
|
+
}
|
|
20678
|
+
const data = await response.json();
|
|
20679
|
+
if (!data.challenge) throw new Error("DID challenge response was empty");
|
|
20680
|
+
return signDidAuthVp(privateKey, data.challenge);
|
|
20681
|
+
}, "requestFreshDidAuthVp");
|
|
19343
20682
|
var fetchRecoveryShare = /* @__PURE__ */ __name(async (serverUrl, token, providerType, type, credentialId, tenantId) => {
|
|
19344
|
-
const params = new URLSearchParams({ type, providerType
|
|
20683
|
+
const params = new URLSearchParams({ type, providerType });
|
|
19345
20684
|
if (credentialId) {
|
|
19346
20685
|
params.set("credentialId", credentialId);
|
|
19347
20686
|
}
|
|
19348
20687
|
const response = await fetch(`${serverUrl}/keys/recovery?${params}`, {
|
|
19349
20688
|
method: "GET",
|
|
19350
|
-
headers:
|
|
20689
|
+
headers: {
|
|
20690
|
+
"Content-Type": "application/json",
|
|
20691
|
+
"X-Auth-Token": token,
|
|
20692
|
+
...tenantId ? { "X-Tenant-Id": tenantId } : {}
|
|
20693
|
+
}
|
|
19351
20694
|
});
|
|
19352
20695
|
if (!response.ok) {
|
|
19353
20696
|
throw new Error(`No ${type} recovery share found`);
|
|
19354
20697
|
}
|
|
19355
20698
|
return response.json();
|
|
19356
20699
|
}, "fetchRecoveryShare");
|
|
20700
|
+
var IdentityRecoverySessionConsumedError = class extends Error {
|
|
20701
|
+
static {
|
|
20702
|
+
__name(this, "IdentityRecoverySessionConsumedError");
|
|
20703
|
+
}
|
|
20704
|
+
constructor(message) {
|
|
20705
|
+
super(message);
|
|
20706
|
+
this.name = "IdentityRecoverySessionConsumedError";
|
|
20707
|
+
}
|
|
20708
|
+
};
|
|
20709
|
+
var postJson = /* @__PURE__ */ __name(async (url, body, headers) => {
|
|
20710
|
+
const response = await fetch(url, {
|
|
20711
|
+
method: "POST",
|
|
20712
|
+
headers,
|
|
20713
|
+
body: JSON.stringify(body)
|
|
20714
|
+
});
|
|
20715
|
+
if (!response.ok) {
|
|
20716
|
+
const data = await response.json().catch(() => null);
|
|
20717
|
+
const message = data?.error?.message || data?.message || response.statusText;
|
|
20718
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 408) {
|
|
20719
|
+
throw new ShareWriteRejectedError(message);
|
|
20720
|
+
}
|
|
20721
|
+
throw new Error(message);
|
|
20722
|
+
}
|
|
20723
|
+
return response.json();
|
|
20724
|
+
}, "postJson");
|
|
19357
20725
|
var postRecoveryMethod = /* @__PURE__ */ __name(async (serverUrl, token, providerType, body, didAuthVp, tenantId) => {
|
|
19358
20726
|
const response = await fetch(`${serverUrl}/keys/recovery`, {
|
|
19359
20727
|
method: "POST",
|
|
@@ -19361,9 +20729,40 @@ var postRecoveryMethod = /* @__PURE__ */ __name(async (serverUrl, token, provide
|
|
|
19361
20729
|
body: JSON.stringify({ authToken: token, providerType, ...body })
|
|
19362
20730
|
});
|
|
19363
20731
|
if (!response.ok) {
|
|
19364
|
-
throw new Error(`Failed to add recovery method: ${response
|
|
20732
|
+
throw new Error(`Failed to add recovery method: ${describeHttpStatus(response)}`);
|
|
19365
20733
|
}
|
|
19366
20734
|
}, "postRecoveryMethod");
|
|
20735
|
+
var postRecoveryConfirmation = /* @__PURE__ */ __name(async (serverUrl, token, providerType, body, didAuthVp, tenantId) => {
|
|
20736
|
+
const response = await fetch(`${serverUrl}/keys/recovery/confirm`, {
|
|
20737
|
+
method: "POST",
|
|
20738
|
+
headers: buildHeaders(token, didAuthVp, tenantId),
|
|
20739
|
+
body: JSON.stringify({ authToken: token, providerType, ...body })
|
|
20740
|
+
});
|
|
20741
|
+
if (!response.ok) {
|
|
20742
|
+
const data = await response.json().catch(() => null);
|
|
20743
|
+
const message = data?.error?.message || data?.message || `Failed to confirm recovery method`;
|
|
20744
|
+
throw new Error(message);
|
|
20745
|
+
}
|
|
20746
|
+
}, "postRecoveryConfirmation");
|
|
20747
|
+
var calculateShareChecksum = /* @__PURE__ */ __name(async (share) => {
|
|
20748
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(share));
|
|
20749
|
+
return bytesToHex(new Uint8Array(digest));
|
|
20750
|
+
}, "calculateShareChecksum");
|
|
20751
|
+
var RECOVERY_PHRASE_CHALLENGE_COUNT = 2;
|
|
20752
|
+
var selectRecoveryPhraseChallengeIndices = /* @__PURE__ */ __name((wordCount, challengeCount = RECOVERY_PHRASE_CHALLENGE_COUNT) => {
|
|
20753
|
+
if (wordCount < challengeCount) {
|
|
20754
|
+
throw new Error("Recovery phrase does not contain enough words for confirmation");
|
|
20755
|
+
}
|
|
20756
|
+
const indices = /* @__PURE__ */ new Set();
|
|
20757
|
+
const limit = 4294967296 - 4294967296 % wordCount;
|
|
20758
|
+
while (indices.size < challengeCount) {
|
|
20759
|
+
const randomValue = crypto.getRandomValues(new Uint32Array(1))[0];
|
|
20760
|
+
if (randomValue === void 0) throw new Error("Failed to choose recovery phrase words");
|
|
20761
|
+
if (randomValue >= limit) continue;
|
|
20762
|
+
indices.add(randomValue % wordCount);
|
|
20763
|
+
}
|
|
20764
|
+
return [...indices].sort((a, b) => a - b);
|
|
20765
|
+
}, "selectRecoveryPhraseChallengeIndices");
|
|
19367
20766
|
var VERSION_PREFIX_LEN = 4;
|
|
19368
20767
|
var formatVersionedEmailShare = /* @__PURE__ */ __name((emailShare, shareVersion) => shareVersion.toString(16).padStart(VERSION_PREFIX_LEN, "0") + emailShare, "formatVersionedEmailShare");
|
|
19369
20768
|
var parseVersionedEmailShare = /* @__PURE__ */ __name((input) => {
|
|
@@ -19376,44 +20775,512 @@ var parseVersionedEmailShare = /* @__PURE__ */ __name((input) => {
|
|
|
19376
20775
|
}
|
|
19377
20776
|
return { share: input, version: void 0 };
|
|
19378
20777
|
}, "parseVersionedEmailShare");
|
|
19379
|
-
var sendEmailBackupShare = /* @__PURE__ */ __name(async (serverUrl, token, providerType, emailShare, email, shareVersion, tenantId) => {
|
|
19380
|
-
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
|
|
19384
|
-
|
|
19385
|
-
|
|
19386
|
-
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19390
|
-
|
|
19391
|
-
|
|
19392
|
-
}
|
|
19393
|
-
}, "sendEmailBackupShare");
|
|
19394
|
-
var sendEmailShareToRecoveryEmail = /* @__PURE__ */ __name(async (serverUrl, token, providerType, emailShare, shareVersion, tenantId) => {
|
|
19395
|
-
const payload = shareVersion != null ? formatVersionedEmailShare(emailShare, shareVersion) : emailShare;
|
|
20778
|
+
var sendEmailBackupShare = /* @__PURE__ */ __name(async (serverUrl, token, providerType, emailShare, email, escrowRelayPublicKey, escrowRelayKeyId, emailBranding, shareVersion, didAuthVp, tenantId) => {
|
|
20779
|
+
const recoveryKey = shareVersion != null ? formatVersionedEmailShare(emailShare, shareVersion) : emailShare;
|
|
20780
|
+
const confirmationCode = generateEmailRelayConfirmationCode();
|
|
20781
|
+
const relayPayload = await encryptEmailRelayPayload(
|
|
20782
|
+
{
|
|
20783
|
+
targetEmail: email,
|
|
20784
|
+
recoveryKey,
|
|
20785
|
+
confirmationCode,
|
|
20786
|
+
branding: emailBranding
|
|
20787
|
+
},
|
|
20788
|
+
escrowRelayPublicKey,
|
|
20789
|
+
escrowRelayKeyId
|
|
20790
|
+
);
|
|
19396
20791
|
const response = await fetch(`${serverUrl}/keys/email-backup`, {
|
|
19397
20792
|
method: "POST",
|
|
19398
|
-
headers: buildHeaders(token,
|
|
20793
|
+
headers: buildHeaders(token, didAuthVp, tenantId),
|
|
19399
20794
|
body: JSON.stringify({
|
|
19400
20795
|
authToken: token,
|
|
19401
20796
|
providerType,
|
|
19402
|
-
|
|
19403
|
-
|
|
20797
|
+
relayPayload,
|
|
20798
|
+
confirmationCode,
|
|
20799
|
+
email,
|
|
20800
|
+
shareVersion
|
|
19404
20801
|
})
|
|
19405
20802
|
});
|
|
19406
20803
|
if (!response.ok) {
|
|
19407
|
-
throw new Error(`Failed to send recovery share
|
|
20804
|
+
throw new Error(`Failed to send recovery share: ${describeHttpStatus(response)}`);
|
|
20805
|
+
}
|
|
20806
|
+
}, "sendEmailBackupShare");
|
|
20807
|
+
var rotationChain = Promise.resolve();
|
|
20808
|
+
var withRotationLock = /* @__PURE__ */ __name((operation) => {
|
|
20809
|
+
const run = rotationChain.then(operation, operation);
|
|
20810
|
+
rotationChain = run.catch(() => void 0);
|
|
20811
|
+
return run;
|
|
20812
|
+
}, "withRotationLock");
|
|
20813
|
+
var pendingShareId = /* @__PURE__ */ __name((storageId) => `sss-pending-share:${storageId ?? DEFAULT_DEVICE_SHARE_ID2}`, "pendingShareId");
|
|
20814
|
+
var PENDING_WRITE_EXPIRY_MS = 10 * 60 * 1e3;
|
|
20815
|
+
var MAX_PENDING_SHARE_CANDIDATES = 8;
|
|
20816
|
+
var readPendingShareCandidates = /* @__PURE__ */ __name(async (storage, storageId) => {
|
|
20817
|
+
const raw = await storage.getDeviceShare(pendingShareId(storageId));
|
|
20818
|
+
if (!raw) return [];
|
|
20819
|
+
if (!raw.trimStart().startsWith("{")) return [{ share: raw, createdAt: 0 }];
|
|
20820
|
+
const data = JSON.parse(raw);
|
|
20821
|
+
if (!data || typeof data !== "object" || !("v" in data) || data.v !== 1 || !("candidates" in data) || !Array.isArray(data.candidates) || !data.candidates.every(
|
|
20822
|
+
(candidate) => candidate !== null && typeof candidate === "object" && "share" in candidate && typeof candidate.share === "string" && "createdAt" in candidate && typeof candidate.createdAt === "number" && Number.isFinite(candidate.createdAt) && candidate.createdAt >= 0
|
|
20823
|
+
))
|
|
20824
|
+
throw new Error("Invalid pending share candidates");
|
|
20825
|
+
return data.candidates;
|
|
20826
|
+
}, "readPendingShareCandidates");
|
|
20827
|
+
var clearPendingShareCandidates = /* @__PURE__ */ __name(async (storage, storageId) => storage.clearAllShares(pendingShareId(storageId)), "clearPendingShareCandidates");
|
|
20828
|
+
var writePendingShareCandidates = /* @__PURE__ */ __name(async (storage, candidates, storageId) => {
|
|
20829
|
+
if (!candidates.length) return clearPendingShareCandidates(storage, storageId);
|
|
20830
|
+
await storage.storeDeviceShare(JSON.stringify({ v: 1, candidates }), pendingShareId(storageId));
|
|
20831
|
+
}, "writePendingShareCandidates");
|
|
20832
|
+
var addPendingShareCandidate = /* @__PURE__ */ __name(async (storage, candidate, storageId) => {
|
|
20833
|
+
const candidates = await readPendingShareCandidates(storage, storageId);
|
|
20834
|
+
if (candidates.length >= MAX_PENDING_SHARE_CANDIDATES) {
|
|
20835
|
+
throw new AtomicUpdateError(
|
|
20836
|
+
"Reconcile the pending share update before writing new shares",
|
|
20837
|
+
"store_device",
|
|
20838
|
+
false
|
|
20839
|
+
);
|
|
20840
|
+
}
|
|
20841
|
+
await writePendingShareCandidates(storage, [...candidates, candidate], storageId);
|
|
20842
|
+
}, "addPendingShareCandidate");
|
|
20843
|
+
var removePendingShareCandidate = /* @__PURE__ */ __name(async (storage, share, storageId) => {
|
|
20844
|
+
const candidates = await readPendingShareCandidates(storage, storageId);
|
|
20845
|
+
await writePendingShareCandidates(
|
|
20846
|
+
storage,
|
|
20847
|
+
candidates.filter((candidate) => candidate.share !== share),
|
|
20848
|
+
storageId
|
|
20849
|
+
);
|
|
20850
|
+
}, "removePendingShareCandidate");
|
|
20851
|
+
var resolvePendingShareCandidates = /* @__PURE__ */ __name(async (storage, storageId, fetchCurrent, expectedDid, didFromPrivateKey, assertActiveUser = () => {
|
|
20852
|
+
}, onPromoted, fetchHistorical) => {
|
|
20853
|
+
const candidates = await readPendingShareCandidates(storage, storageId);
|
|
20854
|
+
const snapshot = await fetchCurrent();
|
|
20855
|
+
assertActiveUser();
|
|
20856
|
+
const { authShare, currentVersion } = snapshot;
|
|
20857
|
+
if (authShare && (currentVersion === null || !Number.isInteger(currentVersion) || currentVersion < 1)) {
|
|
20858
|
+
throw new Error("Current auth share is missing a valid share version");
|
|
20859
|
+
}
|
|
20860
|
+
if (authShare && currentVersion !== null) {
|
|
20861
|
+
for (const candidate of candidates) {
|
|
20862
|
+
const health = await verifyStoredShares(
|
|
20863
|
+
{ getDevice: /* @__PURE__ */ __name(async () => candidate.share, "getDevice"), getAuth: /* @__PURE__ */ __name(async () => authShare, "getAuth") },
|
|
20864
|
+
expectedDid,
|
|
20865
|
+
didFromPrivateKey
|
|
20866
|
+
);
|
|
20867
|
+
assertActiveUser();
|
|
20868
|
+
if (!health.healthy) continue;
|
|
20869
|
+
const privateKey = await reconstructFromShares([candidate.share, authShare]);
|
|
20870
|
+
assertActiveUser();
|
|
20871
|
+
await storage.storeDeviceShare(candidate.share, storageId);
|
|
20872
|
+
await storage.storeShareVersion(currentVersion, storageId);
|
|
20873
|
+
await removePendingShareCandidate(storage, candidate.share, storageId);
|
|
20874
|
+
assertActiveUser();
|
|
20875
|
+
onPromoted?.(snapshot);
|
|
20876
|
+
return { snapshot, privateKey };
|
|
20877
|
+
}
|
|
20878
|
+
}
|
|
20879
|
+
const matched = /* @__PURE__ */ new Set();
|
|
20880
|
+
const history = /* @__PURE__ */ new Map();
|
|
20881
|
+
let historyComplete = true;
|
|
20882
|
+
if (candidates.length && currentVersion !== null) {
|
|
20883
|
+
for (let version = currentVersion - 1; version >= 1 && version >= currentVersion - MAX_RECONCILIATION_HISTORY; version--) {
|
|
20884
|
+
if (!fetchHistorical) {
|
|
20885
|
+
historyComplete = false;
|
|
20886
|
+
break;
|
|
20887
|
+
}
|
|
20888
|
+
try {
|
|
20889
|
+
const share = await fetchHistorical(version);
|
|
20890
|
+
if (share) history.set(version, share);
|
|
20891
|
+
} catch {
|
|
20892
|
+
historyComplete = false;
|
|
20893
|
+
}
|
|
20894
|
+
assertActiveUser();
|
|
20895
|
+
}
|
|
20896
|
+
}
|
|
20897
|
+
const mainShare = await storage.getDeviceShare(storageId);
|
|
20898
|
+
let mainHealthy = false;
|
|
20899
|
+
const retainedShares = [...history.values(), ...authShare ? [authShare] : []];
|
|
20900
|
+
if (mainShare) {
|
|
20901
|
+
for (const share of retainedShares) {
|
|
20902
|
+
const health = await verifyStoredShares(
|
|
20903
|
+
{ getDevice: /* @__PURE__ */ __name(async () => mainShare, "getDevice"), getAuth: /* @__PURE__ */ __name(async () => share, "getAuth") },
|
|
20904
|
+
expectedDid,
|
|
20905
|
+
didFromPrivateKey
|
|
20906
|
+
);
|
|
20907
|
+
assertActiveUser();
|
|
20908
|
+
if (health.healthy) {
|
|
20909
|
+
mainHealthy = true;
|
|
20910
|
+
break;
|
|
20911
|
+
}
|
|
20912
|
+
}
|
|
20913
|
+
}
|
|
20914
|
+
let promoted;
|
|
20915
|
+
for (const candidate of candidates) {
|
|
20916
|
+
for (const [version, share] of history) {
|
|
20917
|
+
const health = await verifyStoredShares(
|
|
20918
|
+
{ getDevice: /* @__PURE__ */ __name(async () => candidate.share, "getDevice"), getAuth: /* @__PURE__ */ __name(async () => share, "getAuth") },
|
|
20919
|
+
expectedDid,
|
|
20920
|
+
didFromPrivateKey
|
|
20921
|
+
);
|
|
20922
|
+
assertActiveUser();
|
|
20923
|
+
if (!health.healthy) continue;
|
|
20924
|
+
matched.add(candidate.share);
|
|
20925
|
+
if (!mainHealthy && !promoted) {
|
|
20926
|
+
await storage.storeDeviceShare(candidate.share, storageId);
|
|
20927
|
+
await storage.storeShareVersion(version, storageId);
|
|
20928
|
+
assertActiveUser();
|
|
20929
|
+
onPromoted?.({ currentVersion: version, authShare: share });
|
|
20930
|
+
promoted = candidate.share;
|
|
20931
|
+
}
|
|
20932
|
+
break;
|
|
20933
|
+
}
|
|
20934
|
+
}
|
|
20935
|
+
const live = candidates.filter(
|
|
20936
|
+
(candidate) => candidate.share !== promoted && (matched.has(candidate.share) || !historyComplete || candidate.createdAt !== 0 && Date.now() - candidate.createdAt <= PENDING_WRITE_EXPIRY_MS)
|
|
20937
|
+
);
|
|
20938
|
+
if (live.length !== candidates.length) {
|
|
20939
|
+
assertActiveUser();
|
|
20940
|
+
await writePendingShareCandidates(storage, live, storageId);
|
|
20941
|
+
}
|
|
20942
|
+
return { snapshot, privateKey: null };
|
|
20943
|
+
}, "resolvePendingShareCandidates");
|
|
20944
|
+
var activeShareUpdates = /* @__PURE__ */ new WeakMap();
|
|
20945
|
+
var shareUpdateStorageOwners = /* @__PURE__ */ new WeakMap();
|
|
20946
|
+
var withShareUpdateLock = /* @__PURE__ */ __name(async (storage, storageId, operation) => {
|
|
20947
|
+
const id = pendingShareId(storageId);
|
|
20948
|
+
const owner = shareUpdateStorageOwners.get(storage) ?? storage;
|
|
20949
|
+
const active = activeShareUpdates.get(owner) ?? /* @__PURE__ */ new Set();
|
|
20950
|
+
activeShareUpdates.set(owner, active);
|
|
20951
|
+
const busy = /* @__PURE__ */ __name(() => new AtomicUpdateError("A share update is already in progress", "store_device", false), "busy");
|
|
20952
|
+
if (active.has(id)) throw busy();
|
|
20953
|
+
active.add(id);
|
|
20954
|
+
try {
|
|
20955
|
+
if (typeof navigator !== "undefined" && navigator.locks) {
|
|
20956
|
+
return await navigator.locks.request(id, { ifAvailable: true }, async (lock) => {
|
|
20957
|
+
if (!lock) throw busy();
|
|
20958
|
+
return operation();
|
|
20959
|
+
});
|
|
20960
|
+
}
|
|
20961
|
+
return await operation();
|
|
20962
|
+
} finally {
|
|
20963
|
+
active.delete(id);
|
|
20964
|
+
}
|
|
20965
|
+
}, "withShareUpdateLock");
|
|
20966
|
+
var persistSharesWithoutLock = /* @__PURE__ */ __name(async (privateKey, serverUrl, token, providerType, primaryDid, storage, storageId, didFromPrivateKey, signDidAuthVp, tenantId, onPromoted, assertActiveUser = () => {
|
|
20967
|
+
}) => {
|
|
20968
|
+
if (primaryDid && didFromPrivateKey) {
|
|
20969
|
+
const derivedDid = await didFromPrivateKey(privateKey);
|
|
20970
|
+
if (derivedDid && derivedDid !== primaryDid) {
|
|
20971
|
+
throw new Error(
|
|
20972
|
+
"rotateShares: key does not match expected DID \u2014 refusing to overwrite server shares"
|
|
20973
|
+
);
|
|
20974
|
+
}
|
|
20975
|
+
}
|
|
20976
|
+
const { snapshot } = await resolvePendingShareCandidates(
|
|
20977
|
+
storage,
|
|
20978
|
+
storageId,
|
|
20979
|
+
async () => {
|
|
20980
|
+
const data = await fetchAuthShareRaw(
|
|
20981
|
+
serverUrl,
|
|
20982
|
+
token,
|
|
20983
|
+
providerType,
|
|
20984
|
+
void 0,
|
|
20985
|
+
tenantId
|
|
20986
|
+
);
|
|
20987
|
+
return {
|
|
20988
|
+
currentVersion: data?.shareVersion ?? null,
|
|
20989
|
+
authShare: authShareToString(data?.authShare)
|
|
20990
|
+
};
|
|
20991
|
+
},
|
|
20992
|
+
primaryDid,
|
|
20993
|
+
didFromPrivateKey ?? (async (key) => key === privateKey ? primaryDid : `${primaryDid}:mismatch`),
|
|
20994
|
+
assertActiveUser,
|
|
20995
|
+
onPromoted,
|
|
20996
|
+
async (version) => authShareToString(
|
|
20997
|
+
(await fetchAuthShareRaw(serverUrl, token, providerType, version, tenantId))?.authShare
|
|
20998
|
+
)
|
|
20999
|
+
);
|
|
21000
|
+
const didAuthVp = signDidAuthVp ? await requestFreshDidAuthVp(serverUrl, privateKey, primaryDid, signDidAuthVp, tenantId) : void 0;
|
|
21001
|
+
assertActiveUser();
|
|
21002
|
+
let shareVersion;
|
|
21003
|
+
let stagedShare = "";
|
|
21004
|
+
const shares = await atomicShareUpdate(privateKey, {
|
|
21005
|
+
storeDevice: /* @__PURE__ */ __name(async (share) => {
|
|
21006
|
+
assertActiveUser();
|
|
21007
|
+
stagedShare = share;
|
|
21008
|
+
await addPendingShareCandidate(storage, { share, createdAt: Date.now() }, storageId);
|
|
21009
|
+
}, "storeDevice"),
|
|
21010
|
+
clearDevice: /* @__PURE__ */ __name(() => removePendingShareCandidate(storage, stagedShare, storageId), "clearDevice"),
|
|
21011
|
+
storeAuth: /* @__PURE__ */ __name(async (share) => {
|
|
21012
|
+
assertActiveUser();
|
|
21013
|
+
const result = await putAuthShare(
|
|
21014
|
+
serverUrl,
|
|
21015
|
+
token,
|
|
21016
|
+
providerType,
|
|
21017
|
+
share,
|
|
21018
|
+
primaryDid,
|
|
21019
|
+
didAuthVp,
|
|
21020
|
+
tenantId,
|
|
21021
|
+
snapshot.currentVersion ?? 0
|
|
21022
|
+
);
|
|
21023
|
+
shareVersion = result.shareVersion;
|
|
21024
|
+
}, "storeAuth")
|
|
21025
|
+
});
|
|
21026
|
+
if (shareVersion === void 0) {
|
|
21027
|
+
throw new Error("Server did not return a share version");
|
|
19408
21028
|
}
|
|
19409
|
-
|
|
21029
|
+
await storage.storeDeviceShare(shares.deviceShare, storageId);
|
|
21030
|
+
await storage.storeShareVersion(shareVersion, storageId);
|
|
21031
|
+
await removePendingShareCandidate(storage, shares.deviceShare, storageId);
|
|
21032
|
+
assertActiveUser();
|
|
21033
|
+
return { shares, shareVersion };
|
|
21034
|
+
}, "persistSharesWithoutLock");
|
|
21035
|
+
var persistSharesAtomically = /* @__PURE__ */ __name((...args) => withShareUpdateLock(
|
|
21036
|
+
args[5],
|
|
21037
|
+
args[6],
|
|
21038
|
+
() => withRotationLock(() => persistSharesWithoutLock(...args))
|
|
21039
|
+
), "persistSharesAtomically");
|
|
19410
21040
|
function createSSSStrategy(config) {
|
|
19411
|
-
const {
|
|
21041
|
+
const {
|
|
21042
|
+
serverUrl,
|
|
21043
|
+
enableEmailBackupShare = false,
|
|
21044
|
+
escrowRelayPublicKey = "",
|
|
21045
|
+
escrowRelayKeyId = "",
|
|
21046
|
+
emailBranding,
|
|
21047
|
+
tenantId
|
|
21048
|
+
} = config;
|
|
19412
21049
|
const storage = config.storage || defaultStorage;
|
|
21050
|
+
let storageGeneration = 0;
|
|
21051
|
+
const localWrites = /* @__PURE__ */ new Set();
|
|
21052
|
+
const guardedStorage = /* @__PURE__ */ __name(() => {
|
|
21053
|
+
const generation = storageGeneration;
|
|
21054
|
+
const write = /* @__PURE__ */ __name((operation) => {
|
|
21055
|
+
if (generation !== storageGeneration) {
|
|
21056
|
+
return Promise.reject(new Error("This recovery request was cancelled."));
|
|
21057
|
+
}
|
|
21058
|
+
const pending = operation();
|
|
21059
|
+
localWrites.add(pending);
|
|
21060
|
+
return pending.finally(() => localWrites.delete(pending));
|
|
21061
|
+
}, "write");
|
|
21062
|
+
const guarded = {
|
|
21063
|
+
...storage,
|
|
21064
|
+
storeDeviceShare: /* @__PURE__ */ __name((share, id) => write(() => storage.storeDeviceShare(share, id)), "storeDeviceShare"),
|
|
21065
|
+
storeShareVersion: /* @__PURE__ */ __name((version, id) => write(() => storage.storeShareVersion(version, id)), "storeShareVersion"),
|
|
21066
|
+
clearAllShares: /* @__PURE__ */ __name((id) => write(() => storage.clearAllShares(id)), "clearAllShares"),
|
|
21067
|
+
deleteDeviceShare: /* @__PURE__ */ __name((id) => write(() => storage.deleteDeviceShare(id)), "deleteDeviceShare")
|
|
21068
|
+
};
|
|
21069
|
+
shareUpdateStorageOwners.set(guarded, storage);
|
|
21070
|
+
return guarded;
|
|
21071
|
+
}, "guardedStorage");
|
|
19413
21072
|
let activeStorageId;
|
|
19414
21073
|
let lastEmailShare;
|
|
19415
21074
|
let lastShareVersion;
|
|
19416
|
-
let
|
|
21075
|
+
let pendingPhraseConfirmation;
|
|
21076
|
+
let pendingBackupConfirmation;
|
|
21077
|
+
let pendingIdentityRecovery;
|
|
21078
|
+
let lastServerSnapshot = null;
|
|
21079
|
+
let escrowEnrollmentInFlight;
|
|
21080
|
+
let unenrolledRotation;
|
|
21081
|
+
const escrowRequest = /* @__PURE__ */ __name(async (path, init, pinAttempt = false) => {
|
|
21082
|
+
const response = await fetch(`${serverUrl}/keys/escrow${path}`, {
|
|
21083
|
+
signal: requestTimeoutSignal(),
|
|
21084
|
+
...init
|
|
21085
|
+
});
|
|
21086
|
+
if (!response.ok) {
|
|
21087
|
+
if (!pinAttempt && path === "/recover" && response.status === 429) {
|
|
21088
|
+
const body = await response.json().catch(() => void 0);
|
|
21089
|
+
const message = body && typeof body === "object" && "message" in body && typeof body.message === "string" ? body.message : "";
|
|
21090
|
+
const retryAfter = /Try again after (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\./.exec(
|
|
21091
|
+
message
|
|
21092
|
+
)?.[1];
|
|
21093
|
+
throw new EscrowHoldRestartThrottledError(
|
|
21094
|
+
retryAfter && Number.isFinite(Date.parse(retryAfter)) ? retryAfter : void 0
|
|
21095
|
+
);
|
|
21096
|
+
}
|
|
21097
|
+
if (pinAttempt && response.status === 429) {
|
|
21098
|
+
const body = await response.json().catch(() => void 0);
|
|
21099
|
+
if (body && typeof body === "object" && "message" in body && body.message === import_types3.ESCROW_PIN_LOCKED_MESSAGE) {
|
|
21100
|
+
throw new EscrowPinLockedError();
|
|
21101
|
+
}
|
|
21102
|
+
throw new EscrowPinThrottledError();
|
|
21103
|
+
}
|
|
21104
|
+
if (pinAttempt && response.status === 403) {
|
|
21105
|
+
const body = await response.json().catch(() => void 0);
|
|
21106
|
+
if (body && typeof body === "object" && "message" in body && body.message === import_types3.ESCROW_PIN_UNAVAILABLE_MESSAGE) {
|
|
21107
|
+
throw new EscrowPinUnavailableError();
|
|
21108
|
+
}
|
|
21109
|
+
if (body && typeof body === "object" && "message" in body && typeof body.message === "string" && body.message.startsWith("Incorrect PIN.")) {
|
|
21110
|
+
const match = import_types3.ESCROW_PIN_MISMATCH_PATTERN.exec(body.message);
|
|
21111
|
+
const remaining = match ? Number(match[1]) : 0;
|
|
21112
|
+
throw new EscrowPinMismatchError(
|
|
21113
|
+
Number.isSafeInteger(remaining) && remaining >= 0 && remaining < ESCROW_PIN_MAX_ATTEMPTS ? remaining : 0
|
|
21114
|
+
);
|
|
21115
|
+
}
|
|
21116
|
+
}
|
|
21117
|
+
throw new EscrowRequestError(response.status);
|
|
21118
|
+
}
|
|
21119
|
+
try {
|
|
21120
|
+
return await response.json();
|
|
21121
|
+
} catch {
|
|
21122
|
+
throw new EscrowRequestError(response.status);
|
|
21123
|
+
}
|
|
21124
|
+
}, "escrowRequest");
|
|
21125
|
+
const fetchVerifiedEnclaveKey = /* @__PURE__ */ __name(async () => {
|
|
21126
|
+
if (!config.escrow?.enabled) throw new Error("Escrow enrollment is disabled");
|
|
21127
|
+
const { attestation } = await escrowRequest("/attestation", {
|
|
21128
|
+
method: "GET",
|
|
21129
|
+
headers: buildHeaders("", void 0, tenantId)
|
|
21130
|
+
});
|
|
21131
|
+
return verifyEnclaveAttestation(attestation, config.escrow.attestation);
|
|
21132
|
+
}, "fetchVerifiedEnclaveKey");
|
|
21133
|
+
const enrollEscrow = /* @__PURE__ */ __name(async (token, providerType, privateKey, primaryDid, shares, shareVersion, signDidAuthVp, verifiedKey, pinMaterial, clearPin = false) => {
|
|
21134
|
+
if (!config.escrow?.enabled) throw new Error("Escrow enrollment is disabled");
|
|
21135
|
+
if (!signDidAuthVp) throw new Error("DID proof signing is required for escrow enrollment");
|
|
21136
|
+
const { publicKey, keyId } = verifiedKey ?? await fetchVerifiedEnclaveKey();
|
|
21137
|
+
const envelope = await encryptEscrowBlob(
|
|
21138
|
+
{
|
|
21139
|
+
recoveryShare: shares.recoveryShare,
|
|
21140
|
+
did: primaryDid,
|
|
21141
|
+
shareVersion,
|
|
21142
|
+
...pinMaterial ? { pinVerifier: pinMaterial.pinVerifier } : {}
|
|
21143
|
+
},
|
|
21144
|
+
publicKey,
|
|
21145
|
+
keyId
|
|
21146
|
+
);
|
|
21147
|
+
const vp = await requestFreshDidAuthVp(
|
|
21148
|
+
serverUrl,
|
|
21149
|
+
privateKey,
|
|
21150
|
+
primaryDid,
|
|
21151
|
+
signDidAuthVp,
|
|
21152
|
+
tenantId
|
|
21153
|
+
);
|
|
21154
|
+
await escrowRequest("", {
|
|
21155
|
+
method: "POST",
|
|
21156
|
+
headers: buildHeaders(token, vp, tenantId),
|
|
21157
|
+
body: JSON.stringify({
|
|
21158
|
+
authToken: token,
|
|
21159
|
+
providerType,
|
|
21160
|
+
envelope,
|
|
21161
|
+
shareVersion,
|
|
21162
|
+
enclaveKeyId: keyId,
|
|
21163
|
+
...pinMaterial ? { pinSalt: pinMaterial.pinSalt } : {},
|
|
21164
|
+
...clearPin ? { clearPin: true } : {}
|
|
21165
|
+
})
|
|
21166
|
+
});
|
|
21167
|
+
}, "enrollEscrow");
|
|
21168
|
+
const tryEnrollEscrow = /* @__PURE__ */ __name(async (...args) => {
|
|
21169
|
+
if (!config.escrow?.enabled) return;
|
|
21170
|
+
try {
|
|
21171
|
+
await enrollEscrow(...args);
|
|
21172
|
+
} catch (err) {
|
|
21173
|
+
try {
|
|
21174
|
+
if (config.onEscrowError) config.onEscrowError(err);
|
|
21175
|
+
else console.warn("SSS: escrow enrollment failed");
|
|
21176
|
+
} catch {
|
|
21177
|
+
console.warn("SSS: escrow error reporting failed");
|
|
21178
|
+
}
|
|
21179
|
+
}
|
|
21180
|
+
}, "tryEnrollEscrow");
|
|
21181
|
+
const ensureEscrowEnrollmentUnguarded = /* @__PURE__ */ __name(async (self, params, storage2, forceRotate = false) => {
|
|
21182
|
+
if (!config.escrow?.enabled) return { enrolled: false, reason: "disabled" };
|
|
21183
|
+
const pin = params.options?.pin;
|
|
21184
|
+
if (pin !== void 0) {
|
|
21185
|
+
const validation = validatePin(pin);
|
|
21186
|
+
if (!validation.ok)
|
|
21187
|
+
throw new Error(
|
|
21188
|
+
validation.reason === "length" ? "PIN must contain 6\u201312 digits." : "Choose a PIN without trivial or sequential patterns."
|
|
21189
|
+
);
|
|
21190
|
+
}
|
|
21191
|
+
const status = await self.fetchServerKeyStatus(params.token, params.providerType);
|
|
21192
|
+
if (status.escrowOptedOut) return { enrolled: false, reason: "opted-out" };
|
|
21193
|
+
if (!forceRotate && pin === void 0 && status.shareVersion !== null && status.recoveryMethods.some(
|
|
21194
|
+
(method) => method.type === "escrow" && method.shareVersion === status.shareVersion && (Boolean(method.confirmedAt) || "confirmationStatus" in method && method.confirmationStatus === "confirmed")
|
|
21195
|
+
))
|
|
21196
|
+
return { enrolled: true, changed: false };
|
|
21197
|
+
if (!status.primaryDid) throw new Error("Cannot enroll escrow without a primary DID");
|
|
21198
|
+
if (!params.signDidAuthVp) {
|
|
21199
|
+
throw new Error("DID proof signing is required for escrow enrollment");
|
|
21200
|
+
}
|
|
21201
|
+
if (!forceRotate && pin === void 0 && unenrolledRotation && unenrolledRotation.shareVersion === status.shareVersion && unenrolledRotation.primaryDid === status.primaryDid) {
|
|
21202
|
+
const retry = unenrolledRotation;
|
|
21203
|
+
await enrollEscrow(
|
|
21204
|
+
params.token,
|
|
21205
|
+
params.providerType,
|
|
21206
|
+
params.privateKey,
|
|
21207
|
+
retry.primaryDid,
|
|
21208
|
+
retry.shares,
|
|
21209
|
+
retry.shareVersion,
|
|
21210
|
+
params.signDidAuthVp,
|
|
21211
|
+
void 0,
|
|
21212
|
+
void 0,
|
|
21213
|
+
retry.clearPin
|
|
21214
|
+
);
|
|
21215
|
+
unenrolledRotation = void 0;
|
|
21216
|
+
return { enrolled: true, changed: true, shareVersion: retry.shareVersion };
|
|
21217
|
+
}
|
|
21218
|
+
const verifiedKey = await fetchVerifiedEnclaveKey();
|
|
21219
|
+
const pinSalt = pin !== void 0 ? generatePinSalt() : void 0;
|
|
21220
|
+
const pinMaterial = pin !== void 0 && pinSalt !== void 0 ? { pinSalt, pinVerifier: await derivePinProof(pin, pinSalt) } : void 0;
|
|
21221
|
+
const storageId = activeStorageId;
|
|
21222
|
+
const primaryDid = status.primaryDid;
|
|
21223
|
+
const { shares, shareVersion } = await withShareUpdateLock(
|
|
21224
|
+
storage2,
|
|
21225
|
+
storageId,
|
|
21226
|
+
() => persistSharesWithoutLock(
|
|
21227
|
+
params.privateKey,
|
|
21228
|
+
serverUrl,
|
|
21229
|
+
params.token,
|
|
21230
|
+
params.providerType,
|
|
21231
|
+
primaryDid,
|
|
21232
|
+
storage2,
|
|
21233
|
+
storageId,
|
|
21234
|
+
void 0,
|
|
21235
|
+
params.signDidAuthVp,
|
|
21236
|
+
tenantId
|
|
21237
|
+
)
|
|
21238
|
+
);
|
|
21239
|
+
lastEmailShare = shares.emailShare;
|
|
21240
|
+
lastShareVersion = shareVersion;
|
|
21241
|
+
lastServerSnapshot = {
|
|
21242
|
+
currentVersion: shareVersion,
|
|
21243
|
+
resolvedVersion: shareVersion,
|
|
21244
|
+
authShare: shares.authShare,
|
|
21245
|
+
primaryDid: status.primaryDid
|
|
21246
|
+
};
|
|
21247
|
+
unenrolledRotation = pinMaterial ? void 0 : { shares, shareVersion, primaryDid: status.primaryDid, clearPin: forceRotate };
|
|
21248
|
+
await enrollEscrow(
|
|
21249
|
+
params.token,
|
|
21250
|
+
params.providerType,
|
|
21251
|
+
params.privateKey,
|
|
21252
|
+
status.primaryDid,
|
|
21253
|
+
shares,
|
|
21254
|
+
shareVersion,
|
|
21255
|
+
params.signDidAuthVp,
|
|
21256
|
+
verifiedKey,
|
|
21257
|
+
pinMaterial,
|
|
21258
|
+
forceRotate
|
|
21259
|
+
);
|
|
21260
|
+
unenrolledRotation = void 0;
|
|
21261
|
+
return { enrolled: true, changed: true, shareVersion };
|
|
21262
|
+
}, "ensureEscrowEnrollmentUnguarded");
|
|
21263
|
+
const recordPromotion = /* @__PURE__ */ __name((primaryDid) => (snapshot) => {
|
|
21264
|
+
lastEmailShare = void 0;
|
|
21265
|
+
lastShareVersion = snapshot.currentVersion ?? void 0;
|
|
21266
|
+
lastServerSnapshot = {
|
|
21267
|
+
...snapshot,
|
|
21268
|
+
resolvedVersion: snapshot.currentVersion,
|
|
21269
|
+
primaryDid
|
|
21270
|
+
};
|
|
21271
|
+
}, "recordPromotion");
|
|
21272
|
+
const captureActiveUserGuard = /* @__PURE__ */ __name(() => {
|
|
21273
|
+
const storageId = activeStorageId;
|
|
21274
|
+
return () => {
|
|
21275
|
+
if (activeStorageId !== storageId) {
|
|
21276
|
+
throw new AtomicUpdateError(
|
|
21277
|
+
"Active account changed during share update",
|
|
21278
|
+
"verify_stored",
|
|
21279
|
+
false
|
|
21280
|
+
);
|
|
21281
|
+
}
|
|
21282
|
+
};
|
|
21283
|
+
}, "captureActiveUserGuard");
|
|
19417
21284
|
return {
|
|
19418
21285
|
name: "sss",
|
|
19419
21286
|
capabilities: {
|
|
@@ -19424,23 +21291,31 @@ function createSSSStrategy(config) {
|
|
|
19424
21291
|
},
|
|
19425
21292
|
// --- User scoping ---
|
|
19426
21293
|
setActiveUser(userId) {
|
|
21294
|
+
if (activeStorageId !== `sss-device-share:${userId}`) storageGeneration++;
|
|
19427
21295
|
activeStorageId = `sss-device-share:${userId}`;
|
|
21296
|
+
lastServerSnapshot = null;
|
|
21297
|
+
pendingPhraseConfirmation = void 0;
|
|
21298
|
+
pendingBackupConfirmation = void 0;
|
|
19428
21299
|
},
|
|
19429
21300
|
// --- Key lifecycle ---
|
|
19430
21301
|
async hasLocalKey() {
|
|
19431
21302
|
if (await storage.hasDeviceShare(activeStorageId)) return true;
|
|
21303
|
+
if ((await readPendingShareCandidates(storage, activeStorageId)).length) return true;
|
|
19432
21304
|
if (activeStorageId) {
|
|
19433
21305
|
return storage.hasDeviceShare();
|
|
19434
21306
|
}
|
|
19435
21307
|
return false;
|
|
19436
21308
|
},
|
|
19437
21309
|
async getLocalKey() {
|
|
19438
|
-
const
|
|
21310
|
+
const storage2 = guardedStorage();
|
|
21311
|
+
const scoped = await storage2.getDeviceShare(activeStorageId);
|
|
19439
21312
|
if (scoped) return scoped;
|
|
21313
|
+
const pending = await readPendingShareCandidates(storage2, activeStorageId);
|
|
21314
|
+
if (pending.length) return pending[pending.length - 1].share;
|
|
19440
21315
|
if (activeStorageId) {
|
|
19441
|
-
const legacy = await
|
|
21316
|
+
const legacy = await storage2.getDeviceShare();
|
|
19442
21317
|
if (legacy) {
|
|
19443
|
-
await
|
|
21318
|
+
await storage2.storeDeviceShare(legacy, activeStorageId);
|
|
19444
21319
|
return legacy;
|
|
19445
21320
|
}
|
|
19446
21321
|
}
|
|
@@ -19449,8 +21324,20 @@ function createSSSStrategy(config) {
|
|
|
19449
21324
|
async storeLocalKey(key) {
|
|
19450
21325
|
return storage.storeDeviceShare(key, activeStorageId);
|
|
19451
21326
|
},
|
|
19452
|
-
async clearLocalKeys() {
|
|
19453
|
-
|
|
21327
|
+
async clearLocalKeys(options) {
|
|
21328
|
+
const storageId = activeStorageId;
|
|
21329
|
+
if (options?.preservePending) {
|
|
21330
|
+
const storage2 = guardedStorage();
|
|
21331
|
+
return withShareUpdateLock(
|
|
21332
|
+
storage2,
|
|
21333
|
+
storageId,
|
|
21334
|
+
() => storage2.deleteDeviceShare(storageId)
|
|
21335
|
+
);
|
|
21336
|
+
}
|
|
21337
|
+
storageGeneration++;
|
|
21338
|
+
await Promise.allSettled(localWrites);
|
|
21339
|
+
await clearPendingShareCandidates(storage, storageId);
|
|
21340
|
+
return storage.clearAllShares(storageId);
|
|
19454
21341
|
},
|
|
19455
21342
|
async splitKey(privateKey) {
|
|
19456
21343
|
const { shares } = await splitAndVerify(privateKey);
|
|
@@ -19474,9 +21361,175 @@ function createSSSStrategy(config) {
|
|
|
19474
21361
|
);
|
|
19475
21362
|
return result.healthy;
|
|
19476
21363
|
},
|
|
21364
|
+
async getFreshDidAuthVp(privateKey, did, signDidAuthVp) {
|
|
21365
|
+
return requestFreshDidAuthVp(serverUrl, privateKey, did, signDidAuthVp, tenantId);
|
|
21366
|
+
},
|
|
21367
|
+
async atomicUpdateShares(params) {
|
|
21368
|
+
const storage2 = guardedStorage();
|
|
21369
|
+
const result = await persistSharesAtomically(
|
|
21370
|
+
params.privateKey,
|
|
21371
|
+
serverUrl,
|
|
21372
|
+
params.token,
|
|
21373
|
+
params.providerType,
|
|
21374
|
+
params.did,
|
|
21375
|
+
storage2,
|
|
21376
|
+
activeStorageId,
|
|
21377
|
+
void 0,
|
|
21378
|
+
params.signDidAuthVp,
|
|
21379
|
+
tenantId,
|
|
21380
|
+
recordPromotion(params.did),
|
|
21381
|
+
captureActiveUserGuard()
|
|
21382
|
+
);
|
|
21383
|
+
lastEmailShare = result.shares.emailShare;
|
|
21384
|
+
lastShareVersion = result.shareVersion;
|
|
21385
|
+
lastServerSnapshot = {
|
|
21386
|
+
currentVersion: result.shareVersion,
|
|
21387
|
+
resolvedVersion: result.shareVersion,
|
|
21388
|
+
authShare: result.shares.authShare,
|
|
21389
|
+
primaryDid: params.did
|
|
21390
|
+
};
|
|
21391
|
+
},
|
|
21392
|
+
async reconcileShares(params) {
|
|
21393
|
+
const storage2 = guardedStorage();
|
|
21394
|
+
const storageId = activeStorageId;
|
|
21395
|
+
const assertActiveUser = /* @__PURE__ */ __name(() => {
|
|
21396
|
+
if (activeStorageId !== storageId) {
|
|
21397
|
+
throw new AtomicUpdateError(
|
|
21398
|
+
"Active account changed during reconciliation",
|
|
21399
|
+
"verify_stored",
|
|
21400
|
+
false
|
|
21401
|
+
);
|
|
21402
|
+
}
|
|
21403
|
+
}, "assertActiveUser");
|
|
21404
|
+
return withShareUpdateLock(storage2, storageId, async () => {
|
|
21405
|
+
let deviceShare = await storage2.getDeviceShare(storageId);
|
|
21406
|
+
const pending = await readPendingShareCandidates(storage2, storageId);
|
|
21407
|
+
if (!deviceShare && !pending.length) return null;
|
|
21408
|
+
const resolution = await resolvePendingShareCandidates(
|
|
21409
|
+
storage2,
|
|
21410
|
+
storageId,
|
|
21411
|
+
async () => {
|
|
21412
|
+
const data = await fetchAuthShareRaw(
|
|
21413
|
+
serverUrl,
|
|
21414
|
+
params.token,
|
|
21415
|
+
params.providerType,
|
|
21416
|
+
void 0,
|
|
21417
|
+
tenantId
|
|
21418
|
+
);
|
|
21419
|
+
return {
|
|
21420
|
+
currentVersion: data?.shareVersion ?? null,
|
|
21421
|
+
authShare: authShareToString(data?.authShare)
|
|
21422
|
+
};
|
|
21423
|
+
},
|
|
21424
|
+
params.expectedDid,
|
|
21425
|
+
params.didFromPrivateKey,
|
|
21426
|
+
assertActiveUser,
|
|
21427
|
+
recordPromotion(params.expectedDid),
|
|
21428
|
+
async (version) => authShareToString(
|
|
21429
|
+
(await fetchAuthShareRaw(
|
|
21430
|
+
serverUrl,
|
|
21431
|
+
params.token,
|
|
21432
|
+
params.providerType,
|
|
21433
|
+
version,
|
|
21434
|
+
tenantId
|
|
21435
|
+
))?.authShare
|
|
21436
|
+
)
|
|
21437
|
+
);
|
|
21438
|
+
assertActiveUser();
|
|
21439
|
+
if (resolution.privateKey) {
|
|
21440
|
+
return { privateKey: resolution.privateKey, did: params.expectedDid };
|
|
21441
|
+
}
|
|
21442
|
+
const { currentVersion, authShare: currentAuthShare } = resolution.snapshot;
|
|
21443
|
+
if (currentVersion === null && !currentAuthShare) return null;
|
|
21444
|
+
deviceShare = await storage2.getDeviceShare(storageId);
|
|
21445
|
+
const localVersion = await storage2.getShareVersion(storageId);
|
|
21446
|
+
if (!deviceShare) return null;
|
|
21447
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
21448
|
+
const addCandidate = /* @__PURE__ */ __name((version, authShare) => {
|
|
21449
|
+
if (version !== null && authShare && !candidates.has(version)) {
|
|
21450
|
+
candidates.set(version, authShare);
|
|
21451
|
+
}
|
|
21452
|
+
}, "addCandidate");
|
|
21453
|
+
addCandidate(
|
|
21454
|
+
lastServerSnapshot?.resolvedVersion ?? null,
|
|
21455
|
+
lastServerSnapshot?.authShare ?? null
|
|
21456
|
+
);
|
|
21457
|
+
addCandidate(currentVersion, currentAuthShare);
|
|
21458
|
+
const versionsToTry = /* @__PURE__ */ new Set();
|
|
21459
|
+
if (localVersion !== null) versionsToTry.add(localVersion);
|
|
21460
|
+
if (currentVersion !== null) {
|
|
21461
|
+
for (let version = currentVersion; version >= 1 && version >= currentVersion - MAX_RECONCILIATION_HISTORY; version--) {
|
|
21462
|
+
versionsToTry.add(version);
|
|
21463
|
+
}
|
|
21464
|
+
}
|
|
21465
|
+
for (const version of versionsToTry) {
|
|
21466
|
+
if (candidates.has(version)) continue;
|
|
21467
|
+
const data = await fetchAuthShareRaw(
|
|
21468
|
+
serverUrl,
|
|
21469
|
+
params.token,
|
|
21470
|
+
params.providerType,
|
|
21471
|
+
version,
|
|
21472
|
+
tenantId
|
|
21473
|
+
);
|
|
21474
|
+
addCandidate(version, authShareToString(data?.authShare));
|
|
21475
|
+
}
|
|
21476
|
+
for (const [candidateVersion, authShare] of candidates) {
|
|
21477
|
+
const health = await verifyStoredShares(
|
|
21478
|
+
{
|
|
21479
|
+
getDevice: /* @__PURE__ */ __name(async () => deviceShare, "getDevice"),
|
|
21480
|
+
getAuth: /* @__PURE__ */ __name(async () => authShare, "getAuth")
|
|
21481
|
+
},
|
|
21482
|
+
params.expectedDid,
|
|
21483
|
+
params.didFromPrivateKey
|
|
21484
|
+
);
|
|
21485
|
+
if (!health.healthy) continue;
|
|
21486
|
+
assertActiveUser();
|
|
21487
|
+
const privateKey = await reconstructFromShares([deviceShare, authShare]);
|
|
21488
|
+
if (candidateVersion === currentVersion) {
|
|
21489
|
+
if (localVersion !== currentVersion) {
|
|
21490
|
+
await storage2.storeShareVersion(candidateVersion, storageId);
|
|
21491
|
+
}
|
|
21492
|
+
assertActiveUser();
|
|
21493
|
+
lastServerSnapshot = {
|
|
21494
|
+
currentVersion,
|
|
21495
|
+
resolvedVersion: candidateVersion,
|
|
21496
|
+
authShare,
|
|
21497
|
+
primaryDid: params.expectedDid
|
|
21498
|
+
};
|
|
21499
|
+
return localVersion === currentVersion ? null : { privateKey, did: params.expectedDid };
|
|
21500
|
+
}
|
|
21501
|
+
const result = await persistSharesWithoutLock(
|
|
21502
|
+
privateKey,
|
|
21503
|
+
serverUrl,
|
|
21504
|
+
params.token,
|
|
21505
|
+
params.providerType,
|
|
21506
|
+
params.expectedDid,
|
|
21507
|
+
storage2,
|
|
21508
|
+
storageId,
|
|
21509
|
+
params.didFromPrivateKey,
|
|
21510
|
+
params.signDidAuthVp,
|
|
21511
|
+
tenantId,
|
|
21512
|
+
recordPromotion(params.expectedDid),
|
|
21513
|
+
assertActiveUser
|
|
21514
|
+
);
|
|
21515
|
+
assertActiveUser();
|
|
21516
|
+
lastEmailShare = result.shares.emailShare;
|
|
21517
|
+
lastShareVersion = result.shareVersion;
|
|
21518
|
+
lastServerSnapshot = {
|
|
21519
|
+
currentVersion: result.shareVersion,
|
|
21520
|
+
resolvedVersion: result.shareVersion,
|
|
21521
|
+
authShare: result.shares.authShare,
|
|
21522
|
+
primaryDid: params.expectedDid
|
|
21523
|
+
};
|
|
21524
|
+
return { privateKey, did: params.expectedDid };
|
|
21525
|
+
}
|
|
21526
|
+
return null;
|
|
21527
|
+
});
|
|
21528
|
+
},
|
|
19477
21529
|
// --- Server communication ---
|
|
19478
21530
|
async fetchServerKeyStatus(token, providerType) {
|
|
19479
|
-
const
|
|
21531
|
+
const storage2 = guardedStorage();
|
|
21532
|
+
const localVersion = await storage2.getShareVersion(activeStorageId);
|
|
19480
21533
|
const data = await fetchAuthShareRaw(
|
|
19481
21534
|
serverUrl,
|
|
19482
21535
|
token,
|
|
@@ -19485,7 +21538,7 @@ function createSSSStrategy(config) {
|
|
|
19485
21538
|
tenantId
|
|
19486
21539
|
);
|
|
19487
21540
|
if (!data) {
|
|
19488
|
-
|
|
21541
|
+
lastServerSnapshot = null;
|
|
19489
21542
|
return {
|
|
19490
21543
|
exists: false,
|
|
19491
21544
|
needsMigration: false,
|
|
@@ -19493,16 +21546,25 @@ function createSSSStrategy(config) {
|
|
|
19493
21546
|
recoveryMethods: [],
|
|
19494
21547
|
authShare: null,
|
|
19495
21548
|
shareVersion: null,
|
|
19496
|
-
maskedRecoveryEmail: null
|
|
21549
|
+
maskedRecoveryEmail: null,
|
|
21550
|
+
sssActivationState: null
|
|
19497
21551
|
};
|
|
19498
21552
|
}
|
|
19499
21553
|
const rawAuthShare = data.authShare;
|
|
19500
|
-
const authShareString =
|
|
21554
|
+
const authShareString = authShareToString(rawAuthShare);
|
|
19501
21555
|
const serverVersion = data.shareVersion ?? null;
|
|
19502
|
-
if (
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
21556
|
+
if (
|
|
21557
|
+
// Version 0 means "no auth share yet" and must never be pinned locally.
|
|
21558
|
+
serverVersion != null && serverVersion > 0 && localVersion == null && !(await readPendingShareCandidates(storage2, activeStorageId)).length
|
|
21559
|
+
) {
|
|
21560
|
+
storage2.storeShareVersion(serverVersion, activeStorageId).catch((e) => console.warn("SSS: failed to backfill local shareVersion", e));
|
|
21561
|
+
}
|
|
21562
|
+
lastServerSnapshot = {
|
|
21563
|
+
currentVersion: serverVersion,
|
|
21564
|
+
resolvedVersion: authShareString ? localVersion ?? serverVersion : null,
|
|
21565
|
+
authShare: authShareString,
|
|
21566
|
+
primaryDid: data.primaryDid || null
|
|
21567
|
+
};
|
|
19506
21568
|
return {
|
|
19507
21569
|
exists: !!rawAuthShare || !!data.keyProvider || !!data.primaryDid,
|
|
19508
21570
|
needsMigration: data.keyProvider === "web3auth",
|
|
@@ -19510,35 +21572,258 @@ function createSSSStrategy(config) {
|
|
|
19510
21572
|
recoveryMethods: data.recoveryMethods || [],
|
|
19511
21573
|
authShare: authShareString,
|
|
19512
21574
|
shareVersion: serverVersion,
|
|
19513
|
-
maskedRecoveryEmail: data.maskedRecoveryEmail ?? null
|
|
21575
|
+
maskedRecoveryEmail: data.maskedRecoveryEmail ?? null,
|
|
21576
|
+
escrowOptedOut: data.escrowOptedOut === true,
|
|
21577
|
+
escrowPin: data.escrowPin,
|
|
21578
|
+
sssActivationState: data.sssActivationState ?? "active"
|
|
19514
21579
|
};
|
|
19515
21580
|
},
|
|
19516
21581
|
async storeAuthShare(token, providerType, authShare, primaryDid, didAuthVp) {
|
|
19517
|
-
const
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
|
|
19521
|
-
|
|
19522
|
-
|
|
19523
|
-
|
|
19524
|
-
|
|
21582
|
+
const storage2 = guardedStorage();
|
|
21583
|
+
const { shareVersion } = await withRotationLock(
|
|
21584
|
+
() => putAuthShare(
|
|
21585
|
+
serverUrl,
|
|
21586
|
+
token,
|
|
21587
|
+
providerType,
|
|
21588
|
+
authShare,
|
|
21589
|
+
primaryDid,
|
|
21590
|
+
didAuthVp,
|
|
21591
|
+
tenantId
|
|
21592
|
+
)
|
|
19525
21593
|
);
|
|
19526
|
-
await
|
|
21594
|
+
await storage2.storeShareVersion(shareVersion, activeStorageId);
|
|
19527
21595
|
lastShareVersion = shareVersion;
|
|
19528
21596
|
},
|
|
19529
|
-
async markMigrated(token, providerType, didAuthVp) {
|
|
19530
|
-
const response = await fetch(`${serverUrl}/keys/migrate`, {
|
|
21597
|
+
async markMigrated(token, providerType, didAuthVp) {
|
|
21598
|
+
const response = await fetch(`${serverUrl}/keys/migrate`, {
|
|
21599
|
+
method: "POST",
|
|
21600
|
+
headers: buildHeaders(token, didAuthVp, tenantId),
|
|
21601
|
+
body: JSON.stringify({ authToken: token, providerType })
|
|
21602
|
+
});
|
|
21603
|
+
if (!response.ok) {
|
|
21604
|
+
throw new Error(`Failed to mark migrated: ${describeHttpStatus(response)}`);
|
|
21605
|
+
}
|
|
21606
|
+
},
|
|
21607
|
+
async activate(token, providerType, didAuthVp) {
|
|
21608
|
+
const response = await fetch(`${serverUrl}/keys/activate`, {
|
|
19531
21609
|
method: "POST",
|
|
19532
21610
|
headers: buildHeaders(token, didAuthVp, tenantId),
|
|
19533
21611
|
body: JSON.stringify({ authToken: token, providerType })
|
|
19534
21612
|
});
|
|
19535
21613
|
if (!response.ok) {
|
|
19536
|
-
throw new Error(`Failed to
|
|
21614
|
+
throw new Error(`Failed to activate SSS key: ${describeHttpStatus(response)}`);
|
|
19537
21615
|
}
|
|
19538
21616
|
},
|
|
19539
21617
|
// --- Recovery execution ---
|
|
21618
|
+
async getEscrowEnrollmentState(params) {
|
|
21619
|
+
if (!config.escrow?.enabled) return { state: "disabled" };
|
|
21620
|
+
const status = await this.fetchServerKeyStatus(params.token, params.providerType);
|
|
21621
|
+
if (status.escrowOptedOut) return { state: "opted-out", escrowPin: status.escrowPin };
|
|
21622
|
+
const state = status.shareVersion !== null && status.recoveryMethods.some(
|
|
21623
|
+
(method) => method.type === "escrow" && method.shareVersion === status.shareVersion && (Boolean(method.confirmedAt) || "confirmationStatus" in method && method.confirmationStatus === "confirmed")
|
|
21624
|
+
) ? "enrolled" : "not-enrolled";
|
|
21625
|
+
return { state, escrowPin: status.escrowPin };
|
|
21626
|
+
},
|
|
21627
|
+
async disableEscrowRecovery(params) {
|
|
21628
|
+
const status = await this.fetchServerKeyStatus(params.token, params.providerType);
|
|
21629
|
+
if (!status.primaryDid) throw new Error("Cannot disable escrow without a primary DID");
|
|
21630
|
+
const vp = await requestFreshDidAuthVp(
|
|
21631
|
+
serverUrl,
|
|
21632
|
+
params.privateKey,
|
|
21633
|
+
status.primaryDid,
|
|
21634
|
+
params.signDidAuthVp,
|
|
21635
|
+
tenantId
|
|
21636
|
+
);
|
|
21637
|
+
await escrowRequest("", {
|
|
21638
|
+
method: "DELETE",
|
|
21639
|
+
headers: buildHeaders(params.token, vp, tenantId),
|
|
21640
|
+
body: JSON.stringify({
|
|
21641
|
+
authToken: params.token,
|
|
21642
|
+
providerType: params.providerType,
|
|
21643
|
+
optOut: true
|
|
21644
|
+
})
|
|
21645
|
+
});
|
|
21646
|
+
},
|
|
21647
|
+
async enableEscrowRecovery(params) {
|
|
21648
|
+
const storage2 = guardedStorage();
|
|
21649
|
+
return withRotationLock(async () => {
|
|
21650
|
+
if (!config.escrow?.enabled)
|
|
21651
|
+
return { enrolled: false, reason: "disabled" };
|
|
21652
|
+
const status = await this.fetchServerKeyStatus(params.token, params.providerType);
|
|
21653
|
+
if (!status.primaryDid)
|
|
21654
|
+
throw new Error("Cannot enable escrow without a primary DID");
|
|
21655
|
+
const vp = await requestFreshDidAuthVp(
|
|
21656
|
+
serverUrl,
|
|
21657
|
+
params.privateKey,
|
|
21658
|
+
status.primaryDid,
|
|
21659
|
+
params.signDidAuthVp,
|
|
21660
|
+
tenantId
|
|
21661
|
+
);
|
|
21662
|
+
await escrowRequest("/opt-in", {
|
|
21663
|
+
method: "POST",
|
|
21664
|
+
headers: buildHeaders(params.token, vp, tenantId),
|
|
21665
|
+
body: JSON.stringify({
|
|
21666
|
+
authToken: params.token,
|
|
21667
|
+
providerType: params.providerType
|
|
21668
|
+
})
|
|
21669
|
+
});
|
|
21670
|
+
return ensureEscrowEnrollmentUnguarded(this, params, storage2);
|
|
21671
|
+
});
|
|
21672
|
+
},
|
|
21673
|
+
async setEscrowPin({ pin, ...params }) {
|
|
21674
|
+
const storage2 = guardedStorage();
|
|
21675
|
+
const result = await withRotationLock(
|
|
21676
|
+
() => ensureEscrowEnrollmentUnguarded(this, { ...params, options: { pin } }, storage2)
|
|
21677
|
+
);
|
|
21678
|
+
if (!result.enrolled) {
|
|
21679
|
+
throw new Error("Automatic recovery is not available for this account.");
|
|
21680
|
+
}
|
|
21681
|
+
},
|
|
21682
|
+
async clearEscrowPin(params) {
|
|
21683
|
+
const storage2 = guardedStorage();
|
|
21684
|
+
const result = await withRotationLock(
|
|
21685
|
+
() => ensureEscrowEnrollmentUnguarded(this, params, storage2, true)
|
|
21686
|
+
);
|
|
21687
|
+
if (!result.enrolled) {
|
|
21688
|
+
throw new Error("Automatic recovery is not available for this account.");
|
|
21689
|
+
}
|
|
21690
|
+
},
|
|
21691
|
+
/** Enroll missing/stale escrow material using the standard atomic rotation. */
|
|
21692
|
+
async ensureEscrowEnrollment(params) {
|
|
21693
|
+
const storage2 = guardedStorage();
|
|
21694
|
+
if (params.options?.pin !== void 0)
|
|
21695
|
+
return withRotationLock(
|
|
21696
|
+
() => ensureEscrowEnrollmentUnguarded(this, params, storage2)
|
|
21697
|
+
);
|
|
21698
|
+
if (escrowEnrollmentInFlight) return escrowEnrollmentInFlight;
|
|
21699
|
+
escrowEnrollmentInFlight = withRotationLock(
|
|
21700
|
+
() => ensureEscrowEnrollmentUnguarded(this, params, storage2)
|
|
21701
|
+
);
|
|
21702
|
+
try {
|
|
21703
|
+
return await escrowEnrollmentInFlight;
|
|
21704
|
+
} finally {
|
|
21705
|
+
escrowEnrollmentInFlight = void 0;
|
|
21706
|
+
}
|
|
21707
|
+
},
|
|
21708
|
+
/** Start or explicitly restart a hold with fresh client-bound resume secrets. */
|
|
21709
|
+
async startEscrowRecovery(params) {
|
|
21710
|
+
const sessionProof = params.recoverySessionToken !== void 0;
|
|
21711
|
+
if (sessionProof ? params.token !== void 0 || params.providerType !== void 0 : !params.token || !params.providerType) {
|
|
21712
|
+
throw new Error("Provide exactly one identity proof");
|
|
21713
|
+
}
|
|
21714
|
+
const pair = await generateEscrowKeyPair();
|
|
21715
|
+
const result = await escrowRequest(
|
|
21716
|
+
"/recover",
|
|
21717
|
+
{
|
|
21718
|
+
method: "POST",
|
|
21719
|
+
headers: buildHeaders("", void 0, params.tenantId ?? tenantId),
|
|
21720
|
+
body: JSON.stringify({
|
|
21721
|
+
clientEphemeralPublicKey: pair.publicKey,
|
|
21722
|
+
...params.options?.restart ? { restart: true } : {},
|
|
21723
|
+
...params.options?.releasePolicy ? { releasePolicy: params.options.releasePolicy } : {},
|
|
21724
|
+
...sessionProof ? { recoverySessionToken: params.recoverySessionToken } : { authToken: params.token, providerType: params.providerType }
|
|
21725
|
+
})
|
|
21726
|
+
},
|
|
21727
|
+
params.options?.releasePolicy === "pin"
|
|
21728
|
+
);
|
|
21729
|
+
return { ...result, clientEphemeralPrivateKey: pair.privateKey };
|
|
21730
|
+
},
|
|
21731
|
+
/** Read hold status; secrets (auth or resume token) never travel in a URL. */
|
|
21732
|
+
async getEscrowRecoveryStatus(params) {
|
|
21733
|
+
const query = new URLSearchParams(
|
|
21734
|
+
"holdId" in params ? { holdId: params.holdId } : { providerType: params.providerType }
|
|
21735
|
+
);
|
|
21736
|
+
const secret = "holdId" in params ? params.resumeToken : params.token;
|
|
21737
|
+
const result = await escrowRequest(
|
|
21738
|
+
`/status?${query}`,
|
|
21739
|
+
{
|
|
21740
|
+
method: "GET",
|
|
21741
|
+
headers: {
|
|
21742
|
+
...buildHeaders("", void 0, tenantId),
|
|
21743
|
+
"X-Auth-Token": secret
|
|
21744
|
+
}
|
|
21745
|
+
}
|
|
21746
|
+
);
|
|
21747
|
+
return result.hold;
|
|
21748
|
+
},
|
|
21749
|
+
/** Cancel a pending hold with a fresh DID-owner proof. */
|
|
21750
|
+
async cancelEscrowRecovery(params) {
|
|
21751
|
+
const status = await this.fetchServerKeyStatus(params.token, params.providerType);
|
|
21752
|
+
if (!status.primaryDid) throw new Error("Cannot cancel escrow without a primary DID");
|
|
21753
|
+
const vp = await requestFreshDidAuthVp(
|
|
21754
|
+
serverUrl,
|
|
21755
|
+
params.privateKey,
|
|
21756
|
+
status.primaryDid,
|
|
21757
|
+
params.signDidAuthVp,
|
|
21758
|
+
tenantId
|
|
21759
|
+
);
|
|
21760
|
+
const result = await escrowRequest("/cancel", {
|
|
21761
|
+
method: "POST",
|
|
21762
|
+
headers: buildHeaders(params.token, vp, tenantId),
|
|
21763
|
+
body: JSON.stringify({
|
|
21764
|
+
authToken: params.token,
|
|
21765
|
+
providerType: params.providerType
|
|
21766
|
+
})
|
|
21767
|
+
});
|
|
21768
|
+
return { cancelled: result.cancelled };
|
|
21769
|
+
},
|
|
19540
21770
|
async executeRecovery(params) {
|
|
19541
|
-
const
|
|
21771
|
+
const storage2 = guardedStorage();
|
|
21772
|
+
const { token, providerType, input, didFromPrivateKey, signDidAuthVp } = params;
|
|
21773
|
+
if (input.method === "escrow" || input.method === "escrow-pin") {
|
|
21774
|
+
if (!didFromPrivateKey)
|
|
21775
|
+
throw new Error("DID verification is required for escrow recovery");
|
|
21776
|
+
const start = input.method === "escrow-pin" ? await this.startEscrowRecovery({
|
|
21777
|
+
token,
|
|
21778
|
+
providerType,
|
|
21779
|
+
options: { releasePolicy: "pin" }
|
|
21780
|
+
}) : input;
|
|
21781
|
+
let pinProof;
|
|
21782
|
+
if (input.method === "escrow-pin") {
|
|
21783
|
+
if (!("pinSalt" in start) || !start.pinSalt || !start.resumeToken)
|
|
21784
|
+
throw new Error("PIN recovery response is missing required material");
|
|
21785
|
+
pinProof = await derivePinProof(input.pin, start.pinSalt);
|
|
21786
|
+
}
|
|
21787
|
+
const material = await escrowRequest(
|
|
21788
|
+
"/complete",
|
|
21789
|
+
{
|
|
21790
|
+
method: "POST",
|
|
21791
|
+
headers: buildHeaders("", void 0, tenantId),
|
|
21792
|
+
body: JSON.stringify({
|
|
21793
|
+
holdId: start.holdId,
|
|
21794
|
+
resumeToken: start.resumeToken,
|
|
21795
|
+
...pinProof ? { pinProof } : {}
|
|
21796
|
+
})
|
|
21797
|
+
},
|
|
21798
|
+
input.method === "escrow-pin"
|
|
21799
|
+
);
|
|
21800
|
+
const plaintext = await openEscrowRelease(
|
|
21801
|
+
material.sealedShare,
|
|
21802
|
+
start.clientEphemeralPrivateKey
|
|
21803
|
+
);
|
|
21804
|
+
if (plaintext.holdId !== start.holdId || plaintext.did !== material.primaryDid || plaintext.shareVersion !== material.shareVersion) {
|
|
21805
|
+
throw new Error("Escrow recovery material does not match the request");
|
|
21806
|
+
}
|
|
21807
|
+
const authShare = material.authShare.encryptedData;
|
|
21808
|
+
const privateKey2 = await reconstructFromShares([
|
|
21809
|
+
plaintext.recoveryShare,
|
|
21810
|
+
authShare
|
|
21811
|
+
]);
|
|
21812
|
+
if (!material.primaryDid || await didFromPrivateKey(privateKey2) !== material.primaryDid) {
|
|
21813
|
+
throw new Error(
|
|
21814
|
+
"Recovery produced an incorrect key. Try another recovery method."
|
|
21815
|
+
);
|
|
21816
|
+
}
|
|
21817
|
+
pendingIdentityRecovery = {
|
|
21818
|
+
privateKey: privateKey2,
|
|
21819
|
+
primaryDid: material.primaryDid,
|
|
21820
|
+
recoveryShare: plaintext.recoveryShare,
|
|
21821
|
+
authShare,
|
|
21822
|
+
rebindSessionToken: material.rebindSessionToken
|
|
21823
|
+
};
|
|
21824
|
+
if (!token) return { privateKey: privateKey2, did: material.primaryDid };
|
|
21825
|
+
return this.completeIdentityRecovery({ token, providerType, signDidAuthVp });
|
|
21826
|
+
}
|
|
19542
21827
|
let recoveryShare;
|
|
19543
21828
|
let recoveryShareVersion;
|
|
19544
21829
|
switch (input.method) {
|
|
@@ -19632,14 +21917,61 @@ function createSSSStrategy(config) {
|
|
|
19632
21917
|
);
|
|
19633
21918
|
}
|
|
19634
21919
|
}
|
|
19635
|
-
await
|
|
19636
|
-
|
|
19637
|
-
|
|
21920
|
+
const { shares, shareVersion } = await persistSharesAtomically(
|
|
21921
|
+
privateKey,
|
|
21922
|
+
serverUrl,
|
|
21923
|
+
token,
|
|
21924
|
+
providerType,
|
|
21925
|
+
primaryDid,
|
|
21926
|
+
storage2,
|
|
21927
|
+
activeStorageId,
|
|
21928
|
+
didFromPrivateKey,
|
|
21929
|
+
signDidAuthVp,
|
|
21930
|
+
tenantId,
|
|
21931
|
+
recordPromotion(primaryDid),
|
|
21932
|
+
captureActiveUserGuard()
|
|
21933
|
+
);
|
|
21934
|
+
lastEmailShare = shares.emailShare;
|
|
21935
|
+
lastShareVersion = shareVersion;
|
|
21936
|
+
lastServerSnapshot = {
|
|
21937
|
+
currentVersion: shareVersion,
|
|
21938
|
+
resolvedVersion: shareVersion,
|
|
21939
|
+
authShare: shares.authShare,
|
|
21940
|
+
primaryDid
|
|
21941
|
+
};
|
|
21942
|
+
await tryEnrollEscrow(
|
|
21943
|
+
token,
|
|
21944
|
+
providerType,
|
|
21945
|
+
privateKey,
|
|
21946
|
+
primaryDid,
|
|
21947
|
+
shares,
|
|
21948
|
+
shareVersion,
|
|
21949
|
+
signDidAuthVp
|
|
21950
|
+
);
|
|
19638
21951
|
return { privateKey, did: primaryDid };
|
|
19639
21952
|
},
|
|
19640
21953
|
// --- Recovery setup ---
|
|
19641
21954
|
async setupRecoveryMethod(params) {
|
|
21955
|
+
const storage2 = guardedStorage();
|
|
19642
21956
|
const { token, providerType, privateKey, input, authUser, signDidAuthVp } = params;
|
|
21957
|
+
if (input.method === "escrow" && input.pin !== void 0) {
|
|
21958
|
+
if (!signDidAuthVp)
|
|
21959
|
+
throw new Error("DID proof signing is required for escrow enrollment");
|
|
21960
|
+
const result = await this.enableEscrowRecovery({
|
|
21961
|
+
token,
|
|
21962
|
+
providerType,
|
|
21963
|
+
privateKey,
|
|
21964
|
+
signDidAuthVp,
|
|
21965
|
+
options: { pin: input.pin }
|
|
21966
|
+
});
|
|
21967
|
+
if (!result.enrolled || !result.changed)
|
|
21968
|
+
throw new Error("Escrow enrollment is disabled");
|
|
21969
|
+
return { method: "escrow", shareVersion: result.shareVersion };
|
|
21970
|
+
}
|
|
21971
|
+
if (input.method === "escrow" && !config.escrow?.enabled) {
|
|
21972
|
+
throw new Error("Escrow enrollment is disabled");
|
|
21973
|
+
}
|
|
21974
|
+
const escrowKey = input.method === "escrow" ? await fetchVerifiedEnclaveKey() : void 0;
|
|
19643
21975
|
let passkeyCredential;
|
|
19644
21976
|
if (input.method === "passkey") {
|
|
19645
21977
|
if (!isWebAuthnSupported()) {
|
|
@@ -19649,10 +21981,6 @@ function createSSSStrategy(config) {
|
|
|
19649
21981
|
const userName = authUser?.email || authUser?.phone || authUser?.id || "";
|
|
19650
21982
|
passkeyCredential = await createPasskeyCredential(userId, userName);
|
|
19651
21983
|
}
|
|
19652
|
-
const vpJwt = signDidAuthVp ? await signDidAuthVp(privateKey) : void 0;
|
|
19653
|
-
const { shares } = await splitAndVerify(privateKey);
|
|
19654
|
-
lastEmailShare = shares.emailShare;
|
|
19655
|
-
await storage.storeDeviceShare(shares.deviceShare, activeStorageId);
|
|
19656
21984
|
const serverData = await fetchAuthShareRaw(
|
|
19657
21985
|
serverUrl,
|
|
19658
21986
|
token,
|
|
@@ -19661,39 +21989,42 @@ function createSSSStrategy(config) {
|
|
|
19661
21989
|
tenantId
|
|
19662
21990
|
);
|
|
19663
21991
|
const primaryDid = serverData?.primaryDid || "";
|
|
19664
|
-
|
|
21992
|
+
if (!primaryDid) throw new Error("Cannot set up recovery without a primary DID");
|
|
21993
|
+
const { shares, shareVersion } = await persistSharesAtomically(
|
|
21994
|
+
privateKey,
|
|
19665
21995
|
serverUrl,
|
|
19666
21996
|
token,
|
|
19667
21997
|
providerType,
|
|
19668
|
-
shares.authShare,
|
|
19669
21998
|
primaryDid,
|
|
19670
|
-
|
|
19671
|
-
|
|
21999
|
+
storage2,
|
|
22000
|
+
activeStorageId,
|
|
22001
|
+
void 0,
|
|
22002
|
+
signDidAuthVp,
|
|
22003
|
+
tenantId,
|
|
22004
|
+
recordPromotion(primaryDid),
|
|
22005
|
+
captureActiveUserGuard()
|
|
19672
22006
|
);
|
|
19673
|
-
|
|
19674
|
-
|
|
19675
|
-
|
|
19676
|
-
|
|
19677
|
-
|
|
19678
|
-
|
|
19679
|
-
|
|
19680
|
-
|
|
19681
|
-
tenantId
|
|
19682
|
-
) : authUser?.email ? sendEmailBackupShare(
|
|
19683
|
-
serverUrl,
|
|
19684
|
-
token,
|
|
19685
|
-
providerType,
|
|
19686
|
-
lastEmailShare,
|
|
19687
|
-
authUser.email,
|
|
19688
|
-
shareVersion,
|
|
19689
|
-
tenantId
|
|
19690
|
-
) : Promise.resolve();
|
|
19691
|
-
resend.catch(
|
|
19692
|
-
(e) => console.warn("Email backup share re-send failed (non-fatal):", e)
|
|
19693
|
-
);
|
|
19694
|
-
lastEmailShare = void 0;
|
|
19695
|
-
}
|
|
22007
|
+
lastEmailShare = shares.emailShare;
|
|
22008
|
+
lastShareVersion = shareVersion;
|
|
22009
|
+
lastServerSnapshot = {
|
|
22010
|
+
currentVersion: shareVersion,
|
|
22011
|
+
resolvedVersion: shareVersion,
|
|
22012
|
+
authShare: shares.authShare,
|
|
22013
|
+
primaryDid
|
|
22014
|
+
};
|
|
19696
22015
|
switch (input.method) {
|
|
22016
|
+
case "escrow":
|
|
22017
|
+
await enrollEscrow(
|
|
22018
|
+
token,
|
|
22019
|
+
providerType,
|
|
22020
|
+
privateKey,
|
|
22021
|
+
primaryDid,
|
|
22022
|
+
shares,
|
|
22023
|
+
shareVersion,
|
|
22024
|
+
signDidAuthVp,
|
|
22025
|
+
escrowKey
|
|
22026
|
+
);
|
|
22027
|
+
return { method: "escrow", shareVersion };
|
|
19697
22028
|
case "passkey": {
|
|
19698
22029
|
const credential = passkeyCredential;
|
|
19699
22030
|
const encryptedShare = await encryptShareWithPasskey(
|
|
@@ -19713,9 +22044,38 @@ function createSSSStrategy(config) {
|
|
|
19713
22044
|
credentialId: credential.credentialId,
|
|
19714
22045
|
shareVersion
|
|
19715
22046
|
},
|
|
19716
|
-
|
|
22047
|
+
signDidAuthVp ? await requestFreshDidAuthVp(
|
|
22048
|
+
serverUrl,
|
|
22049
|
+
privateKey,
|
|
22050
|
+
primaryDid,
|
|
22051
|
+
signDidAuthVp,
|
|
22052
|
+
tenantId
|
|
22053
|
+
) : void 0,
|
|
19717
22054
|
tenantId
|
|
19718
22055
|
);
|
|
22056
|
+
await postRecoveryConfirmation(
|
|
22057
|
+
serverUrl,
|
|
22058
|
+
token,
|
|
22059
|
+
providerType,
|
|
22060
|
+
{ type: "passkey", credentialId: credential.credentialId },
|
|
22061
|
+
signDidAuthVp ? await requestFreshDidAuthVp(
|
|
22062
|
+
serverUrl,
|
|
22063
|
+
privateKey,
|
|
22064
|
+
primaryDid,
|
|
22065
|
+
signDidAuthVp,
|
|
22066
|
+
tenantId
|
|
22067
|
+
) : void 0,
|
|
22068
|
+
tenantId
|
|
22069
|
+
);
|
|
22070
|
+
await tryEnrollEscrow(
|
|
22071
|
+
token,
|
|
22072
|
+
providerType,
|
|
22073
|
+
privateKey,
|
|
22074
|
+
primaryDid,
|
|
22075
|
+
shares,
|
|
22076
|
+
shareVersion,
|
|
22077
|
+
signDidAuthVp
|
|
22078
|
+
);
|
|
19719
22079
|
return { method: "passkey", credentialId: credential.credentialId };
|
|
19720
22080
|
}
|
|
19721
22081
|
case "phrase": {
|
|
@@ -19728,10 +22088,38 @@ function createSSSStrategy(config) {
|
|
|
19728
22088
|
type: "phrase",
|
|
19729
22089
|
shareVersion
|
|
19730
22090
|
},
|
|
19731
|
-
|
|
22091
|
+
signDidAuthVp ? await requestFreshDidAuthVp(
|
|
22092
|
+
serverUrl,
|
|
22093
|
+
privateKey,
|
|
22094
|
+
primaryDid,
|
|
22095
|
+
signDidAuthVp,
|
|
22096
|
+
tenantId
|
|
22097
|
+
) : void 0,
|
|
19732
22098
|
tenantId
|
|
19733
22099
|
);
|
|
19734
|
-
|
|
22100
|
+
const challengeWordIndices = selectRecoveryPhraseChallengeIndices(
|
|
22101
|
+
phrase.split(/\s+/).length
|
|
22102
|
+
);
|
|
22103
|
+
const challengeWordOptions = await buildRecoveryPhraseChallengeOptions(
|
|
22104
|
+
phrase,
|
|
22105
|
+
challengeWordIndices
|
|
22106
|
+
);
|
|
22107
|
+
pendingPhraseConfirmation = { phrase, challengeWordIndices };
|
|
22108
|
+
await tryEnrollEscrow(
|
|
22109
|
+
token,
|
|
22110
|
+
providerType,
|
|
22111
|
+
privateKey,
|
|
22112
|
+
primaryDid,
|
|
22113
|
+
shares,
|
|
22114
|
+
shareVersion,
|
|
22115
|
+
signDidAuthVp
|
|
22116
|
+
);
|
|
22117
|
+
return {
|
|
22118
|
+
method: "phrase",
|
|
22119
|
+
phrase,
|
|
22120
|
+
challengeWordIndices,
|
|
22121
|
+
challengeWordOptions
|
|
22122
|
+
};
|
|
19735
22123
|
}
|
|
19736
22124
|
case "backup": {
|
|
19737
22125
|
const encrypted = await encryptWithPassword(
|
|
@@ -19743,6 +22131,7 @@ function createSSSStrategy(config) {
|
|
|
19743
22131
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19744
22132
|
primaryDid: input.did,
|
|
19745
22133
|
shareVersion,
|
|
22134
|
+
shareChecksum: await calculateShareChecksum(shares.recoveryShare),
|
|
19746
22135
|
encryptedShare: {
|
|
19747
22136
|
ciphertext: encrypted.ciphertext,
|
|
19748
22137
|
iv: encrypted.iv,
|
|
@@ -19758,36 +22147,121 @@ function createSSSStrategy(config) {
|
|
|
19758
22147
|
type: "backup",
|
|
19759
22148
|
shareVersion
|
|
19760
22149
|
},
|
|
19761
|
-
|
|
22150
|
+
signDidAuthVp ? await requestFreshDidAuthVp(
|
|
22151
|
+
serverUrl,
|
|
22152
|
+
privateKey,
|
|
22153
|
+
primaryDid,
|
|
22154
|
+
signDidAuthVp,
|
|
22155
|
+
tenantId
|
|
22156
|
+
) : void 0,
|
|
19762
22157
|
tenantId
|
|
19763
22158
|
);
|
|
22159
|
+
pendingBackupConfirmation = {
|
|
22160
|
+
backupFile,
|
|
22161
|
+
recoveryShare: shares.recoveryShare
|
|
22162
|
+
};
|
|
22163
|
+
await tryEnrollEscrow(
|
|
22164
|
+
token,
|
|
22165
|
+
providerType,
|
|
22166
|
+
privateKey,
|
|
22167
|
+
primaryDid,
|
|
22168
|
+
shares,
|
|
22169
|
+
shareVersion,
|
|
22170
|
+
signDidAuthVp
|
|
22171
|
+
);
|
|
19764
22172
|
return { method: "backup", backupFile };
|
|
19765
22173
|
}
|
|
19766
22174
|
case "email": {
|
|
19767
|
-
await
|
|
22175
|
+
const didAuthVp = signDidAuthVp ? await requestFreshDidAuthVp(
|
|
22176
|
+
serverUrl,
|
|
22177
|
+
privateKey,
|
|
22178
|
+
primaryDid,
|
|
22179
|
+
signDidAuthVp,
|
|
22180
|
+
tenantId
|
|
22181
|
+
) : void 0;
|
|
22182
|
+
await sendEmailBackupShare(
|
|
19768
22183
|
serverUrl,
|
|
19769
22184
|
token,
|
|
19770
22185
|
providerType,
|
|
19771
22186
|
shares.emailShare,
|
|
22187
|
+
input.email,
|
|
22188
|
+
escrowRelayPublicKey,
|
|
22189
|
+
escrowRelayKeyId,
|
|
22190
|
+
emailBranding,
|
|
19772
22191
|
shareVersion,
|
|
22192
|
+
didAuthVp,
|
|
19773
22193
|
tenantId
|
|
19774
22194
|
);
|
|
19775
|
-
await
|
|
19776
|
-
serverUrl,
|
|
22195
|
+
await tryEnrollEscrow(
|
|
19777
22196
|
token,
|
|
19778
22197
|
providerType,
|
|
19779
|
-
|
|
19780
|
-
|
|
19781
|
-
|
|
19782
|
-
|
|
19783
|
-
|
|
19784
|
-
tenantId
|
|
22198
|
+
privateKey,
|
|
22199
|
+
primaryDid,
|
|
22200
|
+
shares,
|
|
22201
|
+
shareVersion,
|
|
22202
|
+
signDidAuthVp
|
|
19785
22203
|
);
|
|
19786
|
-
hasRecoveryEmail = true;
|
|
19787
22204
|
return { method: "email" };
|
|
19788
22205
|
}
|
|
19789
22206
|
}
|
|
19790
22207
|
},
|
|
22208
|
+
async confirmRecoveryMethod(params) {
|
|
22209
|
+
const { token, providerType, privateKey, input, signDidAuthVp } = params;
|
|
22210
|
+
const primaryDid = lastServerSnapshot?.primaryDid;
|
|
22211
|
+
if (!primaryDid) throw new Error("Cannot confirm recovery without a primary DID");
|
|
22212
|
+
const confirmationBody = { type: input.method };
|
|
22213
|
+
if (input.method === "phrase") {
|
|
22214
|
+
const pending = pendingPhraseConfirmation;
|
|
22215
|
+
if (!pending) throw new Error("Generate a new recovery phrase to continue");
|
|
22216
|
+
const phraseWords = pending.phrase.trim().toLowerCase().split(/\s+/);
|
|
22217
|
+
const matches = input.challengeWords.length === pending.challengeWordIndices.length && pending.challengeWordIndices.every(
|
|
22218
|
+
(wordIndex, answerIndex) => input.challengeWords[answerIndex]?.trim().toLowerCase() === phraseWords[wordIndex]
|
|
22219
|
+
);
|
|
22220
|
+
if (!matches) {
|
|
22221
|
+
throw new Error("Those words do not match. Check your phrase and try again.");
|
|
22222
|
+
}
|
|
22223
|
+
} else if (input.method === "backup") {
|
|
22224
|
+
const pending = pendingBackupConfirmation;
|
|
22225
|
+
if (!pending) throw new Error("Generate a new backup file to continue");
|
|
22226
|
+
let suppliedBackup;
|
|
22227
|
+
let decryptedShare;
|
|
22228
|
+
try {
|
|
22229
|
+
suppliedBackup = JSON.parse(input.fileContents);
|
|
22230
|
+
decryptedShare = await decryptWithPassword(
|
|
22231
|
+
suppliedBackup.encryptedShare.ciphertext,
|
|
22232
|
+
suppliedBackup.encryptedShare.iv,
|
|
22233
|
+
suppliedBackup.encryptedShare.salt,
|
|
22234
|
+
input.password,
|
|
22235
|
+
suppliedBackup.encryptedShare.kdfParams
|
|
22236
|
+
);
|
|
22237
|
+
} catch {
|
|
22238
|
+
throw new Error("Incorrect password or corrupted data. Please try again.");
|
|
22239
|
+
}
|
|
22240
|
+
const checksum = await calculateShareChecksum(decryptedShare);
|
|
22241
|
+
if (suppliedBackup.shareChecksum !== checksum || pending.backupFile.shareChecksum !== checksum || decryptedShare !== pending.recoveryShare) {
|
|
22242
|
+
throw new Error("This backup file could not be verified. Please try again.");
|
|
22243
|
+
}
|
|
22244
|
+
} else {
|
|
22245
|
+
confirmationBody.code = input.code;
|
|
22246
|
+
}
|
|
22247
|
+
const didAuthVp = signDidAuthVp ? await requestFreshDidAuthVp(
|
|
22248
|
+
serverUrl,
|
|
22249
|
+
privateKey,
|
|
22250
|
+
primaryDid,
|
|
22251
|
+
signDidAuthVp,
|
|
22252
|
+
tenantId
|
|
22253
|
+
) : void 0;
|
|
22254
|
+
await postRecoveryConfirmation(
|
|
22255
|
+
serverUrl,
|
|
22256
|
+
token,
|
|
22257
|
+
providerType,
|
|
22258
|
+
confirmationBody,
|
|
22259
|
+
didAuthVp,
|
|
22260
|
+
tenantId
|
|
22261
|
+
);
|
|
22262
|
+
if (input.method === "phrase") pendingPhraseConfirmation = void 0;
|
|
22263
|
+
if (input.method === "backup") pendingBackupConfirmation = void 0;
|
|
22264
|
+
},
|
|
19791
22265
|
async getAvailableRecoveryMethods(token, providerType) {
|
|
19792
22266
|
try {
|
|
19793
22267
|
const serverData = await fetchAuthShareRaw(
|
|
@@ -19798,15 +22272,274 @@ function createSSSStrategy(config) {
|
|
|
19798
22272
|
tenantId
|
|
19799
22273
|
);
|
|
19800
22274
|
const methods = serverData?.recoveryMethods || [];
|
|
19801
|
-
|
|
19802
|
-
|
|
19803
|
-
|
|
19804
|
-
|
|
22275
|
+
const activationState = serverData?.sssActivationState ?? "active";
|
|
22276
|
+
return methods.filter((method) => {
|
|
22277
|
+
if (method.confirmedAt) return true;
|
|
22278
|
+
if (method.confirmationStatus) return false;
|
|
22279
|
+
return activationState === "active";
|
|
22280
|
+
});
|
|
19805
22281
|
} catch (e) {
|
|
19806
22282
|
console.error("Error getting recovery methods:", e);
|
|
19807
22283
|
return [];
|
|
19808
22284
|
}
|
|
19809
22285
|
},
|
|
22286
|
+
async startIdentityRecovery(email) {
|
|
22287
|
+
await postJson(
|
|
22288
|
+
`${serverUrl}/keys/recovery-session/start`,
|
|
22289
|
+
{ email },
|
|
22290
|
+
buildHeaders("", void 0, tenantId)
|
|
22291
|
+
);
|
|
22292
|
+
},
|
|
22293
|
+
async verifyIdentityRecovery(email, code) {
|
|
22294
|
+
const result = await postJson(
|
|
22295
|
+
`${serverUrl}/keys/recovery-session/verify`,
|
|
22296
|
+
{ email, code },
|
|
22297
|
+
buildHeaders("", void 0, tenantId)
|
|
22298
|
+
);
|
|
22299
|
+
return {
|
|
22300
|
+
...result,
|
|
22301
|
+
recoveryMethods: result.recoveryMethods.map((method) => ({
|
|
22302
|
+
...method,
|
|
22303
|
+
createdAt: new Date(method.createdAt)
|
|
22304
|
+
}))
|
|
22305
|
+
};
|
|
22306
|
+
},
|
|
22307
|
+
async prepareIdentityRecovery(params) {
|
|
22308
|
+
const { input } = params;
|
|
22309
|
+
if (input.method === "escrow") {
|
|
22310
|
+
throw new Error("Use executeRecovery to complete an escrow hold");
|
|
22311
|
+
}
|
|
22312
|
+
let recoveryShare;
|
|
22313
|
+
switch (input.method) {
|
|
22314
|
+
case "passkey":
|
|
22315
|
+
break;
|
|
22316
|
+
case "phrase":
|
|
22317
|
+
if (!await validateRecoveryPhrase(input.phrase)) {
|
|
22318
|
+
throw new Error("Invalid recovery phrase");
|
|
22319
|
+
}
|
|
22320
|
+
recoveryShare = await recoveryPhraseToShare(input.phrase);
|
|
22321
|
+
break;
|
|
22322
|
+
case "backup": {
|
|
22323
|
+
const backup = JSON.parse(input.fileContents);
|
|
22324
|
+
if (backup.version !== 1) throw new Error("Unsupported backup file version");
|
|
22325
|
+
recoveryShare = await decryptWithPassword(
|
|
22326
|
+
backup.encryptedShare.ciphertext,
|
|
22327
|
+
backup.encryptedShare.iv,
|
|
22328
|
+
backup.encryptedShare.salt,
|
|
22329
|
+
input.password,
|
|
22330
|
+
backup.encryptedShare.kdfParams
|
|
22331
|
+
);
|
|
22332
|
+
break;
|
|
22333
|
+
}
|
|
22334
|
+
case "email":
|
|
22335
|
+
recoveryShare = parseVersionedEmailShare(input.emailShare.trim()).share;
|
|
22336
|
+
break;
|
|
22337
|
+
}
|
|
22338
|
+
try {
|
|
22339
|
+
const material = await postJson(
|
|
22340
|
+
`${serverUrl}/keys/recovery-session/recover`,
|
|
22341
|
+
{
|
|
22342
|
+
recoverySessionToken: params.recoverySessionToken,
|
|
22343
|
+
type: input.method,
|
|
22344
|
+
...input.method === "passkey" ? { credentialId: input.credentialId } : {}
|
|
22345
|
+
},
|
|
22346
|
+
buildHeaders("", void 0, tenantId)
|
|
22347
|
+
);
|
|
22348
|
+
if (input.method === "passkey") {
|
|
22349
|
+
if (!material.encryptedShare) {
|
|
22350
|
+
throw new Error("No passkey recovery data found");
|
|
22351
|
+
}
|
|
22352
|
+
recoveryShare = await decryptShareWithPasskey({
|
|
22353
|
+
encryptedData: material.encryptedShare.encryptedData,
|
|
22354
|
+
iv: material.encryptedShare.iv,
|
|
22355
|
+
credentialId: input.credentialId
|
|
22356
|
+
});
|
|
22357
|
+
}
|
|
22358
|
+
if (!recoveryShare) throw new Error("Recovery data was empty");
|
|
22359
|
+
const authShare = material.authShare.encryptedData;
|
|
22360
|
+
let privateKey;
|
|
22361
|
+
try {
|
|
22362
|
+
privateKey = await reconstructFromShares([recoveryShare, authShare]);
|
|
22363
|
+
} catch {
|
|
22364
|
+
throw new Error(
|
|
22365
|
+
"Recovery produced an incorrect key. Try another recovery method."
|
|
22366
|
+
);
|
|
22367
|
+
}
|
|
22368
|
+
const derivedDid = await params.didFromPrivateKey(privateKey);
|
|
22369
|
+
if (!derivedDid || derivedDid !== material.primaryDid) {
|
|
22370
|
+
throw new Error(
|
|
22371
|
+
"Recovery produced an incorrect key. Try another recovery method."
|
|
22372
|
+
);
|
|
22373
|
+
}
|
|
22374
|
+
pendingIdentityRecovery = {
|
|
22375
|
+
privateKey,
|
|
22376
|
+
primaryDid: material.primaryDid,
|
|
22377
|
+
recoveryShare,
|
|
22378
|
+
authShare,
|
|
22379
|
+
rebindSessionToken: material.rebindSessionToken
|
|
22380
|
+
};
|
|
22381
|
+
return { privateKey, did: material.primaryDid };
|
|
22382
|
+
} catch (error) {
|
|
22383
|
+
const message = error instanceof Error ? error.message : "Identity recovery failed";
|
|
22384
|
+
throw new IdentityRecoverySessionConsumedError(message);
|
|
22385
|
+
}
|
|
22386
|
+
},
|
|
22387
|
+
hasPendingIdentityRecovery() {
|
|
22388
|
+
return Boolean(pendingIdentityRecovery);
|
|
22389
|
+
},
|
|
22390
|
+
cancelIdentityRecovery() {
|
|
22391
|
+
pendingIdentityRecovery = void 0;
|
|
22392
|
+
},
|
|
22393
|
+
async completeIdentityRecovery(params) {
|
|
22394
|
+
const storage2 = guardedStorage();
|
|
22395
|
+
const generation = storageGeneration;
|
|
22396
|
+
const pending = pendingIdentityRecovery;
|
|
22397
|
+
const signDidAuthVp = params.signDidAuthVp;
|
|
22398
|
+
if (!pending) throw new Error("No identity recovery is waiting for a new sign-in");
|
|
22399
|
+
if (!signDidAuthVp) {
|
|
22400
|
+
throw new Error("DID proof signing is required to bind a new sign-in");
|
|
22401
|
+
}
|
|
22402
|
+
const storageId = activeStorageId;
|
|
22403
|
+
const { shares, shareVersion } = await withShareUpdateLock(
|
|
22404
|
+
storage2,
|
|
22405
|
+
storageId,
|
|
22406
|
+
() => withRotationLock(async () => {
|
|
22407
|
+
const assertActive = /* @__PURE__ */ __name(() => {
|
|
22408
|
+
if (activeStorageId !== storageId || pendingIdentityRecovery !== pending || storageGeneration !== generation) {
|
|
22409
|
+
throw new Error("This recovery request was cancelled.");
|
|
22410
|
+
}
|
|
22411
|
+
}, "assertActive");
|
|
22412
|
+
assertActive();
|
|
22413
|
+
const candidates = await readPendingShareCandidates(storage2, storageId);
|
|
22414
|
+
if (pending.rebind && pending.rebind.storageId !== storageId) {
|
|
22415
|
+
throw new Error("Active account changed during identity recovery");
|
|
22416
|
+
}
|
|
22417
|
+
if (candidates.some(
|
|
22418
|
+
(candidate) => candidate.share !== pending.rebind?.shares.deviceShare
|
|
22419
|
+
)) {
|
|
22420
|
+
throw new Error(
|
|
22421
|
+
"Reconcile the pending share update before binding a sign-in"
|
|
22422
|
+
);
|
|
22423
|
+
}
|
|
22424
|
+
let shareVersion2;
|
|
22425
|
+
const retry = pending.rebind?.attempted ?? false;
|
|
22426
|
+
if (retry && pending.rebind) {
|
|
22427
|
+
const current = await fetchAuthShareRaw(
|
|
22428
|
+
serverUrl,
|
|
22429
|
+
params.token,
|
|
22430
|
+
params.providerType,
|
|
22431
|
+
void 0,
|
|
22432
|
+
tenantId
|
|
22433
|
+
);
|
|
22434
|
+
const authShare = authShareToString(current?.authShare);
|
|
22435
|
+
if (authShare && current?.shareVersion != null) {
|
|
22436
|
+
const health = await verifyStoredShares(
|
|
22437
|
+
{
|
|
22438
|
+
getDevice: /* @__PURE__ */ __name(async () => pending.rebind.shares.deviceShare, "getDevice"),
|
|
22439
|
+
getAuth: /* @__PURE__ */ __name(async () => authShare, "getAuth")
|
|
22440
|
+
},
|
|
22441
|
+
pending.primaryDid,
|
|
22442
|
+
async (key) => key === pending.privateKey ? pending.primaryDid : ""
|
|
22443
|
+
);
|
|
22444
|
+
if (health.healthy) shareVersion2 = current.shareVersion;
|
|
22445
|
+
}
|
|
22446
|
+
}
|
|
22447
|
+
assertActive();
|
|
22448
|
+
if (shareVersion2 === void 0) {
|
|
22449
|
+
const didAuthVp = await requestFreshDidAuthVp(
|
|
22450
|
+
serverUrl,
|
|
22451
|
+
pending.privateKey,
|
|
22452
|
+
pending.primaryDid,
|
|
22453
|
+
signDidAuthVp,
|
|
22454
|
+
tenantId
|
|
22455
|
+
);
|
|
22456
|
+
assertActive();
|
|
22457
|
+
pending.rebind ??= {
|
|
22458
|
+
shares: (await splitAndVerify(pending.privateKey)).shares,
|
|
22459
|
+
storageId,
|
|
22460
|
+
attempted: false
|
|
22461
|
+
};
|
|
22462
|
+
const rebind = pending.rebind;
|
|
22463
|
+
if (!candidates.some(
|
|
22464
|
+
(candidate) => candidate.share === rebind.shares.deviceShare
|
|
22465
|
+
)) {
|
|
22466
|
+
await addPendingShareCandidate(
|
|
22467
|
+
storage2,
|
|
22468
|
+
{ share: rebind.shares.deviceShare, createdAt: Date.now() },
|
|
22469
|
+
storageId
|
|
22470
|
+
);
|
|
22471
|
+
}
|
|
22472
|
+
assertActive();
|
|
22473
|
+
rebind.attempted = true;
|
|
22474
|
+
try {
|
|
22475
|
+
const response = await postJson(
|
|
22476
|
+
`${serverUrl}/keys/recovery-session/rebind`,
|
|
22477
|
+
{
|
|
22478
|
+
recoverySessionToken: pending.rebindSessionToken,
|
|
22479
|
+
providerType: params.providerType,
|
|
22480
|
+
primaryDid: pending.primaryDid,
|
|
22481
|
+
authShare: {
|
|
22482
|
+
encryptedData: rebind.shares.authShare,
|
|
22483
|
+
encryptedDek: "",
|
|
22484
|
+
iv: ""
|
|
22485
|
+
}
|
|
22486
|
+
},
|
|
22487
|
+
{
|
|
22488
|
+
...buildHeaders("", didAuthVp, tenantId),
|
|
22489
|
+
"X-Auth-Token": params.token
|
|
22490
|
+
}
|
|
22491
|
+
);
|
|
22492
|
+
shareVersion2 = response.shareVersion;
|
|
22493
|
+
} catch (error) {
|
|
22494
|
+
if (error instanceof ShareWriteRejectedError && !retry) {
|
|
22495
|
+
await removePendingShareCandidate(
|
|
22496
|
+
storage2,
|
|
22497
|
+
rebind.shares.deviceShare,
|
|
22498
|
+
storageId
|
|
22499
|
+
);
|
|
22500
|
+
pending.rebind = void 0;
|
|
22501
|
+
}
|
|
22502
|
+
throw error;
|
|
22503
|
+
}
|
|
22504
|
+
}
|
|
22505
|
+
if (shareVersion2 === void 0 || !pending.rebind) {
|
|
22506
|
+
throw new Error("Server did not confirm the new share");
|
|
22507
|
+
}
|
|
22508
|
+
assertActive();
|
|
22509
|
+
const shares2 = pending.rebind.shares;
|
|
22510
|
+
await storage2.storeDeviceShare(shares2.deviceShare, storageId);
|
|
22511
|
+
await storage2.storeShareVersion(shareVersion2, storageId);
|
|
22512
|
+
await clearPendingShareCandidates(storage2, storageId);
|
|
22513
|
+
assertActive();
|
|
22514
|
+
lastEmailShare = shares2.emailShare;
|
|
22515
|
+
lastShareVersion = shareVersion2;
|
|
22516
|
+
lastServerSnapshot = {
|
|
22517
|
+
currentVersion: shareVersion2,
|
|
22518
|
+
resolvedVersion: shareVersion2,
|
|
22519
|
+
authShare: shares2.authShare,
|
|
22520
|
+
primaryDid: pending.primaryDid
|
|
22521
|
+
};
|
|
22522
|
+
pendingIdentityRecovery = void 0;
|
|
22523
|
+
return { shares: shares2, shareVersion: shareVersion2 };
|
|
22524
|
+
})
|
|
22525
|
+
);
|
|
22526
|
+
await tryEnrollEscrow(
|
|
22527
|
+
params.token,
|
|
22528
|
+
params.providerType,
|
|
22529
|
+
pending.privateKey,
|
|
22530
|
+
pending.primaryDid,
|
|
22531
|
+
shares,
|
|
22532
|
+
shareVersion,
|
|
22533
|
+
signDidAuthVp,
|
|
22534
|
+
void 0,
|
|
22535
|
+
void 0,
|
|
22536
|
+
true
|
|
22537
|
+
);
|
|
22538
|
+
if (activeStorageId !== storageId || storageGeneration !== generation) {
|
|
22539
|
+
throw new Error("This recovery request was cancelled.");
|
|
22540
|
+
}
|
|
22541
|
+
return { privateKey: pending.privateKey, did: pending.primaryDid };
|
|
22542
|
+
},
|
|
19810
22543
|
// --- Contact method management ---
|
|
19811
22544
|
async upgradeContactMethod(token, providerType, previousPhone, email, code) {
|
|
19812
22545
|
const res = await fetch(`${serverUrl}/keys/upgrade-contact-method`, {
|
|
@@ -19829,7 +22562,7 @@ function createSSSStrategy(config) {
|
|
|
19829
22562
|
return { customToken: data?.customToken };
|
|
19830
22563
|
},
|
|
19831
22564
|
// --- Email backup ---
|
|
19832
|
-
async sendEmailBackupShare(token, providerType, _privateKey, email) {
|
|
22565
|
+
async sendEmailBackupShare(token, providerType, _privateKey, email, didAuthVp) {
|
|
19833
22566
|
if (!enableEmailBackupShare) return;
|
|
19834
22567
|
if (!lastEmailShare) {
|
|
19835
22568
|
console.warn(
|
|
@@ -19837,29 +22570,22 @@ function createSSSStrategy(config) {
|
|
|
19837
22570
|
);
|
|
19838
22571
|
return;
|
|
19839
22572
|
}
|
|
19840
|
-
if (
|
|
19841
|
-
await sendEmailShareToRecoveryEmail(
|
|
19842
|
-
serverUrl,
|
|
19843
|
-
token,
|
|
19844
|
-
providerType,
|
|
19845
|
-
lastEmailShare,
|
|
19846
|
-
lastShareVersion,
|
|
19847
|
-
tenantId
|
|
19848
|
-
);
|
|
19849
|
-
} else {
|
|
19850
|
-
if (!email) {
|
|
19851
|
-
console.warn("Cannot send email backup share: no email address");
|
|
19852
|
-
return;
|
|
19853
|
-
}
|
|
22573
|
+
if (email) {
|
|
19854
22574
|
await sendEmailBackupShare(
|
|
19855
22575
|
serverUrl,
|
|
19856
22576
|
token,
|
|
19857
22577
|
providerType,
|
|
19858
22578
|
lastEmailShare,
|
|
19859
22579
|
email,
|
|
22580
|
+
escrowRelayPublicKey,
|
|
22581
|
+
escrowRelayKeyId,
|
|
22582
|
+
emailBranding,
|
|
19860
22583
|
lastShareVersion,
|
|
22584
|
+
didAuthVp,
|
|
19861
22585
|
tenantId
|
|
19862
22586
|
);
|
|
22587
|
+
} else {
|
|
22588
|
+
throw new Error("Enter a recovery email before sending a recovery key");
|
|
19863
22589
|
}
|
|
19864
22590
|
lastEmailShare = void 0;
|
|
19865
22591
|
lastShareVersion = void 0;
|
|
@@ -19876,6 +22602,8 @@ function createSSSStrategy(config) {
|
|
|
19876
22602
|
return [SSS_DB_NAME];
|
|
19877
22603
|
},
|
|
19878
22604
|
async cleanup() {
|
|
22605
|
+
storageGeneration++;
|
|
22606
|
+
await Promise.allSettled(localWrites);
|
|
19879
22607
|
}
|
|
19880
22608
|
};
|
|
19881
22609
|
}
|
|
@@ -19968,13 +22696,7 @@ var decryptShareFromTransfer = /* @__PURE__ */ __name(async (payload, recipientP
|
|
|
19968
22696
|
}, "decryptShareFromTransfer");
|
|
19969
22697
|
|
|
19970
22698
|
// src/qr-login.ts
|
|
19971
|
-
var buildHeaders2 = /* @__PURE__ */ __name((
|
|
19972
|
-
const headers = { "Content-Type": "application/json" };
|
|
19973
|
-
if (token) {
|
|
19974
|
-
headers.Authorization = `Bearer ${token}`;
|
|
19975
|
-
}
|
|
19976
|
-
return headers;
|
|
19977
|
-
}, "buildHeaders");
|
|
22699
|
+
var buildHeaders2 = /* @__PURE__ */ __name(() => ({ "Content-Type": "application/json" }), "buildHeaders");
|
|
19978
22700
|
var createQrLoginSession = /* @__PURE__ */ __name(async (config) => {
|
|
19979
22701
|
const ephemeralKeypair = await generateEphemeralKeypair();
|
|
19980
22702
|
const response = await fetch(`${config.serverUrl}/qr-login/session`, {
|
|
@@ -20042,10 +22764,14 @@ var pollUntilApproved = /* @__PURE__ */ __name(async (config, sessionId, ephemer
|
|
|
20042
22764
|
await new Promise((resolve, reject) => {
|
|
20043
22765
|
const timer = setTimeout(resolve, intervalMs);
|
|
20044
22766
|
if (options?.signal) {
|
|
20045
|
-
options.signal.addEventListener(
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
22767
|
+
options.signal.addEventListener(
|
|
22768
|
+
"abort",
|
|
22769
|
+
() => {
|
|
22770
|
+
clearTimeout(timer);
|
|
22771
|
+
reject(new Error("QR login polling aborted"));
|
|
22772
|
+
},
|
|
22773
|
+
{ once: true }
|
|
22774
|
+
);
|
|
20049
22775
|
}
|
|
20050
22776
|
});
|
|
20051
22777
|
}
|
|
@@ -20097,6 +22823,3 @@ var notifyDevicesForQrSession = /* @__PURE__ */ __name(async (config, sessionId,
|
|
|
20097
22823
|
return { sent: false, deviceCount: 0 };
|
|
20098
22824
|
}
|
|
20099
22825
|
}, "notifyDevicesForQrSession");
|
|
20100
|
-
|
|
20101
|
-
// src/types.ts
|
|
20102
|
-
var import_types = __toESM(require_dist());
|