@twin.org/identity-connector-entity-storage 0.0.3-next.3 → 0.0.3-next.30
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 +2 -2
- package/dist/es/entityStorageIdentityConnector.js +179 -27
- package/dist/es/entityStorageIdentityConnector.js.map +1 -1
- package/dist/es/entityStorageIdentityProfileConnector.js.map +1 -1
- package/dist/es/models/IEntityStorageIdentityConnectorConstructorOptions.js.map +1 -1
- package/dist/types/entityStorageIdentityConnector.d.ts +53 -8
- package/docs/changelog.md +493 -70
- package/docs/examples.md +221 -1
- package/docs/reference/classes/EntityStorageIdentityConnector.md +172 -40
- package/docs/reference/classes/EntityStorageIdentityProfileConnector.md +8 -8
- package/docs/reference/classes/EntityStorageIdentityResolverConnector.md +4 -4
- package/docs/reference/classes/IdentityDocument.md +4 -4
- package/docs/reference/classes/IdentityProfile.md +5 -5
- package/docs/reference/interfaces/IEntityStorageIdentityConnectorConstructorOptions.md +4 -4
- package/docs/reference/interfaces/IEntityStorageIdentityProfileConnectorConstructorOptions.md +2 -2
- package/docs/reference/interfaces/IEntityStorageIdentityResolverConnectorConstructorOptions.md +4 -4
- package/locales/en.json +21 -16
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# TWIN Identity
|
|
1
|
+
# TWIN Identity
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The identity-connector-entity-storage package provides an entity storage backed connector for identity workflows, enabling reliable persistence and retrieval of identity records. It supports implementations that need consistent storage semantics while staying aligned with shared contracts across the ecosystem.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Copyright 2024 IOTA Stiftung.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
-
import { BaseError, BitString, Coerce, Compression, CompressionType, Converter, GeneralError, Guards, Is, JsonHelper, NotFoundError, ObjectHelper, RandomHelper } from "@twin.org/core";
|
|
4
|
-
import { JsonLdProcessor } from "@twin.org/data-json-ld";
|
|
3
|
+
import { ArrayHelper, BaseError, BitString, Coerce, Compression, CompressionType, Converter, GeneralError, Guards, Is, JsonHelper, NotFoundError, ObjectHelper, RandomHelper, Url, Urn } from "@twin.org/core";
|
|
4
|
+
import { JsonLdHelper, JsonLdProcessor } from "@twin.org/data-json-ld";
|
|
5
5
|
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
6
6
|
import { DocumentHelper } from "@twin.org/identity-models";
|
|
7
|
-
import { DidContexts, DidTypes, DidVerificationMethodType, ProofHelper, ProofTypes } from "@twin.org/standards-w3c-did";
|
|
7
|
+
import { DidContexts, DidTypes, DidVerificationMethodType, JwsAlgorithms, ProofHelper, ProofTypes } from "@twin.org/standards-w3c-did";
|
|
8
8
|
import { VaultConnectorFactory, VaultConnectorHelper, VaultKeyType } from "@twin.org/vault-models";
|
|
9
9
|
import { Jwk, Jwt } from "@twin.org/web";
|
|
10
10
|
/**
|
|
@@ -147,9 +147,8 @@ export class EntityStorageIdentityConnector {
|
|
|
147
147
|
if (Is.stringValue(verificationMethodId)) {
|
|
148
148
|
// If there is a verification method id, we will try to get the key from the vault.
|
|
149
149
|
try {
|
|
150
|
-
const defaultMethodId = `${controller}/${verificationMethodId}`;
|
|
151
150
|
// If there is an existing key, we will use it.
|
|
152
|
-
const existingKey = await this._vaultConnector.getKey(
|
|
151
|
+
const existingKey = await this._vaultConnector.getKey(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, verificationMethodId));
|
|
153
152
|
methodKeyPublic = existingKey.publicKey;
|
|
154
153
|
}
|
|
155
154
|
catch { }
|
|
@@ -352,6 +351,81 @@ export class EntityStorageIdentityConnector {
|
|
|
352
351
|
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "removeServiceFailed", undefined, error);
|
|
353
352
|
}
|
|
354
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Add an alias to the alsoKnownAs property on the document.
|
|
356
|
+
* If the alias is already present the operation is a no-op.
|
|
357
|
+
* @param controller The controller of the identity who can make changes.
|
|
358
|
+
* @param documentId The id of the document to update.
|
|
359
|
+
* @param alias The alias to add. Must be a Url or Urn (typically another DID).
|
|
360
|
+
* @returns Nothing.
|
|
361
|
+
* @throws GeneralError if the alias is not a Url or Urn.
|
|
362
|
+
* @throws NotFoundError if the id can not be resolved.
|
|
363
|
+
*/
|
|
364
|
+
async addAlsoKnownAs(controller, documentId, alias) {
|
|
365
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
366
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "documentId", documentId);
|
|
367
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "alias", alias);
|
|
368
|
+
if (!Url.tryParseExact(alias) && !Urn.tryParseExact(alias)) {
|
|
369
|
+
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "invalidAlias", { alias });
|
|
370
|
+
}
|
|
371
|
+
try {
|
|
372
|
+
const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
|
|
373
|
+
if (Is.undefined(didIdentityDocument)) {
|
|
374
|
+
throw new NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", documentId);
|
|
375
|
+
}
|
|
376
|
+
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
377
|
+
const didDocument = didIdentityDocument.document;
|
|
378
|
+
const existing = Is.array(didDocument.alsoKnownAs) ? didDocument.alsoKnownAs : [];
|
|
379
|
+
if (existing.includes(alias)) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
didDocument.alsoKnownAs = [...existing, alias];
|
|
383
|
+
await this.updateDocument(controller, didDocument);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "addAlsoKnownAsFailed", undefined, error);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Remove an alias from the alsoKnownAs property on the document.
|
|
391
|
+
* If the alias is not present the operation is a no-op.
|
|
392
|
+
* @param controller The controller of the identity who can make changes.
|
|
393
|
+
* @param documentId The id of the document to update.
|
|
394
|
+
* @param alias The alias to remove. Must be a Url or Urn.
|
|
395
|
+
* @returns Nothing.
|
|
396
|
+
* @throws GeneralError if the alias is not a Url or Urn.
|
|
397
|
+
* @throws NotFoundError if the id can not be resolved.
|
|
398
|
+
*/
|
|
399
|
+
async removeAlsoKnownAs(controller, documentId, alias) {
|
|
400
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
401
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "documentId", documentId);
|
|
402
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "alias", alias);
|
|
403
|
+
if (!Url.tryParseExact(alias) && !Urn.tryParseExact(alias)) {
|
|
404
|
+
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "invalidAlias", { alias });
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
|
|
408
|
+
if (Is.undefined(didIdentityDocument)) {
|
|
409
|
+
throw new NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", documentId);
|
|
410
|
+
}
|
|
411
|
+
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
412
|
+
const didDocument = didIdentityDocument.document;
|
|
413
|
+
if (!Is.array(didDocument.alsoKnownAs) || !didDocument.alsoKnownAs.includes(alias)) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const filtered = didDocument.alsoKnownAs.filter(a => a !== alias);
|
|
417
|
+
if (filtered.length === 0) {
|
|
418
|
+
delete didDocument.alsoKnownAs;
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
didDocument.alsoKnownAs = filtered;
|
|
422
|
+
}
|
|
423
|
+
await this.updateDocument(controller, didDocument);
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "removeAlsoKnownAsFailed", undefined, error);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
355
429
|
/**
|
|
356
430
|
* Create a verifiable credential for a verification method.
|
|
357
431
|
* @param controller The controller of the identity who can make changes.
|
|
@@ -361,6 +435,8 @@ export class EntityStorageIdentityConnector {
|
|
|
361
435
|
* @param options Additional options for creating the verifiable credential.
|
|
362
436
|
* @param options.revocationIndex The bitmap revocation index of the credential, if undefined will not have revocation status.
|
|
363
437
|
* @param options.expirationDate The date the verifiable credential is valid until.
|
|
438
|
+
* @param options.jwtHeaderFields Additional fields to add to the JWT header.
|
|
439
|
+
* @param options.jwtPayloadFields Additional fields to add to the JWT payload.
|
|
364
440
|
* @returns The created verifiable credential and its token.
|
|
365
441
|
* @throws NotFoundError if the id can not be resolved.
|
|
366
442
|
*/
|
|
@@ -371,6 +447,9 @@ export class EntityStorageIdentityConnector {
|
|
|
371
447
|
if (!Is.undefined(options?.revocationIndex)) {
|
|
372
448
|
Guards.number(EntityStorageIdentityConnector.CLASS_NAME, "options.revocationIndex", options.revocationIndex);
|
|
373
449
|
}
|
|
450
|
+
if (!Is.undefined(options?.expirationDate)) {
|
|
451
|
+
Guards.date(EntityStorageIdentityConnector.CLASS_NAME, "options.expirationDate", options.expirationDate);
|
|
452
|
+
}
|
|
374
453
|
try {
|
|
375
454
|
const idParts = DocumentHelper.parseId(verificationMethodId);
|
|
376
455
|
if (Is.empty(idParts.fragment)) {
|
|
@@ -412,7 +491,8 @@ export class EntityStorageIdentityConnector {
|
|
|
412
491
|
finalTypes.push(credType);
|
|
413
492
|
}
|
|
414
493
|
const verifiableCredential = {
|
|
415
|
-
"@context": JsonLdProcessor.combineContexts(DidContexts.ContextVCv1, credContext)
|
|
494
|
+
"@context": (JsonLdProcessor.combineContexts(DidContexts.ContextVCv1, credContext) ??
|
|
495
|
+
DidContexts.ContextVCv1),
|
|
416
496
|
id,
|
|
417
497
|
type: finalTypes,
|
|
418
498
|
credentialSubject: subjectClone,
|
|
@@ -432,9 +512,10 @@ export class EntityStorageIdentityConnector {
|
|
|
432
512
|
: undefined
|
|
433
513
|
};
|
|
434
514
|
const jwtHeader = {
|
|
515
|
+
...options?.jwtHeaderFields,
|
|
435
516
|
kid: verificationDidMethod.id,
|
|
436
517
|
typ: "JWT",
|
|
437
|
-
alg:
|
|
518
|
+
alg: JwsAlgorithms.EdDSA
|
|
438
519
|
};
|
|
439
520
|
const jwtVc = ObjectHelper.pick(ObjectHelper.clone(verifiableCredential), [
|
|
440
521
|
"@context",
|
|
@@ -442,6 +523,15 @@ export class EntityStorageIdentityConnector {
|
|
|
442
523
|
"credentialSubject",
|
|
443
524
|
"credentialStatus"
|
|
444
525
|
]);
|
|
526
|
+
// Add the proof to the VC after extracting the jwt data
|
|
527
|
+
// as the jwt does not include the proof
|
|
528
|
+
verifiableCredential.proof = await this.createProof(controller, verificationMethodId, ProofTypes.DataIntegrityProof, JsonLdHelper.toNodeObject(verifiableCredential));
|
|
529
|
+
// As we are adding the receipt to the data we update the JSON-LD context
|
|
530
|
+
const proofContext = verifiableCredential.proof["@context"];
|
|
531
|
+
if (!Is.empty(proofContext)) {
|
|
532
|
+
verifiableCredential["@context"] = (JsonLdProcessor.combineContexts(verifiableCredential["@context"], proofContext) ?? verifiableCredential["@context"]);
|
|
533
|
+
delete verifiableCredential.proof["@context"];
|
|
534
|
+
}
|
|
445
535
|
if (Is.array(jwtVc.credentialSubject)) {
|
|
446
536
|
jwtVc.credentialSubject = jwtVc.credentialSubject.map(c => {
|
|
447
537
|
ObjectHelper.propertyDelete(c, "id");
|
|
@@ -452,12 +542,16 @@ export class EntityStorageIdentityConnector {
|
|
|
452
542
|
ObjectHelper.propertyDelete(jwtVc.credentialSubject, "id");
|
|
453
543
|
}
|
|
454
544
|
const jwtPayload = {
|
|
545
|
+
...options?.jwtPayloadFields,
|
|
455
546
|
iss: idParts.id,
|
|
456
547
|
nbf: Math.floor(Date.now() / 1000),
|
|
457
548
|
jti: verifiableCredential.id,
|
|
458
549
|
sub: credId,
|
|
459
550
|
vc: jwtVc
|
|
460
551
|
};
|
|
552
|
+
if (Is.date(options?.expirationDate)) {
|
|
553
|
+
jwtPayload.exp = Math.floor(options.expirationDate.getTime() / 1000);
|
|
554
|
+
}
|
|
461
555
|
const signature = await Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (header, payload) => VaultConnectorHelper.jwtSigner(this._vaultConnector, EntityStorageIdentityConnector.buildVaultKey(idParts.id, idParts.fragment ?? ""), header, payload));
|
|
462
556
|
return {
|
|
463
557
|
verifiableCredential,
|
|
@@ -470,13 +564,23 @@ export class EntityStorageIdentityConnector {
|
|
|
470
564
|
}
|
|
471
565
|
/**
|
|
472
566
|
* Check a verifiable credential is valid.
|
|
473
|
-
* @param
|
|
567
|
+
* @param credential The credential to verify.
|
|
474
568
|
* @returns The credential stored in the jwt and the revocation status.
|
|
475
569
|
*/
|
|
476
|
-
async checkVerifiableCredential(
|
|
477
|
-
|
|
570
|
+
async checkVerifiableCredential(credential) {
|
|
571
|
+
if (Is.object(credential)) {
|
|
572
|
+
Guards.objectValue(EntityStorageIdentityConnector.CLASS_NAME, "credential", credential);
|
|
573
|
+
Guards.objectValue(EntityStorageIdentityConnector.CLASS_NAME, "credential.proof", credential.proof);
|
|
574
|
+
const { proof, ...doc } = credential;
|
|
575
|
+
await this.verifyProof(JsonLdHelper.toNodeObject(doc), ArrayHelper.fromObjectOrArray(proof)[0]);
|
|
576
|
+
return {
|
|
577
|
+
revoked: false,
|
|
578
|
+
verifiableCredential: doc
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "credential", credential);
|
|
478
582
|
try {
|
|
479
|
-
const jwtDecoded = await Jwt.decode(
|
|
583
|
+
const jwtDecoded = await Jwt.decode(credential);
|
|
480
584
|
const jwtHeader = jwtDecoded.header;
|
|
481
585
|
const jwtPayload = jwtDecoded.payload;
|
|
482
586
|
const jwtSignature = jwtDecoded.signature;
|
|
@@ -511,7 +615,7 @@ export class EntityStorageIdentityConnector {
|
|
|
511
615
|
method: jwtHeader.kid
|
|
512
616
|
});
|
|
513
617
|
}
|
|
514
|
-
await Jwt.verifySignature(
|
|
618
|
+
await Jwt.verifySignature(credential, await Jwk.toCryptoKey(didMethod.publicKeyJwk));
|
|
515
619
|
const verifiableCredential = jwtPayload.vc;
|
|
516
620
|
if (Is.object(verifiableCredential)) {
|
|
517
621
|
if (Is.string(jwtPayload.jti)) {
|
|
@@ -641,11 +745,14 @@ export class EntityStorageIdentityConnector {
|
|
|
641
745
|
* @param contexts The contexts for the data stored in the verifiable credential.
|
|
642
746
|
* @param types The types for the data stored in the verifiable credential.
|
|
643
747
|
* @param verifiableCredentials The credentials to use for creating the presentation in jwt format.
|
|
644
|
-
* @param
|
|
748
|
+
* @param options Additional options for creating the verifiable presentation.
|
|
749
|
+
* @param options.expirationDate The date the verifiable presentation is valid until.
|
|
750
|
+
* @param options.jwtHeaderFields Additional fields to add to the JWT header.
|
|
751
|
+
* @param options.jwtPayloadFields Additional fields to add to the JWT payload.
|
|
645
752
|
* @returns The created verifiable presentation and its token.
|
|
646
753
|
* @throws NotFoundError if the id can not be resolved.
|
|
647
754
|
*/
|
|
648
|
-
async createVerifiablePresentation(controller, verificationMethodId, presentationId, contexts, types, verifiableCredentials,
|
|
755
|
+
async createVerifiablePresentation(controller, verificationMethodId, presentationId, contexts, types, verifiableCredentials, options) {
|
|
649
756
|
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
650
757
|
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "verificationMethodId", verificationMethodId);
|
|
651
758
|
if (Is.array(types)) {
|
|
@@ -655,8 +762,8 @@ export class EntityStorageIdentityConnector {
|
|
|
655
762
|
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "types", types);
|
|
656
763
|
}
|
|
657
764
|
Guards.arrayValue(EntityStorageIdentityConnector.CLASS_NAME, "verifiableCredentials", verifiableCredentials);
|
|
658
|
-
if (!Is.undefined(
|
|
659
|
-
Guards.
|
|
765
|
+
if (!Is.undefined(options?.expirationDate)) {
|
|
766
|
+
Guards.date(EntityStorageIdentityConnector.CLASS_NAME, "options.expirationDate", options.expirationDate);
|
|
660
767
|
}
|
|
661
768
|
try {
|
|
662
769
|
const idParts = DocumentHelper.parseId(verificationMethodId);
|
|
@@ -694,31 +801,37 @@ export class EntityStorageIdentityConnector {
|
|
|
694
801
|
else if (Is.stringValue(types)) {
|
|
695
802
|
finalTypes.push(types);
|
|
696
803
|
}
|
|
804
|
+
const combinedContext = JsonLdProcessor.combineContexts(DidContexts.ContextVCv1, contexts) ??
|
|
805
|
+
DidContexts.ContextVCv1;
|
|
697
806
|
const verifiablePresentation = {
|
|
698
|
-
"@context":
|
|
807
|
+
"@context": combinedContext,
|
|
699
808
|
id: presentationId,
|
|
700
809
|
type: finalTypes,
|
|
701
810
|
verifiableCredential: verifiableCredentials,
|
|
702
811
|
holder: idParts.id
|
|
703
812
|
};
|
|
704
813
|
const jwtHeader = {
|
|
814
|
+
...options?.jwtHeaderFields,
|
|
705
815
|
kid: didMethod.id,
|
|
706
816
|
typ: "JWT",
|
|
707
|
-
alg:
|
|
817
|
+
alg: JwsAlgorithms.EdDSA
|
|
708
818
|
};
|
|
709
819
|
const jwtVp = ObjectHelper.pick(ObjectHelper.clone(verifiablePresentation), [
|
|
710
820
|
"@context",
|
|
711
821
|
"type",
|
|
712
822
|
"verifiableCredential"
|
|
713
823
|
]);
|
|
824
|
+
// Add the proof to the VP after extracting the jwt data
|
|
825
|
+
// as the jwt does not include the proof
|
|
826
|
+
verifiablePresentation.proof = await this.createProof(controller, verificationMethodId, ProofTypes.DataIntegrityProof, JsonLdHelper.toNodeObject(verifiablePresentation));
|
|
714
827
|
const jwtPayload = {
|
|
715
|
-
|
|
828
|
+
...options?.jwtPayloadFields,
|
|
829
|
+
iss: verifiablePresentation.holder,
|
|
716
830
|
nbf: Math.floor(Date.now() / 1000),
|
|
717
831
|
vp: jwtVp
|
|
718
832
|
};
|
|
719
|
-
if (Is.
|
|
720
|
-
|
|
721
|
-
jwtPayload.exp = Math.floor(Date.now() / 1000) + expiresInSeconds;
|
|
833
|
+
if (Is.date(options?.expirationDate)) {
|
|
834
|
+
jwtPayload.exp = Math.floor(options.expirationDate.getTime() / 1000);
|
|
722
835
|
}
|
|
723
836
|
const signature = await Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (header, payload) => VaultConnectorHelper.jwtSigner(this._vaultConnector, EntityStorageIdentityConnector.buildVaultKey(idParts.id, idParts.fragment ?? ""), header, payload));
|
|
724
837
|
return {
|
|
@@ -732,11 +845,19 @@ export class EntityStorageIdentityConnector {
|
|
|
732
845
|
}
|
|
733
846
|
/**
|
|
734
847
|
* Check a verifiable presentation is valid.
|
|
735
|
-
* @param
|
|
848
|
+
* @param presentation The presentation to verify.
|
|
736
849
|
* @returns The presentation stored in the jwt and the revocation status.
|
|
737
850
|
*/
|
|
738
|
-
async checkVerifiablePresentation(
|
|
739
|
-
|
|
851
|
+
async checkVerifiablePresentation(presentation) {
|
|
852
|
+
if (Is.object(presentation)) {
|
|
853
|
+
const { proof, ...doc } = presentation;
|
|
854
|
+
const proofEntry = ArrayHelper.fromObjectOrArray(proof)[0];
|
|
855
|
+
Guards.objectValue(EntityStorageIdentityConnector.CLASS_NAME, "proofEntry", proofEntry);
|
|
856
|
+
await this.verifyProof(JsonLdHelper.toNodeObject(doc), proofEntry);
|
|
857
|
+
return { revoked: false, verifiablePresentation: doc };
|
|
858
|
+
}
|
|
859
|
+
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "presentation", presentation);
|
|
860
|
+
const presentationJwt = presentation;
|
|
740
861
|
try {
|
|
741
862
|
const jwtDecoded = await Jwt.decode(presentationJwt);
|
|
742
863
|
const jwtHeader = jwtDecoded.header;
|
|
@@ -821,11 +942,15 @@ export class EntityStorageIdentityConnector {
|
|
|
821
942
|
}
|
|
822
943
|
/**
|
|
823
944
|
* Create a proof for arbitrary data with the specified verification method.
|
|
945
|
+
* This method uses async signing to ensure the private key never leaves the vault,
|
|
946
|
+
* with algorithm validation to ensure key type compatibility.
|
|
824
947
|
* @param controller The controller of the identity who can make changes.
|
|
825
948
|
* @param verificationMethodId The verification method id to use.
|
|
826
949
|
* @param proofType The type of proof to create.
|
|
827
950
|
* @param unsecureDocument The unsecure document to create the proof for.
|
|
828
951
|
* @returns The proof.
|
|
952
|
+
* @throws NotFoundError if the identity or method is not found.
|
|
953
|
+
* @throws GeneralError if algorithm doesn't match key type or proof creation fails.
|
|
829
954
|
*/
|
|
830
955
|
async createProof(controller, verificationMethodId, proofType, unsecureDocument) {
|
|
831
956
|
Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
@@ -862,8 +987,14 @@ export class EntityStorageIdentityConnector {
|
|
|
862
987
|
});
|
|
863
988
|
}
|
|
864
989
|
const vaultKey = EntityStorageIdentityConnector.buildVaultKey(didDocument.id, idParts.fragment ?? "");
|
|
865
|
-
const
|
|
866
|
-
|
|
990
|
+
const keyType = await this._vaultConnector.getKeyType(vaultKey);
|
|
991
|
+
if (Is.undefined(keyType)) {
|
|
992
|
+
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "privateKeyMissing", {
|
|
993
|
+
keyId: vaultKey
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
const unsignedProof = ProofHelper.createUnsignedProof(proofType, verificationMethodId);
|
|
997
|
+
const signedProof = await ProofHelper.createProofWithSigner(proofType, unsecureDocument, unsignedProof, async (data, algorithm) => this.signWithVault(vaultKey, keyType, data, algorithm));
|
|
867
998
|
return signedProof;
|
|
868
999
|
}
|
|
869
1000
|
catch (error) {
|
|
@@ -916,6 +1047,27 @@ export class EntityStorageIdentityConnector {
|
|
|
916
1047
|
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "verifyProofFailed", undefined, error);
|
|
917
1048
|
}
|
|
918
1049
|
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Signs data using the vault connector with algorithm validation.
|
|
1052
|
+
* @param vaultKey The vault key identifier.
|
|
1053
|
+
* @param keyType The type of the key.
|
|
1054
|
+
* @param data The data to sign.
|
|
1055
|
+
* @param algorithm The signing algorithm.
|
|
1056
|
+
* @returns The signature bytes.
|
|
1057
|
+
* @throws GeneralError if algorithm doesn't match key type.
|
|
1058
|
+
* @internal
|
|
1059
|
+
*/
|
|
1060
|
+
async signWithVault(vaultKey, keyType, data, algorithm) {
|
|
1061
|
+
if (algorithm === JwsAlgorithms.EdDSA && keyType !== VaultKeyType.Ed25519) {
|
|
1062
|
+
throw new GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "algorithmKeyTypeMismatch", {
|
|
1063
|
+
algorithm,
|
|
1064
|
+
expectedKeyType: VaultKeyType.Ed25519,
|
|
1065
|
+
actualKeyType: keyType,
|
|
1066
|
+
keyId: vaultKey
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
return this._vaultConnector.sign(vaultKey, data);
|
|
1070
|
+
}
|
|
919
1071
|
/**
|
|
920
1072
|
* Get all the methods from a document.
|
|
921
1073
|
* @param document The document to get the methods from.
|