@twin.org/immutable-proof-service 0.0.1-next.2 → 0.0.1-next.21

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.
@@ -1,18 +1,85 @@
1
1
  'use strict';
2
2
 
3
+ var entity = require('@twin.org/entity');
3
4
  var core = require('@twin.org/core');
4
5
  var immutableProofModels = require('@twin.org/immutable-proof-models');
5
6
  var standardsW3cDid = require('@twin.org/standards-w3c-did');
6
7
  var web = require('@twin.org/web');
8
+ var backgroundTaskModels = require('@twin.org/background-task-models');
7
9
  var crypto = require('@twin.org/crypto');
8
10
  var dataJsonLd = require('@twin.org/data-json-ld');
9
- var dataSchemaOrg = require('@twin.org/data-schema-org');
10
- var entity = require('@twin.org/entity');
11
11
  var entityStorageModels = require('@twin.org/entity-storage-models');
12
12
  var identityModels = require('@twin.org/identity-models');
13
13
  var immutableStorageModels = require('@twin.org/immutable-storage-models');
14
14
  var vaultModels = require('@twin.org/vault-models');
15
15
 
16
+ // Copyright 2024 IOTA Stiftung.
17
+ // SPDX-License-Identifier: Apache-2.0.
18
+ /**
19
+ * Class describing the immutable proof.
20
+ */
21
+ exports.ImmutableProof = class ImmutableProof {
22
+ /**
23
+ * The id of the proof.
24
+ */
25
+ id;
26
+ /**
27
+ * The identity of the node which controls the proof.
28
+ */
29
+ nodeIdentity;
30
+ /**
31
+ * The identity of the user which created the proof.
32
+ */
33
+ userIdentity;
34
+ /**
35
+ * The date/time of when the proof was created.
36
+ */
37
+ dateCreated;
38
+ /**
39
+ * The associated id for the item.
40
+ */
41
+ proofObjectId;
42
+ /**
43
+ * The associated hash for the item.
44
+ */
45
+ proofObjectHash;
46
+ /**
47
+ * The immutable storage id.
48
+ */
49
+ immutableStorageId;
50
+ };
51
+ __decorate([
52
+ entity.property({ type: "string", isPrimary: true }),
53
+ __metadata("design:type", String)
54
+ ], exports.ImmutableProof.prototype, "id", void 0);
55
+ __decorate([
56
+ entity.property({ type: "string" }),
57
+ __metadata("design:type", String)
58
+ ], exports.ImmutableProof.prototype, "nodeIdentity", void 0);
59
+ __decorate([
60
+ entity.property({ type: "string" }),
61
+ __metadata("design:type", String)
62
+ ], exports.ImmutableProof.prototype, "userIdentity", void 0);
63
+ __decorate([
64
+ entity.property({ type: "string", format: "date-time", sortDirection: entity.SortDirection.Descending }),
65
+ __metadata("design:type", String)
66
+ ], exports.ImmutableProof.prototype, "dateCreated", void 0);
67
+ __decorate([
68
+ entity.property({ type: "string" }),
69
+ __metadata("design:type", String)
70
+ ], exports.ImmutableProof.prototype, "proofObjectId", void 0);
71
+ __decorate([
72
+ entity.property({ type: "string" }),
73
+ __metadata("design:type", String)
74
+ ], exports.ImmutableProof.prototype, "proofObjectHash", void 0);
75
+ __decorate([
76
+ entity.property({ type: "string" }),
77
+ __metadata("design:type", String)
78
+ ], exports.ImmutableProof.prototype, "immutableStorageId", void 0);
79
+ exports.ImmutableProof = __decorate([
80
+ entity.entity()
81
+ ], exports.ImmutableProof);
82
+
16
83
  /**
17
84
  * The source used when communicating about these routes.
18
85
  */
