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

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
  }
@@ -166,8 +233,8 @@ function generateRestRoutesImmutableProof(baseRouteName, componentName) {
166
233
  operationId: "immutableProofVerify",
167
234
  summary: "Verify a proof",
168
235
  tag: tagsImmutableProof[0].name,
169
- method: "POST",
170
- path: `${baseRouteName}/:id`,
236
+ method: "GET",
237
+ path: `${baseRouteName}/:id/verify`,
171
238
  handler: async (httpRequestContext, request) => immutableProofVerify(httpRequestContext, componentName, request),
172
239
  requestType: {
173
240
  type: "IImmutableProofVerifyRequest",
@@ -177,13 +244,6 @@ function generateRestRoutesImmutableProof(baseRouteName, componentName) {
177
244
  request: {
178
245
  pathParams: {
179
246
  id: "ais:1234567890"
180
- },
181
- body: {
182
- proofObject: {
183
- "@context": "http://schema.org",
184
- type: "Person",
185
- name: "John Smith"
186
- }
187
247
  }
188
248
  }
189
249
  }
@@ -279,10 +339,9 @@ async function immutableProofVerify(httpRequestContext, componentName, request)
279
339
  core.Guards.object(ROUTES_SOURCE, "request", request);
280
340
  core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
281
341
  core.Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
282
- core.Guards.object(ROUTES_SOURCE, "request.body.proofObject", request.body.proofObject);
283
342
  const mimeType = request.headers?.[web.HeaderTypes.Accept] === web.MimeTypes.JsonLd ? "jsonld" : "json";
284
343
  const component = core.ComponentFactory.get(componentName);
285
- const result = await component.verify(request.pathParams.id, request.body.proofObject);
344
+ const result = await component.verify(request.pathParams.id);
286
345
  return {
287
346
  headers: {
288
347
  [web.HeaderTypes.ContentType]: mimeType === "json" ? web.MimeTypes.Json : web.MimeTypes.JsonLd
@@ -331,39 +390,50 @@ class ImmutableProofService {
331
390
  */
332
391
  _immutableStorage;
333
392
  /**
334
- * The assertion method id to use for the proofs.
393
+ * The background task connector.
394
+ * @internal
395
+ */
396
+ _backgroundTaskConnector;
397
+ /**
398
+ * The event bus component.
399
+ * @internal
400
+ */
401
+ _eventBusComponent;
402
+ /**
403
+ * The verification method id to use for the proofs.
335
404
  * @internal
336
405
  */
337
- _assertionMethodId;
406
+ _verificationMethodId;
338
407
  /**
339
- * The proof config key id to use for the proofs.
408
+ * The proof hash key id to use for the proofs.
340
409
  * @internal
341
410
  */
342
- _proofConfigKeyId;
411
+ _proofHashKeyId;
343
412
  /**
344
- * Are we currently processing proofs.
413
+ * The identity connector type.
345
414
  * @internal
346
415
  */
347
- _processing;
416
+ _identityConnectorType;
348
417
  /**
349
418
  * Create a new instance of ImmutableProofService.
350
419
  * @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
420
  */
357
421
  constructor(options) {
358
422
  this._vaultConnector = vaultModels.VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
359
423
  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");
424
+ this._immutableStorage = immutableStorageModels.ImmutableStorageConnectorFactory.get(options?.immutableStorageType ?? "immutable-storage");
425
+ this._identityConnectorType = options?.identityConnectorType ?? "identity";
426
+ this._identityConnector = identityModels.IdentityConnectorFactory.get(this._identityConnectorType);
427
+ this._backgroundTaskConnector = backgroundTaskModels.BackgroundTaskConnectorFactory.get(options?.backgroundTaskConnectorType ?? "background-task");
428
+ if (core.Is.stringValue(options?.eventBusComponentType)) {
429
+ this._eventBusComponent = core.ComponentFactory.get(options.eventBusComponentType);
430
+ }
362
431
  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;
432
+ this._verificationMethodId = this._config.verificationMethodId ?? "immutable-proof-assertion";
433
+ this._proofHashKeyId = this._config.proofHashKeyId ?? "immutable-proof-hash";
434
+ this._backgroundTaskConnector.registerHandler("immutable-proof", "@twin.org/immutable-proof-task", "processProofTask", async (task) => {
435
+ await this.finaliseTask(task);
436
+ });
367
437
  }
368
438
  /**
369
439
  * Create a new authentication proof.
@@ -393,7 +463,16 @@ class ImmutableProofService {
393
463
  proofObjectHash: core.Converter.bytesToBase64(hash)
394
464
  };
395
465
  await this._proofStorage.set(proofEntity);
396
- this.startProcessingProofs();
466
+ const immutableProof = this.proofEntityToJsonLd(proofEntity);
467
+ const hashData = await this.generateHashData(proofEntity.nodeIdentity, immutableProof);
468
+ const proofTaskPayload = {
469
+ proofId: id,
470
+ nodeIdentity,
471
+ identityConnectorType: this._identityConnectorType,
472
+ assertionMethodId: this._verificationMethodId,
473
+ hashData: core.Converter.bytesToHex(hashData)
474
+ };
475
+ await this._backgroundTaskConnector.create("immutable-proof", proofTaskPayload);
397
476
  return new core.Urn(ImmutableProofService.NAMESPACE, id).toString();
398
477
  }
399
478
  catch (error) {
@@ -416,7 +495,7 @@ class ImmutableProofService {
416
495
  });
417
496
  }
418
497
  try {
419
- const { immutableProof } = await this.internalGet(id);
498
+ const { immutableProof } = await this.internalGet(id, false);
420
499
  const compacted = await dataJsonLd.JsonLdProcessor.compact(immutableProof, immutableProof["@context"]);
421
500
  return compacted;
422
501
  }
@@ -427,11 +506,10 @@ class ImmutableProofService {
427
506
  /**
428
507
  * Verify an authentication proof.
429
508
  * @param id The id of the proof to verify.
430
- * @param proofObject The object to verify as JSON-LD.
431
509
  * @returns The result of the verification and any failures.
432
510
  * @throws NotFoundError if the proof is not found.
433
511
  */
434
- async verify(id, proofObject) {
512
+ async verify(id) {
435
513
  core.Guards.stringValue(this.CLASS_NAME, "id", id);
436
514
  const urnParsed = core.Urn.fromValidString(id);
437
515
  if (urnParsed.namespaceIdentifier() !== ImmutableProofService.NAMESPACE) {
@@ -441,7 +519,7 @@ class ImmutableProofService {
441
519
  });
442
520
  }
443
521
  try {
444
- const { verified, failure } = await this.internalGet(id, proofObject);
522
+ const { verified, failure } = await this.internalGet(id, true);
445
523
  return {
446
524
  "@context": immutableProofModels.ImmutableProofTypes.ContextRoot,
447
525
  type: immutableProofModels.ImmutableProofTypes.ImmutableProofVerification,
@@ -507,13 +585,9 @@ class ImmutableProofService {
507
585
  * @returns The model.
508
586
  * @internal
509
587
  */
510
- proofEntityToModel(proofEntity) {
588
+ proofEntityToJsonLd(proofEntity) {
511
589
  const model = {
512
- "@context": [
513
- immutableProofModels.ImmutableProofTypes.ContextRoot,
514
- dataSchemaOrg.SchemaOrgTypes.ContextRoot,
515
- standardsW3cDid.DidContexts.ContextVCDataIntegrity
516
- ],
590
+ "@context": immutableProofModels.ImmutableProofTypes.ContextRoot,
517
591
  type: immutableProofModels.ImmutableProofTypes.ImmutableProof,
518
592
  id: proofEntity.id,
519
593
  userIdentity: proofEntity.userIdentity,
@@ -523,74 +597,61 @@ class ImmutableProofService {
523
597
  return model;
524
598
  }
525
599
  /**
526
- * Start processing proofs.
527
- * @returns Nothing.
600
+ * Process a proof.
601
+ * @param proofEntity The proof entity to process.
528
602
  * @internal
529
603
  */
530
- startProcessingProofs() {
531
- if (!this._processing) {
532
- setTimeout(async () => {
533
- await this.processProofs();
534
- }, 0);
535
- }
536
- }
537
- /**
538
- * Process the proofs.
539
- * @internal
540
- */
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
604
+ async finaliseTask(task) {
605
+ if (task.status === backgroundTaskModels.TaskStatus.Success && core.Is.object(task.payload) && core.Is.object(task.result)) {
606
+ const proofEntity = await this._proofStorage.get(task.payload.proofId);
607
+ if (core.Is.object(proofEntity)) {
608
+ const immutableProof = this.proofEntityToJsonLd(proofEntity);
609
+ // As we are adding the proof to the data we update its context
610
+ immutableProof["@context"] = [
611
+ immutableProofModels.ImmutableProofTypes.ContextRoot,
612
+ standardsW3cDid.DidContexts.ContextVCDataIntegrity
613
+ ];
614
+ immutableProof.proof = task.result.proof;
615
+ if (core.Is.stringValue(immutableProof.proof.created)) {
616
+ proofEntity.dateCreated = immutableProof.proof.created;
617
+ }
618
+ const compacted = await dataJsonLd.JsonLdProcessor.compact(immutableProof, immutableProof["@context"]);
619
+ const immutableStoreResult = await this._immutableStorage.store(proofEntity.nodeIdentity, core.ObjectHelper.toBytes(compacted));
620
+ proofEntity.immutableStorageId = immutableStoreResult.id;
621
+ await this._proofStorage.set(proofEntity);
622
+ await this._eventBusComponent?.publish(immutableProofModels.ImmutableProofTopics.ProofCreated, { id: new core.Urn(ImmutableProofService.NAMESPACE, task.payload.proofId).toString() });
552
623
  }
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
624
  }
569
625
  }
570
626
  /**
571
627
  * Verify an authentication proof.
572
628
  * @param id The id of the proof to verify.
573
- * @param proofObject The object to verify as JSON-LD.
629
+ * @param verify Validate the proof.
574
630
  * @returns The result of the verification and any failures.
575
631
  * @throws NotFoundError if the proof is not found.
576
632
  * @internal
577
633
  */
578
- async internalGet(id, proofObject) {
634
+ async internalGet(id, verify) {
579
635
  const urnParsed = core.Urn.fromValidString(id);
580
636
  const proofId = urnParsed.namespaceSpecific(0);
581
637
  const proofEntity = await this._proofStorage.get(proofId);
582
638
  if (core.Is.empty(proofEntity)) {
583
639
  throw new core.NotFoundError(this.CLASS_NAME, "proofNotFound", id);
584
640
  }
585
- let proofModel = await this.proofEntityToModel(proofEntity);
641
+ let proofModel = await this.proofEntityToJsonLd(proofEntity);
586
642
  let verified = false;
587
643
  let failure = immutableProofModels.ImmutableProofFailure.NotIssued;
588
644
  if (core.Is.stringValue(proofEntity.immutableStorageId)) {
589
645
  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)) {
646
+ const immutableResult = await this._immutableStorage.get(proofEntity.immutableStorageId);
647
+ if (core.Is.uint8Array(immutableResult.data)) {
648
+ proofModel = core.ObjectHelper.fromBytes(immutableResult.data);
649
+ proofModel.immutableReceipt = immutableResult.receipt;
650
+ // As we are adding the receipt to the data we update its context
651
+ if (core.Is.array(proofModel["@context"])) {
652
+ proofModel["@context"].push(immutableStorageModels.ImmutableStorageTypes.ContextRoot);
653
+ }
654
+ if (verify && core.Is.object(proofModel.proof)) {
594
655
  if (proofModel.proof.cryptosuite !== standardsW3cDid.DidCryptoSuites.EdDSAJcs2022) {
595
656
  failure = immutableProofModels.ImmutableProofFailure.CryptoSuiteMismatch;
596
657
  }
@@ -626,84 +687,21 @@ class ImmutableProofService {
626
687
  * @internal
627
688
  */
628
689
  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);
690
+ // We hash the data for the proof without the the proof or immutable receipt for the proof
691
+ // without these objects we can simplify the context
692
+ const object = core.ObjectHelper.omit(immutableProof, ["proof", "immutableReceipt"]);
693
+ object["@context"] = immutableProofModels.ImmutableProofTypes.ContextRoot;
694
+ const canonicalDocument = core.JsonHelper.canonicalize(object);
695
+ const proofKey = await this._vaultConnector.getKey(`${nodeIdentity}/${this._proofHashKeyId}`);
696
+ const proofHash = crypto.Sha256.sum256(proofKey.privateKey);
632
697
  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);
698
+ const hashData = new Uint8Array(proofHash.length + transformedDocumentHash.length);
699
+ hashData.set(proofHash);
700
+ hashData.set(transformedDocumentHash, proofHash.length);
636
701
  return hashData;
637
702
  }
638
703
  }
639
704
 
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
705
  const restEntryPoints = [
708
706
  {
709
707
  name: "immutable-proof",