@@ -48,7 +115,7 @@ function generateRestRoutesImmutableProof(baseRouteName, componentName) {
48
115
  request: {
49
116
  body: {
50
117
  proofObject: {
51
- "@context": "http://schema.org",
118
+ "@context": "https://schema.org",
52
119
  type: "Person",
53
120
  name: "John Smith"
54
121
  }
@@ -108,7 +175,10 @@ function generateRestRoutesImmutableProof(baseRouteName, componentName) {
108
175
  id: "immutableProofGetResponseExample",
109
176
  response: {
110
177
  body: {
111
- "@context": immutableProofModels.ImmutableProofTypes.ContextRoot,
178
+ "@context": [
179
+ immutableProofModels.ImmutableProofTypes.ContextRoot,
180
+ immutableProofModels.ImmutableProofTypes.ContextRootCommon
181
+ ],
112
182
  type: immutableProofModels.ImmutableProofTypes.ImmutableProof,
113
183
  id: "ais:1234567890",
114
184
  userIdentity: "user-1",
@@ -138,7 +208,10 @@ function generateRestRoutesImmutableProof(baseRouteName, componentName) {
138
208
  [web.HeaderTypes.ContentType]: web.MimeTypes.JsonLd
139
209
  },
140
210
  body: {
141
- "@context": immutableProofModels.ImmutableProofTypes.ContextRoot,
211
+ "@context": [
212
+ immutableProofModels.ImmutableProofTypes.ContextRoot,
213
+ immutableProofModels.ImmutableProofTypes.ContextRootCommon
214
+ ],
142
215
  type: immutableProofModels.ImmutableProofTypes.ImmutableProof,
143
216
  id: "ais:1234567890",
144
217
  userIdentity: "user-1",
@@ -166,8 +239,8 @@ function generateRestRoutesImmutableProof(baseRouteName, componentName) {
166
239
  operationId: "immutableProofVerify",
167
240
  summary: "Verify a proof",
168
241
  tag: tagsImmutableProof[0].name,
169
- method: "POST",
170
- path: `${baseRouteName}/:id`,
242
+ method: "GET",
243
+ path: `${baseRouteName}/:id/verify`,
171
244
  handler: async (httpRequestContext, request) => immutableProofVerify(httpRequestContext, componentName, request),
172
245
  requestType: {
173
246
  type: "IImmutableProofVerifyRequest",
@@ -177,13 +250,6 @@ function generateRestRoutesImmutableProof(baseRouteName, componentName) {
177
250
  request: {
178
251
  pathParams: {
179
252
  id: "ais:1234567890"
180
- },
181
- body: {
182
- proofObject: {
183
- "@context": "http://schema.org",
184
- type: "Person",
185
- name: "John Smith"
186
- }
187
253
  }
188
254
  }
189
255
  }
@@ -279,10 +345,9 @@ async function immutableProofVerify(httpRequestContext, componentName, request)
279
345
  core.Guards.object(ROUTES_SOURCE, "request", request);
280
346
  core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
281
347
  core.Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
282
- core.Guards.object(ROUTES_SOURCE, "request.body.proofObject", request.body.proofObject);
283
348
  const mimeType = request.headers?.[web.HeaderTypes.Accept] === web.MimeTypes.JsonLd ? "jsonld" : "json";
284
349
  const component = core.ComponentFactory.get(componentName);
285
- const result = await component.verify(request.pathParams.id, request.body.proofObject);
350
+ const result = await component.verify(request.pathParams.id);
286
351
  return {
287
352
  headers: {
288
353
  [web.HeaderTypes.ContentType]: mimeType === "json" ? web.MimeTypes.Json : web.MimeTypes.JsonLd
@@ -331,39 +396,50 @@ class ImmutableProofService {
331
396
  */
332
397
  _immutableStorage;
333
398
  /**
334
- * The assertion method id to use for the proofs.
399
+ * The background task connector.
335
400
  * @internal
336
401
  */
337
- _assertionMethodId;
402
+ _backgroundTaskConnector;
338
403
  /**
339
- * The proof config key id to use for the proofs.
404
+ * The event bus component.
340
405
  * @internal
341
406
  */
342
- _proofConfigKeyId;
407
+ _eventBusComponent;
343
408
  /**
344
- * Are we currently processing proofs.
409
+ * The verification method id to use for the proofs.
345
410
  * @internal
346
411
  */
347
- _processing;
412
+ _verificationMethodId;
413
+ /**
414
+ * The proof hash key id to use for the proofs.
415
+ * @internal
416
+ */
417
+ _proofHashKeyId;
418
+ /**
419
+ * The identity connector type.
420
+ * @internal
421
+ */
422
+ _identityConnectorType;
348
423
  /**
349
424
  * Create a new instance of ImmutableProofService.
350
425
  * @param options The dependencies for the immutable proof connector.
351
- * @param options.config The configuration for the connector.
352
- * @param options.vaultConnectorType The vault connector type, defaults to "vault".
353
- * @param options.immutableProofEntityStorageType The entity storage for proofs, defaults to "immutable-proof".
354
- * @param options.immutableStorageType The immutable storage, defaults to "immutable-proof".
355
- * @param options.identityConnectorType The identity connector type, defaults to "identity".
356
426
  */
357
427
  constructor(options) {
358
428
  this._vaultConnector = vaultModels.VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
359
429
  this._proofStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.immutableProofEntityStorageType ?? core.StringHelper.kebabCase("ImmutableProof"));
360
- this._immutableStorage = immutableStorageModels.ImmutableStorageConnectorFactory.get(options?.immutableStorageType ?? "immutable-proof");
361
- this._identityConnector = identityModels.IdentityConnectorFactory.get(options?.identityConnectorType ?? "identity");
430
+ this._immutableStorage = immutableStorageModels.ImmutableStorageConnectorFactory.get(options?.immutableStorageType ?? "immutable-storage");
431
+ this._identityConnectorType = options?.identityConnectorType ?? "identity";
432
+ this._identityConnector = identityModels.IdentityConnectorFactory.get(this._identityConnectorType);
433
+ this._backgroundTaskConnector = backgroundTaskModels.BackgroundTaskConnectorFactory.get(options?.backgroundTaskConnectorType ?? "background-task");
434
+ if (core.Is.stringValue(options?.eventBusComponentType)) {
435
+ this._eventBusComponent = core.ComponentFactory.get(options.eventBusComponentType);
436
+ }
362
437
  this._config = options?.config ?? {};
363
- this._assertionMethodId = this._config.assertionMethodId ?? "immutable-proof";
364
- this._proofConfigKeyId = this._config.proofConfigKeyId ?? "immutable-proof";
365
- dataSchemaOrg.SchemaOrgDataTypes.registerRedirects();
366
- this._processing = false;
438
+ this._verificationMethodId = this._config.verificationMethodId ?? "immutable-proof-assertion";
439
+ this._proofHashKeyId = this._config.proofHashKeyId ?? "immutable-proof-hash";
440
+ this._backgroundTaskConnector.registerHandler("immutable-proof", "@twin.org/immutable-proof-task", "processProofTask", async (task) => {
441
+ await this.finaliseTask(task);
442
+ });
367
443
  }
368
444
  /**
369
445
  * Create a new authentication proof.
@@ -393,7 +469,16 @@ class ImmutableProofService {
393
469
  proofObjectHash: core.Converter.bytesToBase64(hash)
394
470
  };
395
471
  await this._proofStorage.set(proofEntity);
396
- this.startProcessingProofs();
472
+ const immutableProof = this.proofEntityToJsonLd(proofEntity);
473
+ const hashData = await this.generateHashData(proofEntity.nodeIdentity, immutableProof);
474
+ const proofTaskPayload = {
475
+ proofId: id,
476
+ nodeIdentity,
477
+ identityConnectorType: this._identityConnectorType,
478
+ assertionMethodId: this._verificationMethodId,
479
+ hashData: core.Converter.bytesToHex(hashData)
480
+ };
481
+ await this._backgroundTaskConnector.create("immutable-proof", proofTaskPayload);
397
482
  return new core.Urn(ImmutableProofService.NAMESPACE, id).toString();
398
483
  }
399
484
  catch (error) {
@@ -416,7 +501,7 @@ class ImmutableProofService {
416
501
  });
417
502
  }
418
503
  try {
419
- const { immutableProof } = await this.internalGet(id);
504
+ const { immutableProof } = await this.internalGet(id, false);
420
505
  const compacted = await dataJsonLd.JsonLdProcessor.compact(immutableProof, immutableProof["@context"]);
421
506
  return compacted;
422
507
  }
@@ -427,11 +512,10 @@ class ImmutableProofService {
427
512
  /**
428
513
  * Verify an authentication proof.
429
514
  * @param id The id of the proof to verify.
430
- * @param proofObject The object to verify as JSON-LD.
431
515
  * @returns The result of the verification and any failures.
432
516
  * @throws NotFoundError if the proof is not found.
433
517
  */
434
- async verify(id, proofObject) {
518
+ async verify(id) {
435
519
  core.Guards.stringValue(this.CLASS_NAME, "id", id);
436
520
  const urnParsed = core.Urn.fromValidString(id);
437
521
  if (urnParsed.namespaceIdentifier() !== ImmutableProofService.NAMESPACE) {
@@ -441,7 +525,7 @@ class ImmutableProofService {
441
525
  });
442
526
  }
443
527
  try {
444
- const { verified, failure } = await this.internalGet(id, proofObject);
528
+ const { verified, failure } = await this.internalGet(id, true);
445
529
  return {
446
530
  "@context": immutableProofModels.ImmutableProofTypes.ContextRoot,
447
531
  type: immutableProofModels.ImmutableProofTypes.ImmutableProofVerification,
@@ -507,13 +591,9 @@ class ImmutableProofService {
507
591
  * @returns The model.
508
592
  * @internal
509
593
  */
510
- proofEntityToModel(proofEntity) {
594
+ proofEntityToJsonLd(proofEntity) {
511
595
  const model = {
512
- "@context": [
513
- immutableProofModels.ImmutableProofTypes.ContextRoot,
514
- dataSchemaOrg.SchemaOrgTypes.ContextRoot,
515
- standardsW3cDid.DidContexts.ContextVCDataIntegrity
516
- ],
596
+ "@context": [immutableProofModels.ImmutableProofTypes.ContextRoot, immutableProofModels.ImmutableProofTypes.ContextRootCommon],
517
597
  type: immutableProofModels.ImmutableProofTypes.ImmutableProof,
518
598
  id: proofEntity.id,
519
599
  userIdentity: proofEntity.userIdentity,
@@ -523,74 +603,62 @@ class ImmutableProofService {
523
603
  return model;
524
604
  }
525
605
  /**
526
- * Start processing proofs.
527
- * @returns Nothing.
528
- * @internal
529
- */
530
- startProcessingProofs() {
531
- if (!this._processing) {
532
- setTimeout(async () => {
533
- await this.processProofs();
534
- }, 0);
535
- }
536
- }
537
- /**
538
- * Process the proofs.
606
+ * Process a proof.
607
+ * @param proofEntity The proof entity to process.
539
608
  * @internal
540
609
  */
541
- async processProofs() {
542
- // Get the oldest pending proof, plus one more, we can then determine whether to
543
- // trigger another process after this one
544
- const pendingProofs = await this._proofStorage.query({
545
- property: "immutableStorageId",
546
- comparison: entity.ComparisonOperator.Equals,
547
- value: undefined
548
- }, [
549
- {
550
- property: "dateCreated",
551
- sortDirection: entity.SortDirection.Ascending
610
+ async finaliseTask(task) {
611
+ if (task.status === backgroundTaskModels.TaskStatus.Success && core.Is.object(task.payload) && core.Is.object(task.result)) {
612
+ const proofEntity = await this._proofStorage.get(task.payload.proofId);
613
+ if (core.Is.object(proofEntity)) {
614
+ const immutableProof = this.proofEntityToJsonLd(proofEntity);
615
+ // As we are adding the proof to the data we update its context
616
+ immutableProof["@context"] = [
617
+ immutableProofModels.ImmutableProofTypes.ContextRoot,
618
+ immutableProofModels.ImmutableProofTypes.ContextRootCommon,
619
+ standardsW3cDid.DidContexts.ContextVCDataIntegrity
620
+ ];
621
+ immutableProof.proof = task.result.proof;
622
+ if (core.Is.stringValue(immutableProof.proof.created)) {
623
+ proofEntity.dateCreated = immutableProof.proof.created;
624
+ }
625
+ const compacted = await dataJsonLd.JsonLdProcessor.compact(immutableProof, immutableProof["@context"]);
626
+ const immutableStoreResult = await this._immutableStorage.store(proofEntity.nodeIdentity, core.ObjectHelper.toBytes(compacted));
627
+ proofEntity.immutableStorageId = immutableStoreResult.id;
628
+ await this._proofStorage.set(proofEntity);
629
+ await this._eventBusComponent?.publish(immutableProofModels.ImmutableProofTopics.ProofCreated, { id: new core.Urn(ImmutableProofService.NAMESPACE, task.payload.proofId).toString() });
552
630
  }
553
- ], undefined, undefined, 2);
554
- if (pendingProofs.entities.length > 0) {
555
- const proofEntity = pendingProofs.entities[0];
556
- const immutableProof = this.proofEntityToModel(proofEntity);
557
- const hashData = await this.generateHashData(proofEntity.nodeIdentity, immutableProof);
558
- immutableProof.proof = await this._identityConnector.createProof(proofEntity.nodeIdentity, `${proofEntity.nodeIdentity}#${this._assertionMethodId}`, hashData);
559
- proofEntity.dateCreated = immutableProof.proof.created ?? new Date(Date.now()).toISOString();
560
- const compacted = await dataJsonLd.JsonLdProcessor.compact(immutableProof, immutableProof["@context"]);
561
- proofEntity.immutableStorageId = await this._immutableStorage.store(proofEntity.nodeIdentity, core.ObjectHelper.toBytes(compacted));
562
- await this._proofStorage.set(proofEntity);
563
- }
564
- // If there are still remaining proofs, start the timer again
565
- this._processing = false;
566
- if (pendingProofs.entities.length > 1) {
567
- this.startProcessingProofs();
568
631
  }
569
632
  }
570
633
  /**
571
634
  * Verify an authentication proof.
572
635
  * @param id The id of the proof to verify.
573
- * @param proofObject The object to verify as JSON-LD.
636
+ * @param verify Validate the proof.
574
637
  * @returns The result of the verification and any failures.
575
638
  * @throws NotFoundError if the proof is not found.
576
639
  * @internal
577
640
  */
578
- async internalGet(id, proofObject) {
641
+ async internalGet(id, verify) {
579
642
  const urnParsed = core.Urn.fromValidString(id);
580
643
  const proofId = urnParsed.namespaceSpecific(0);
581
644
  const proofEntity = await this._proofStorage.get(proofId);
582
645
  if (core.Is.empty(proofEntity)) {
583
646
  throw new core.NotFoundError(this.CLASS_NAME, "proofNotFound", id);
584
647
  }
585
- let proofModel = await this.proofEntityToModel(proofEntity);
648
+ let proofModel = await this.proofEntityToJsonLd(proofEntity);
586
649
  let verified = false;
587
650
  let failure = immutableProofModels.ImmutableProofFailure.NotIssued;
588
651
  if (core.Is.stringValue(proofEntity.immutableStorageId)) {
589
652
  failure = immutableProofModels.ImmutableProofFailure.ProofMissing;
590
- const immutableData = await this._immutableStorage.get(proofEntity.immutableStorageId);
591
- if (core.Is.uint8Array(immutableData)) {
592
- proofModel = core.ObjectHelper.fromBytes(immutableData);
593
- if (core.Is.object(proofModel.proof) && core.Is.object(proofObject)) {
653
+ const immutableResult = await this._immutableStorage.get(proofEntity.immutableStorageId);
654
+ if (core.Is.uint8Array(immutableResult.data)) {
655
+ proofModel = core.ObjectHelper.fromBytes(immutableResult.data);
656
+ proofModel.immutableReceipt = immutableResult.receipt;
657
+ // As we are adding the receipt to the data we update its context
658
+ if (core.Is.array(proofModel["@context"])) {
659
+ proofModel["@context"].push(immutableStorageModels.ImmutableStorageTypes.ContextRoot);
660
+ }
661
+ if (verify && core.Is.object(proofModel.proof)) {
594
662
  if (proofModel.proof.cryptosuite !== standardsW3cDid.DidCryptoSuites.EdDSAJcs2022) {
595
663
  failure = immutableProofModels.ImmutableProofFailure.CryptoSuiteMismatch;
596
664
  }
@@ -626,84 +694,21 @@ class ImmutableProofService {
626
694
  * @internal
627
695
  */
628
696
  async generateHashData(nodeIdentity, immutableProof) {
629
- const canonicalDocument = core.JsonHelper.canonicalize(core.ObjectHelper.omit(immutableProof, ["proof"]));
630
- const proofConfigKey = await this._vaultConnector.getKey(`${nodeIdentity}/${this._proofConfigKeyId}`);
631
- const proofConfigHash = crypto.Sha256.sum256(proofConfigKey.privateKey);
697
+ // We hash the data for the proof without the the proof or immutable receipt for the proof
698
+ // without these objects we can simplify the context
699
+ const object = core.ObjectHelper.omit(immutableProof, ["proof", "immutableReceipt"]);
700
+ object["@context"] = [immutableProofModels.ImmutableProofTypes.ContextRoot, immutableProofModels.ImmutableProofTypes.ContextRootCommon];
701
+ const canonicalDocument = core.JsonHelper.canonicalize(object);
702
+ const proofKey = await this._vaultConnector.getKey(`${nodeIdentity}/${this._proofHashKeyId}`);
703
+ const proofHash = crypto.Sha256.sum256(proofKey.privateKey);
632
704
  const transformedDocumentHash = crypto.Sha256.sum256(core.Converter.utf8ToBytes(canonicalDocument));
633
- const hashData = new Uint8Array(proofConfigHash.length + transformedDocumentHash.length);
634
- hashData.set(proofConfigHash);
635
- hashData.set(transformedDocumentHash, proofConfigHash.length);
705
+ const hashData = new Uint8Array(proofHash.length + transformedDocumentHash.length);
706
+ hashData.set(proofHash);
707
+ hashData.set(transformedDocumentHash, proofHash.length);
636
708
  return hashData;
637
709
  }
638
710
  }
639
711
 
640
- // Copyright 2024 IOTA Stiftung.
641
- // SPDX-License-Identifier: Apache-2.0.
642
- /**
643
- * Class describing the immutable proof.
644
- */
645
- exports.ImmutableProof = class ImmutableProof {
646
- /**
647
- * The id of the proof.
648
- */
649
- id;
650
- /**
651
- * The identity of the node which controls the proof.
652
- */
653
- nodeIdentity;
654
- /**
655
- * The identity of the user which created the proof.
656
- */
657
- userIdentity;
658
- /**
659
- * The date/time of when the proof was created.
660
- */
661
- dateCreated;
662
- /**
663
- * The associated id for the item.
664
- */
665
- proofObjectId;
666
- /**
667
- * The associated hash for the item.
668
- */
669
- proofObjectHash;
670
- /**
671
- * The immutable storage id.
672
- */
673
- immutableStorageId;
674
- };
675
- __decorate([
676
- entity.property({ type: "string", isPrimary: true }),
677
- __metadata("design:type", String)
678
- ], exports.ImmutableProof.prototype, "id", void 0);
679
- __decorate([
680
- entity.property({ type: "string" }),
681
- __metadata("design:type", String)
682
- ], exports.ImmutableProof.prototype, "nodeIdentity", void 0);
683
- __decorate([
684
- entity.property({ type: "string" }),
685
- __metadata("design:type", String)
686
- ], exports.ImmutableProof.prototype, "userIdentity", void 0);
687
- __decorate([
688
- entity.property({ type: "string", format: "date-time", sortDirection: entity.SortDirection.Descending }),
689
- __metadata("design:type", String)
690
- ], exports.ImmutableProof.prototype, "dateCreated", void 0);
691
- __decorate([
692
- entity.property({ type: "string" }),
693
- __metadata("design:type", String)
694
- ], exports.ImmutableProof.prototype, "proofObjectId", void 0);
695
- __decorate([
696
- entity.property({ type: "string" }),
697
- __metadata("design:type", String)
698
- ], exports.ImmutableProof.prototype, "proofObjectHash", void 0);
699
- __decorate([
700
- entity.property({ type: "string" }),
701
- __metadata("design:type", String)
702
- ], exports.ImmutableProof.prototype, "immutableStorageId", void 0);
703
- exports.ImmutableProof = __decorate([
704
- entity.entity()
705
- ], exports.ImmutableProof);
706
-
707
712
  const restEntryPoints = [
708
713
  {
709
714
  name: "immutable-proof",