@@ -465,7 +464,7 @@ A method-agnostic DID Document.
* [._shallowCloneInternal()](#CoreDocument+_shallowCloneInternal) ⇒ [CoreDocument](#CoreDocument)
* [._strongCountInternal()](#CoreDocument+_strongCountInternal) ⇒ number
* [.toJSON()](#CoreDocument+toJSON) ⇒ any
- * [.generateMethod(storage, keyType, alg, fragment, scope)](#CoreDocument+generateMethod) ⇒ Promise.<(string\|null)>
+ * [.generateMethod(storage, keyType, alg, fragment, scope)](#CoreDocument+generateMethod) ⇒ Promise.<string>
* [.purgeMethod(storage, id)](#CoreDocument+purgeMethod) ⇒ Promise.<void>
* [.createJws(storage, fragment, payload, options)](#CoreDocument+createJws) ⇒ [Promise.<Jws>](#Jws)
* [.createCredentialJwt(storage, fragment, credential, options)](#CoreDocument+createCredentialJwt) ⇒ [Promise.<Jwt>](#Jwt)
@@ -834,7 +833,7 @@ Serializes to a plain JS representation.
**Kind**: instance method of [CoreDocument](#CoreDocument)
-### coreDocument.generateMethod(storage, keyType, alg, fragment, scope) ⇒ Promise.<(string\|null)>
+### coreDocument.generateMethod(storage, keyType, alg, fragment, scope) ⇒ Promise.<string>
Generate new key material in the given `storage` and insert a new verification method with the corresponding
public key material into the DID document.
@@ -888,7 +887,7 @@ See [RFC7515 section 3.1](https://www.rfc-editor.org/rfc/rfc7515#section-3.1).
### coreDocument.createCredentialJwt(storage, fragment, credential, options) ⇒ [Promise.<Jwt>](#Jwt)
-Produces a JWS where the payload is produced from the given `credential`
+Produces a JWT where the payload is produced from the given `credential`
in accordance with [VC-JWT version 1.1.](https://w3c.github.io/vc-jwt/#version-1.1).
The `kid` in the protected header is the `id` of the method identified by `fragment` and the JWS signature will be
@@ -906,6 +905,12 @@ produced by the corresponding private key backed by the `storage` in accordance
### coreDocument.createPresentationJwt(storage, fragment, presentation, signature_options, presentation_options) ⇒ [Promise.<Jwt>](#Jwt)
+Produces a JWT where the payload is produced from the given presentation.
+in accordance with [VC-JWT version 1.1](https://w3c.github.io/vc-jwt/#version-1.1).
+
+The `kid` in the protected header is the `id` of the method identified by `fragment` and the JWS signature will be
+produced by the corresponding private key backed by the `storage` in accordance with the passed `options`.
+
**Kind**: instance method of [CoreDocument](#CoreDocument)
| Param | Type |
@@ -1561,9 +1566,10 @@ It does not imply anything about a potentially present proof property on the pre
* [DecodedJwtPresentation](#DecodedJwtPresentation)
* [.presentation()](#DecodedJwtPresentation+presentation) ⇒ [JwtPresentation](#JwtPresentation)
* [.protectedHeader()](#DecodedJwtPresentation+protectedHeader) ⇒ [JwsHeader](#JwsHeader)
- * [.intoCredential()](#DecodedJwtPresentation+intoCredential) ⇒ [JwtPresentation](#JwtPresentation)
+ * [.intoPresentation()](#DecodedJwtPresentation+intoPresentation) ⇒ [JwtPresentation](#JwtPresentation)
* [.expirationDate()](#DecodedJwtPresentation+expirationDate) ⇒ [Timestamp](#Timestamp) \| undefined
* [.issuanceDate()](#DecodedJwtPresentation+issuanceDate) ⇒ [Timestamp](#Timestamp) \| undefined
+ * [.audience()](#DecodedJwtPresentation+audience) ⇒ string \| undefined
* [.credentials()](#DecodedJwtPresentation+credentials) ⇒ [Array.<DecodedJwtCredential>](#DecodedJwtCredential)
@@ -1576,9 +1582,9 @@ It does not imply anything about a potentially present proof property on the pre
Returns a copy of the protected header parsed from the decoded JWS.
**Kind**: instance method of [DecodedJwtPresentation](#DecodedJwtPresentation)
-
+
-### decodedJwtPresentation.intoCredential() ⇒ [JwtPresentation](#JwtPresentation)
+### decodedJwtPresentation.intoPresentation() ⇒ [JwtPresentation](#JwtPresentation)
Consumes the object and returns the decoded presentation.
### Warning
@@ -1594,7 +1600,13 @@ The expiration date parsed from the JWT claims.
### decodedJwtPresentation.issuanceDate() ⇒ [Timestamp](#Timestamp) \| undefined
-The issuance dated parsed from the JWT claims.
+The issuance date parsed from the JWT claims.
+
+**Kind**: instance method of [DecodedJwtPresentation](#DecodedJwtPresentation)
+
+
+### decodedJwtPresentation.audience() ⇒ string \| undefined
+The `aud` property parsed from JWT claims.
**Kind**: instance method of [DecodedJwtPresentation](#DecodedJwtPresentation)
@@ -1611,16 +1623,15 @@ It can be placed in an origin's `.well-known` directory to prove linkage between
See:
Note:
-- Only [Linked Data Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#linked-data-proof-format)
- is supported.
+- Only the [JSON Web Token Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#json-web-token-proof-format)
**Kind**: global class
* [DomainLinkageConfiguration](#DomainLinkageConfiguration)
* [new DomainLinkageConfiguration(linked_dids)](#new_DomainLinkageConfiguration_new)
* _instance_
- * [.linkedDids()](#DomainLinkageConfiguration+linkedDids) ⇒ [Array.<Credential>](#Credential)
- * [.issuers()](#DomainLinkageConfiguration+issuers) ⇒ Array.<string>
+ * [.linkedDids()](#DomainLinkageConfiguration+linkedDids) ⇒ [Array.<Jwt>](#Jwt)
+ * [.issuers()](#DomainLinkageConfiguration+issuers) ⇒ [Array.<CoreDID>](#CoreDID)
* [.toJSON()](#DomainLinkageConfiguration+toJSON) ⇒ any
* [.clone()](#DomainLinkageConfiguration+clone) ⇒ [DomainLinkageConfiguration](#DomainLinkageConfiguration)
* _static_
@@ -1634,17 +1645,17 @@ Constructs a new `DomainLinkageConfiguration`.
| Param | Type |
| --- | --- |
-| linked_dids | [Array.<Credential>](#Credential) |
+| linked_dids | [Array.<Jwt>](#Jwt) |
-### domainLinkageConfiguration.linkedDids() ⇒ [Array.<Credential>](#Credential)
+### domainLinkageConfiguration.linkedDids() ⇒ [Array.<Jwt>](#Jwt)
List of the Domain Linkage Credentials.
**Kind**: instance method of [DomainLinkageConfiguration](#DomainLinkageConfiguration)
-### domainLinkageConfiguration.issuers() ⇒ Array.<string>
+### domainLinkageConfiguration.issuers() ⇒ [Array.<CoreDID>](#CoreDID)
List of the issuers of the Domain Linkage Credentials.
**Kind**: instance method of [DomainLinkageConfiguration](#DomainLinkageConfiguration)
@@ -1679,20 +1690,32 @@ A validator for a Domain Linkage Configuration and Credentials.
**Kind**: global class
* [DomainLinkageValidator](#DomainLinkageValidator)
- * [.validateLinkage(issuer, configuration, domain, options)](#DomainLinkageValidator.validateLinkage)
- * [.validateCredential(issuer, credential, domain, options)](#DomainLinkageValidator.validateCredential)
+ * [new DomainLinkageValidator(signatureVerifier)](#new_DomainLinkageValidator_new)
+ * [.validateLinkage(issuer, configuration, domain, options)](#DomainLinkageValidator+validateLinkage)
+ * [.validateCredential(issuer, credentialJwt, domain, options)](#DomainLinkageValidator+validateCredential)
+
+
+
+### new DomainLinkageValidator(signatureVerifier)
+Creates a new `DomainLinkageValidator`. If a `signatureVerifier` is provided it will be used when
+verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
+algorithm will be used.
+
+
+| Param | Type |
+| --- | --- |
+| signatureVerifier | IJwsVerifier \| undefined |
-
+
-### DomainLinkageValidator.validateLinkage(issuer, configuration, domain, options)
+### domainLinkageValidator.validateLinkage(issuer, configuration, domain, options)
Validates the linkage between a domain and a DID.
[`DomainLinkageConfiguration`] is validated according to [DID Configuration Resource Verification](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource-verification).
Linkage is valid if no error is thrown.
# Note:
-- Only [Linked Data Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#linked-data-proof-format)
- is supported.
+- Only the [JSON Web Token Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#json-web-token-proof-format)
- Only the Credential issued by `issuer` is verified.
# Errors
@@ -1700,29 +1723,29 @@ Linkage is valid if no error is thrown.
- `configuration` includes multiple credentials issued by `issuer`.
- Validation of the matched Domain Linkage Credential fails.
-**Kind**: static method of [DomainLinkageValidator](#DomainLinkageValidator)
+**Kind**: instance method of [DomainLinkageValidator](#DomainLinkageValidator)
| Param | Type |
| --- | --- |
| issuer | [CoreDocument](#CoreDocument) \| IToCoreDocument |
| configuration | [DomainLinkageConfiguration](#DomainLinkageConfiguration) |
| domain | string |
-| options | [CredentialValidationOptions](#CredentialValidationOptions) |
+| options | [JwtCredentialValidationOptions](#JwtCredentialValidationOptions) |
-
+
-### DomainLinkageValidator.validateCredential(issuer, credential, domain, options)
+### domainLinkageValidator.validateCredential(issuer, credentialJwt, domain, options)
Validates a [Domain Linkage Credential](https://identity.foundation/.well-known/resources/did-configuration/#domain-linkage-credential).
Error will be thrown in case the validation fails.
-**Kind**: static method of [DomainLinkageValidator](#DomainLinkageValidator)
+**Kind**: instance method of [DomainLinkageValidator](#DomainLinkageValidator)
| Param | Type |
| --- | --- |
| issuer | [CoreDocument](#CoreDocument) \| IToCoreDocument |
-| credential | [Credential](#Credential) |
+| credentialJwt | [Jwt](#Jwt) |
| domain | string |
-| options | [CredentialValidationOptions](#CredentialValidationOptions) |
+| options | [JwtCredentialValidationOptions](#JwtCredentialValidationOptions) |
@@ -2137,7 +2160,7 @@ Deserializes an instance from a JSON object.
* [._strongCountInternal()](#IotaDocument+_strongCountInternal) ⇒ number
* [.toJSON()](#IotaDocument+toJSON) ⇒ any
* [.toCoreDocument()](#IotaDocument+toCoreDocument) ⇒ [CoreDocument](#CoreDocument)
- * [.generateMethod(storage, keyType, alg, fragment, scope)](#IotaDocument+generateMethod) ⇒ Promise.<(string\|null)>
+ * [.generateMethod(storage, keyType, alg, fragment, scope)](#IotaDocument+generateMethod) ⇒ Promise.<string>
* [.purgeMethod(storage, id)](#IotaDocument+purgeMethod) ⇒ Promise.<void>
* [.createJwt(storage, fragment, payload, options)](#IotaDocument+createJwt) ⇒ [Promise.<Jws>](#Jws)
* [.createCredentialJwt(storage, fragment, credential, options)](#IotaDocument+createCredentialJwt) ⇒ [Promise.<Jwt>](#Jwt)
@@ -2579,7 +2602,7 @@ Transforms the `IotaDocument` to its `CoreDocument` representation.
**Kind**: instance method of [IotaDocument](#IotaDocument)
-### iotaDocument.generateMethod(storage, keyType, alg, fragment, scope) ⇒ Promise.<(string\|null)>
+### iotaDocument.generateMethod(storage, keyType, alg, fragment, scope) ⇒ Promise.<string>
Generate new key material in the given `storage` and insert a new verification method with the corresponding
public key material into the DID document.
@@ -2651,7 +2674,7 @@ produced by the corresponding private key backed by the `storage` in accordance
### iotaDocument.createPresentationJwt(storage, fragment, presentation, signature_options, presentation_options) ⇒ [Promise.<Jwt>](#Jwt)
-Produces a JWT where the payload is produced from the given `presentation`
+Produces a JWT where the payload is produced from the given presentation.
in accordance with [VC-JWT version 1.1](https://w3c.github.io/vc-jwt/#version-1.1).
The `kid` in the protected header is the `id` of the method identified by `fragment` and the JWS signature will be
@@ -3684,7 +3707,12 @@ A wrapper around a JSON Web Token (JWK).
* [Jwt](#Jwt)
* [new Jwt(jwt_string)](#new_Jwt_new)
- * [.toString()](#Jwt+toString) ⇒ string
+ * _instance_
+ * [.toString()](#Jwt+toString) ⇒ string
+ * [.toJSON()](#Jwt+toJSON) ⇒ any
+ * [.clone()](#Jwt+clone) ⇒ [Jwt](#Jwt)
+ * _static_
+ * [.fromJSON(json)](#Jwt.fromJSON) ⇒ [Jwt](#Jwt)
@@ -3702,6 +3730,29 @@ Creates a new `Jwt` from the given string.
Returns a clone of the JWT string.
**Kind**: instance method of [Jwt](#Jwt)
+
+
+### jwt.toJSON() ⇒ any
+Serializes this to a JSON object.
+
+**Kind**: instance method of [Jwt](#Jwt)
+
+
+### jwt.clone() ⇒ [Jwt](#Jwt)
+Deep clones the object.
+
+**Kind**: instance method of [Jwt](#Jwt)
+
+
+### Jwt.fromJSON(json) ⇒ [Jwt](#Jwt)
+Deserializes an instance from a JSON object.
+
+**Kind**: static method of [Jwt](#Jwt)
+
+| Param | Type |
+| --- | --- |
+| json | any |
+
## JwtCredentialValidationOptions
@@ -3763,7 +3814,7 @@ A type for decoding and validating `Credentials`.
**Kind**: global class
* [JwtCredentialValidator](#JwtCredentialValidator)
- * [new JwtCredentialValidator(signature_verifier)](#new_JwtCredentialValidator_new)
+ * [new JwtCredentialValidator(signatureVerifier)](#new_JwtCredentialValidator_new)
* _instance_
* [.validate(credential_jwt, issuer, options, fail_fast)](#JwtCredentialValidator+validate) ⇒ [DecodedJwtCredential](#DecodedJwtCredential)
* [.verifySignature(credential, trustedIssuers, options)](#JwtCredentialValidator+verifySignature) ⇒ [DecodedJwtCredential](#DecodedJwtCredential)
@@ -3776,15 +3827,15 @@ A type for decoding and validating `Credentials`.
-### new JwtCredentialValidator(signature_verifier)
-Creates a new `JwtCredentialValidator`. If a `signature_verifier` is provided it will be used when
+### new JwtCredentialValidator(signatureVerifier)
+Creates a new `JwtCredentialValidator`. If a `signatureVerifier` is provided it will be used when
verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
algorithm will be used.
| Param | Type |
| --- | --- |
-| signature_verifier | IJwsVerifier \| undefined |
+| signatureVerifier | IJwsVerifier \| undefined |
@@ -3973,7 +4024,7 @@ Returns a copy of the URIs defining the type of the presentation.
### jwtPresentation.verifiableCredential() ⇒ [Array.<Jwt>](#Jwt)
-Returns a copy of the [Credential](#Credential)(s) expressing the claims of the presentation.
+Returns the JWT credentials expressing the claims of the presentation.
**Kind**: instance method of [JwtPresentation](#JwtPresentation)
@@ -3997,7 +4048,7 @@ Returns a copy of the terms-of-use specified by the presentation holder
### jwtPresentation.proof() ⇒ Map.<string, any> \| undefined
-Returns a copy of the proof property.
+Optional proof that can be verified by users in addition to JWS.
**Kind**: instance method of [JwtPresentation](#JwtPresentation)
@@ -4161,7 +4212,7 @@ Deserializes an instance from a JSON object.
* [JwtPresentationValidator](#JwtPresentationValidator)
* [new JwtPresentationValidator(signature_verifier)](#new_JwtPresentationValidator_new)
* _instance_
- * [.validate(presentation_jwt, holder, issuers, options, fail_fast)](#JwtPresentationValidator+validate) ⇒ [DecodedJwtPresentation](#DecodedJwtPresentation)
+ * [.validate(presentation_jwt, holder, issuers, validation_options, fail_fast)](#JwtPresentationValidator+validate) ⇒ [DecodedJwtPresentation](#DecodedJwtPresentation)
* _static_
* [.checkStructure(presentation)](#JwtPresentationValidator.checkStructure)
* [.extractDids(presentation)](#JwtPresentationValidator.extractDids) ⇒ JwtPresentationDids
@@ -4180,7 +4231,34 @@ algorithm will be used.
-### jwtPresentationValidator.validate(presentation_jwt, holder, issuers, options, fail_fast) ⇒ [DecodedJwtPresentation](#DecodedJwtPresentation)
+### jwtPresentationValidator.validate(presentation_jwt, holder, issuers, validation_options, fail_fast) ⇒ [DecodedJwtPresentation](#DecodedJwtPresentation)
+Validates a `JwtPresentation`.
+
+The following properties are validated according to `options`:
+- the JWT can be decoded into semantically valid presentation.
+- the expiration and issuance date contained in the JWT claims.
+- the holder's signature.
+- the relationship between the holder and the credential subjects.
+- the signatures and some properties of the constituent credentials (see `CredentialValidator`).
+
+Validation is done with respect to the properties set in `options`.
+
+# Warning
+The lack of an error returned from this method is in of itself not enough to conclude that the presentation can be
+trusted. This section contains more information on additional checks that should be carried out before and after
+calling this method.
+
+## The state of the supplied DID Documents.
+The caller must ensure that the DID Documents in `holder` and `issuers` are up-to-date.
+
+## Properties that are not validated
+ There are many properties defined in [The Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) that are **not** validated, such as:
+`credentialStatus`, `type`, `credentialSchema`, `refreshService`, **and more**.
+These should be manually checked after validation, according to your requirements.
+
+# Errors
+An error is returned whenever a validated condition is not satisfied or when decoding fails.
+
**Kind**: instance method of [JwtPresentationValidator](#JwtPresentationValidator)
| Param | Type |
@@ -4188,12 +4266,14 @@ algorithm will be used.
| presentation_jwt | [Jwt](#Jwt) |
| holder | [CoreDocument](#CoreDocument) \| IToCoreDocument |
| issuers | Array.<(CoreDocument\|IToCoreDocument)> |
-| options | [JwtPresentationValidationOptions](#JwtPresentationValidationOptions) |
+| validation_options | [JwtPresentationValidationOptions](#JwtPresentationValidationOptions) |
| fail_fast | number |
### JwtPresentationValidator.checkStructure(presentation)
+Validates the semantic structure of the `JwtPresentation`.
+
**Kind**: static method of [JwtPresentationValidator](#JwtPresentationValidator)
| Param | Type |
@@ -4203,6 +4283,14 @@ algorithm will be used.
### JwtPresentationValidator.extractDids(presentation) ⇒ JwtPresentationDids
+Attempt to extract the holder of the presentation and the issuers of the included
+credentials.
+
+# Errors:
+* If deserialization/decoding of the presentation or any of the constituent credentials
+fails.
+* If the holder or any of the issuers can't be parsed as DIDs.
+
**Kind**: static method of [JwtPresentationValidator](#JwtPresentationValidator)
| Param | Type |
diff --git a/bindings/wasm/examples/src/1_advanced/6_domain_linkage.ts b/bindings/wasm/examples/src/1_advanced/6_domain_linkage.ts
index 651c42f2fc..e93e2bdfa7 100644
--- a/bindings/wasm/examples/src/1_advanced/6_domain_linkage.ts
+++ b/bindings/wasm/examples/src/1_advanced/6_domain_linkage.ts
@@ -4,6 +4,7 @@
import { Client, MnemonicSecretManager } from "@iota/client-wasm/node";
import { Bip39 } from "@iota/crypto.js";
import {
+ CoreDID,
Credential,
CredentialValidationOptions,
DIDUrl,
@@ -13,12 +14,17 @@ import {
IotaDID,
IotaDocument,
IotaIdentityClient,
+ JwkMemStore,
+ JwsSignatureOptions,
+ JwtCredentialValidationOptions,
+ KeyIdMemStore,
LinkedDomainService,
ProofOptions,
+ Storage,
Timestamp,
} from "@iota/identity-wasm/node";
import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js";
-import { API_ENDPOINT, createDid } from "../util";
+import { API_ENDPOINT, createDid, createDidStorage } from "../util";
/**
* Demonstrates how to link a domain and a DID and verify the linkage.
@@ -35,8 +41,10 @@ export async function domainLinkage() {
mnemonic: Bip39.randomMnemonic(),
};
+ const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore());
+
// Creates a new wallet and identity (see "0_create_did" example).
- let { document, keypair } = await createDid(client, secretManager);
+ let { document, fragment } = await createDidStorage(client, secretManager, storage);
const did: IotaDID = document.id();
// =====================================================
@@ -74,19 +82,19 @@ export async function domainLinkage() {
});
// Sign the credential.
- domainLinkageCredential = document.signCredential(
+ const credentialJwt = await document.createCredentialJwt(
+ storage,
+ fragment,
domainLinkageCredential,
- keypair.private(),
- "#key-1",
- ProofOptions.default(),
+ new JwsSignatureOptions(),
);
// Create the DID Configuration Resource which wraps the Domain Linkage credential.
- let configurationResource: DomainLinkageConfiguration = new DomainLinkageConfiguration([domainLinkageCredential]);
+ let configurationResource: DomainLinkageConfiguration = new DomainLinkageConfiguration([credentialJwt]);
// The DID Configuration resource can be made available on `https://foo.example.com/.well-known/did-configuration.json`.
let configurationResourceJson = configurationResource.toJSON();
- console.log("Configuration Resource:", JSON.stringify(configurationResource.toJSON(), null, 2));
+ console.log("Configuration Resource:", JSON.stringify(configurationResourceJson, null, 2));
// Now the DID Document links to the Domains through the service, and the Foo domain links to the DID
// through the DID Configuration resource. A bidirectional linkage is established.
@@ -114,16 +122,16 @@ export async function domainLinkage() {
// Retrieve the issuers of the Domain Linkage Credentials which correspond to the possibly linked DIDs.
// Note that in this example only the first entry in the credential is validated.
- let issuers: Array = fetchedConfigurationResource.issuers();
- const issuerDocument: IotaDocument = await didClient.resolveDid(IotaDID.parse(issuers[0]));
+ let issuers: Array = fetchedConfigurationResource.issuers();
+ const issuerDocument: IotaDocument = await didClient.resolveDid(IotaDID.parse(issuers[0].toString()));
// Validate the linkage between the Domain Linkage Credential in the configuration and the provided issuer DID.
// Validation succeeds when no error is thrown.
- DomainLinkageValidator.validateLinkage(
+ new DomainLinkageValidator().validateLinkage(
issuerDocument,
fetchedConfigurationResource,
domainFoo,
- CredentialValidationOptions.default(),
+ JwtCredentialValidationOptions.default(),
);
// =====================================================
@@ -153,12 +161,14 @@ export async function domainLinkage() {
// Validate the linkage between the Domain Linkage Credential in the configuration and the provided issuer DID.
// Validation succeeds when no error is thrown.
- DomainLinkageValidator.validateLinkage(
+ new DomainLinkageValidator().validateLinkage(
didDocument,
fetchedConfigurationResource,
domains[0],
- CredentialValidationOptions.default(),
+ JwtCredentialValidationOptions.default(),
);
+
+ console.log("Successfully validated Domain Linkage!");
}
async function publishDocument(
diff --git a/bindings/wasm/examples/src/util.ts b/bindings/wasm/examples/src/util.ts
index d6a38d687d..367958187a 100644
--- a/bindings/wasm/examples/src/util.ts
+++ b/bindings/wasm/examples/src/util.ts
@@ -3,9 +3,12 @@ import {
IotaDID,
IotaDocument,
IotaIdentityClient,
+ JwkMemStore,
+ JwsAlgorithm,
KeyPair,
KeyType,
MethodScope,
+ Storage,
VerificationMethod,
} from "@iota/identity-wasm/node";
import { AddressTypes, Bech32Helper, IAliasOutput } from "@iota/iota.js";
@@ -15,6 +18,53 @@ export const FAUCET_ENDPOINT = "http://localhost:8091/api/enqueue";
/** Creates a DID Document and publishes it in a new Alias Output.
+Its functionality is equivalent to the "create DID" example
+and exists for convenient calling from the other examples. */
+export async function createDidStorage(client: Client, secretManager: SecretManager, storage: Storage): Promise<{
+ address: AddressTypes;
+ document: IotaDocument;
+ fragment: string;
+}> {
+ const didClient = new IotaIdentityClient(client);
+ const networkHrp: string = await didClient.getNetworkHrp();
+
+ const walletAddressBech32 = (await client.generateAddresses(secretManager, {
+ accountIndex: 0,
+ range: {
+ start: 0,
+ end: 1,
+ },
+ }))[0];
+ console.log("Wallet address Bech32:", walletAddressBech32);
+
+ await ensureAddressHasFunds(client, walletAddressBech32);
+
+ const address = Bech32Helper.addressFromBech32(walletAddressBech32, networkHrp);
+
+ // Create a new DID document with a placeholder DID.
+ // The DID will be derived from the Alias Id of the Alias Output after publishing.
+ const document = new IotaDocument(networkHrp);
+
+ const fragment = await document.generateMethod(
+ storage,
+ JwkMemStore.ed25519KeyType(),
+ JwsAlgorithm.EdDSA,
+ "#jwk",
+ MethodScope.AssertionMethod(),
+ );
+
+ // Construct an Alias Output containing the DID document, with the wallet address
+ // set as both the state controller and governor.
+ const aliasOutput: IAliasOutput = await didClient.newDidOutput(address, document);
+
+ // Publish the Alias Output and get the published DID document.
+ const published = await didClient.publishDidOutput(secretManager, aliasOutput);
+
+ return { address, document: published, fragment };
+}
+
+/** Creates a DID Document and publishes it in a new Alias Output.
+
Its functionality is equivalent to the "create DID" example
and exists for convenient calling from the other examples. */
export async function createDid(client: Client, secretManager: SecretManager): Promise<{
diff --git a/bindings/wasm/src/credential/domain_linkage_configuration.rs b/bindings/wasm/src/credential/domain_linkage_configuration.rs
index 6df4810471..77cfb59e8a 100644
--- a/bindings/wasm/src/credential/domain_linkage_configuration.rs
+++ b/bindings/wasm/src/credential/domain_linkage_configuration.rs
@@ -1,24 +1,26 @@
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-use crate::common::ArrayString;
-use crate::credential::ArrayCredential;
-use crate::credential::WasmCredential;
+use crate::credential::ArrayCoreDID;
+use crate::credential::WasmJwt;
+use crate::did::WasmCoreDID;
use crate::error::Result;
use crate::error::WasmResult;
-use identity_iota::credential::Credential;
+
use identity_iota::credential::DomainLinkageConfiguration;
+use identity_iota::credential::Jwt;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
+use super::ArrayJwt;
+
/// DID Configuration Resource which contains Domain Linkage Credentials.
/// It can be placed in an origin's `.well-known` directory to prove linkage between the origin and a DID.
/// See:
///
/// Note:
-/// - Only [Linked Data Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#linked-data-proof-format)
-/// is supported.
+/// - Only the [JSON Web Token Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#json-web-token-proof-format)
#[wasm_bindgen(js_name = DomainLinkageConfiguration, inspectable)]
pub struct WasmDomainLinkageConfiguration(pub(crate) DomainLinkageConfiguration);
@@ -26,35 +28,39 @@ pub struct WasmDomainLinkageConfiguration(pub(crate) DomainLinkageConfiguration)
impl WasmDomainLinkageConfiguration {
/// Constructs a new `DomainLinkageConfiguration`.
#[wasm_bindgen(constructor)]
- pub fn new(linked_dids: ArrayCredential) -> Result {
- let wasm_credentials: Vec = linked_dids.into_serde().wasm_result()?;
+ pub fn new(linked_dids: &ArrayJwt) -> Result {
+ let wasm_credentials: Vec = linked_dids.into_serde().wasm_result()?;
Ok(Self(DomainLinkageConfiguration::new(wasm_credentials)))
}
/// List of the Domain Linkage Credentials.
#[wasm_bindgen(js_name = linkedDids)]
- pub fn linked_dids(&self) -> ArrayCredential {
+ pub fn linked_dids(&self) -> ArrayJwt {
self
.0
.linked_dids()
.iter()
.cloned()
- .map(WasmCredential::from)
+ .map(WasmJwt::from)
.map(JsValue::from)
.collect::()
- .unchecked_into::()
+ .unchecked_into::()
}
/// List of the issuers of the Domain Linkage Credentials.
#[wasm_bindgen]
- pub fn issuers(&self) -> ArrayString {
- self
- .0
- .issuers()
- .map(|url| url.to_string())
- .map(JsValue::from)
- .collect::()
- .unchecked_into::()
+ pub fn issuers(&self) -> Result {
+ Ok(
+ self
+ .0
+ .issuers()
+ .wasm_result()?
+ .into_iter()
+ .map(WasmCoreDID::from)
+ .map(JsValue::from)
+ .collect::()
+ .unchecked_into::(),
+ )
}
}
diff --git a/bindings/wasm/src/credential/domain_linkage_credential_builder.rs b/bindings/wasm/src/credential/domain_linkage_credential_builder.rs
index bf9e9fc6dc..562d7977f5 100644
--- a/bindings/wasm/src/credential/domain_linkage_credential_builder.rs
+++ b/bindings/wasm/src/credential/domain_linkage_credential_builder.rs
@@ -5,7 +5,7 @@ use crate::error::WasmResult;
use identity_iota::core::Timestamp;
use identity_iota::core::Url;
use identity_iota::credential::DomainLinkageCredentialBuilder;
-use identity_iota::credential::Issuer;
+use identity_iota::did::CoreDID;
use proc_typescript::typescript;
use wasm_bindgen::prelude::*;
@@ -49,8 +49,8 @@ extern "C" {
#[typescript(name = "IDomainLinkageCredential", readonly, optional)]
struct IDomainLinkageCredentialHelper {
/// A reference to the issuer of the `Credential`.
- #[typescript(optional = false, type = "string | CoreDID | IotaDID | Issuer")]
- issuer: Option,
+ #[typescript(optional = false, type = "CoreDID | IotaDID")]
+ issuer: Option,
/// A timestamp of when the `Credential` becomes valid. Defaults to the current datetime.
#[typescript(name = "issuanceDate", type = "Timestamp")]
issuance_date: Option,
diff --git a/bindings/wasm/src/credential/domain_linkage_validator.rs b/bindings/wasm/src/credential/domain_linkage_validator.rs
index 8733f67f07..c553f830de 100644
--- a/bindings/wasm/src/credential/domain_linkage_validator.rs
+++ b/bindings/wasm/src/credential/domain_linkage_validator.rs
@@ -3,30 +3,45 @@
use crate::common::ImportedDocumentLock;
use crate::credential::WasmDomainLinkageConfiguration;
+use crate::credential::WasmJwtCredentialValidationOptions;
use crate::did::IToCoreDocument;
use crate::error::Result;
use crate::error::WasmResult;
+use crate::verification::IJwsVerifier;
+use crate::verification::WasmJwsVerifier;
use identity_iota::core::Url;
use identity_iota::credential::DomainLinkageValidator;
use wasm_bindgen::prelude::wasm_bindgen;
-use super::WasmCredential;
-use super::WasmCredentialValidationOptions;
+use super::WasmJwt;
/// A validator for a Domain Linkage Configuration and Credentials.
#[wasm_bindgen(js_name = DomainLinkageValidator)]
-pub struct WasmDomainLinkageValidator;
+pub struct WasmDomainLinkageValidator {
+ validator: DomainLinkageValidator,
+}
#[wasm_bindgen(js_class = DomainLinkageValidator)]
impl WasmDomainLinkageValidator {
+ /// Creates a new `DomainLinkageValidator`. If a `signatureVerifier` is provided it will be used when
+ /// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
+ /// algorithm will be used.
+ #[wasm_bindgen(constructor)]
+ #[allow(non_snake_case)]
+ pub fn new(signatureVerifier: Option) -> WasmDomainLinkageValidator {
+ let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
+ WasmDomainLinkageValidator {
+ validator: DomainLinkageValidator::with_signature_verifier(signature_verifier),
+ }
+ }
+
/// Validates the linkage between a domain and a DID.
/// [`DomainLinkageConfiguration`] is validated according to [DID Configuration Resource Verification](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource-verification).
///
/// Linkage is valid if no error is thrown.
///
/// # Note:
- /// - Only [Linked Data Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#linked-data-proof-format)
- /// is supported.
+ /// - Only the [JSON Web Token Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#json-web-token-proof-format)
/// - Only the Credential issued by `issuer` is verified.
///
/// # Errors
@@ -35,29 +50,38 @@ impl WasmDomainLinkageValidator {
/// - Validation of the matched Domain Linkage Credential fails.
#[wasm_bindgen(js_name = validateLinkage)]
pub fn validate_linkage(
+ &self,
issuer: &IToCoreDocument,
configuration: &WasmDomainLinkageConfiguration,
domain: &str,
- options: &WasmCredentialValidationOptions,
+ options: &WasmJwtCredentialValidationOptions,
) -> Result<()> {
let domain = Url::parse(domain).wasm_result()?;
let doc = ImportedDocumentLock::from(issuer);
let doc_guard = doc.blocking_read();
- DomainLinkageValidator::validate_linkage(&doc_guard, &configuration.0, &domain, &options.0).wasm_result()
+ self
+ .validator
+ .validate_linkage(&doc_guard, &configuration.0, &domain, &options.0)
+ .wasm_result()
}
/// Validates a [Domain Linkage Credential](https://identity.foundation/.well-known/resources/did-configuration/#domain-linkage-credential).
/// Error will be thrown in case the validation fails.
#[wasm_bindgen(js_name = validateCredential)]
+ #[allow(non_snake_case)]
pub fn validate_credential(
+ &self,
issuer: &IToCoreDocument,
- credential: &WasmCredential,
+ credentialJwt: &WasmJwt,
domain: &str,
- options: &WasmCredentialValidationOptions,
+ options: &WasmJwtCredentialValidationOptions,
) -> Result<()> {
let domain = Url::parse(domain).wasm_result()?;
let doc = ImportedDocumentLock::from(issuer);
let doc_guard = doc.blocking_read();
- DomainLinkageValidator::validate_credential(&doc_guard, &credential.0, &domain, &options.0).wasm_result()
+ self
+ .validator
+ .validate_credential(&doc_guard, &credentialJwt.0, &domain, &options.0)
+ .wasm_result()
}
}
diff --git a/bindings/wasm/src/credential/jwt.rs b/bindings/wasm/src/credential/jwt.rs
index efa7b732e8..270963fe5e 100644
--- a/bindings/wasm/src/credential/jwt.rs
+++ b/bindings/wasm/src/credential/jwt.rs
@@ -25,3 +25,18 @@ impl WasmJwt {
self.0.as_str().to_owned()
}
}
+
+impl_wasm_json!(WasmJwt, Jwt);
+impl_wasm_clone!(WasmJwt, Jwt);
+
+impl From for WasmJwt {
+ fn from(value: Jwt) -> Self {
+ WasmJwt(value)
+ }
+}
+
+impl From for Jwt {
+ fn from(value: WasmJwt) -> Self {
+ value.0
+ }
+}
diff --git a/bindings/wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs b/bindings/wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs
index 866da4a76b..c4d402c1f9 100644
--- a/bindings/wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs
+++ b/bindings/wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs
@@ -36,12 +36,13 @@ pub struct WasmJwtCredentialValidator(JwtCredentialValidator);
#[wasm_bindgen(js_class = JwtCredentialValidator)]
impl WasmJwtCredentialValidator {
- /// Creates a new `JwtCredentialValidator`. If a `signature_verifier` is provided it will be used when
+ /// Creates a new `JwtCredentialValidator`. If a `signatureVerifier` is provided it will be used when
/// verifying decoded JWS signatures, otherwise the default which is only capable of handling the `EdDSA`
/// algorithm will be used.
#[wasm_bindgen(constructor)]
- pub fn new(signature_verifier: Option) -> WasmJwtCredentialValidator {
- let signature_verifier = WasmJwsVerifier::new(signature_verifier);
+ #[allow(non_snake_case)]
+ pub fn new(signatureVerifier: Option) -> WasmJwtCredentialValidator {
+ let signature_verifier = WasmJwsVerifier::new(signatureVerifier);
WasmJwtCredentialValidator(JwtCredentialValidator::with_signature_verifier(signature_verifier))
}
diff --git a/bindings/wasm/src/did/wasm_core_document.rs b/bindings/wasm/src/did/wasm_core_document.rs
index 6b0150692b..2bd30c52e4 100644
--- a/bindings/wasm/src/did/wasm_core_document.rs
+++ b/bindings/wasm/src/did/wasm_core_document.rs
@@ -11,10 +11,11 @@ use crate::common::ArrayString;
use crate::common::ArrayVerificationMethod;
use crate::common::MapStringAny;
use crate::common::OptionOneOrManyString;
-use crate::common::PromiseOptionString;
+use crate::common::PromiseString;
use crate::common::PromiseVoid;
use crate::common::UDIDUrlQuery;
use crate::common::UOneOrManyNumber;
+use crate::credential::ArrayCoreDID;
use crate::credential::WasmCredential;
use crate::credential::WasmJws;
use crate::credential::WasmJwt;
@@ -640,7 +641,7 @@ impl WasmCoreDocument {
alg: WasmJwsAlgorithm,
fragment: Option,
scope: WasmMethodScope,
- ) -> Result {
+ ) -> Result {
let alg: JwsAlgorithm = alg.into_serde().wasm_result()?;
let document_lock_clone: Rc = self.0.clone();
let storage_clone: Rc = storage.0.clone();
@@ -780,12 +781,8 @@ extern "C" {
#[wasm_bindgen(typescript_type = "ICoreDocument")]
pub type ICoreDocument;
- #[wasm_bindgen(typescript_type = "CoreDID[]")]
- pub type ArrayCoreDID;
-
#[wasm_bindgen(typescript_type = "CoreDID | CoreDID[] | null")]
pub type OptionOneOrManyCoreDID;
-
}
#[derive(Deserialize)]
diff --git a/bindings/wasm/src/iota/iota_document.rs b/bindings/wasm/src/iota/iota_document.rs
index ff965ef8b0..e680e651b4 100644
--- a/bindings/wasm/src/iota/iota_document.rs
+++ b/bindings/wasm/src/iota/iota_document.rs
@@ -40,7 +40,7 @@ use crate::common::ArrayVerificationMethod;
use crate::common::MapStringAny;
use crate::common::OptionOneOrManyString;
use crate::common::OptionTimestamp;
-use crate::common::PromiseOptionString;
+use crate::common::PromiseString;
use crate::common::PromiseVoid;
use crate::common::UDIDUrlQuery;
use crate::common::UOneOrManyNumber;
@@ -758,7 +758,7 @@ impl WasmIotaDocument {
alg: WasmJwsAlgorithm,
fragment: Option,
scope: WasmMethodScope,
- ) -> Result {
+ ) -> Result {
let alg: JwsAlgorithm = alg.into_serde().wasm_result()?;
let document_lock_clone: Rc = self.0.clone();
let storage_clone: Rc = storage.0.clone();
diff --git a/examples/1_advanced/7_domain_linkage.rs b/examples/1_advanced/7_domain_linkage.rs
index 1e43b74a6b..bb95a88a97 100644
--- a/examples/1_advanced/7_domain_linkage.rs
+++ b/examples/1_advanced/7_domain_linkage.rs
@@ -1,8 +1,9 @@
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-use examples::create_did;
+use examples::create_did_storage;
use examples::random_stronghold_path;
+use examples::MemStorage;
use examples::API_ENDPOINT;
use identity_iota::core::Duration;
use identity_iota::core::FromJson;
@@ -11,16 +12,15 @@ use identity_iota::core::OrderedSet;
use identity_iota::core::Timestamp;
use identity_iota::core::ToJson;
use identity_iota::core::Url;
+use identity_iota::credential::vc_jwt_validation::CredentialValidationOptions;
use identity_iota::credential::Credential;
-use identity_iota::credential::CredentialValidationOptions;
use identity_iota::credential::DomainLinkageConfiguration;
use identity_iota::credential::DomainLinkageCredentialBuilder;
use identity_iota::credential::DomainLinkageValidationError;
use identity_iota::credential::DomainLinkageValidator;
-use identity_iota::credential::Issuer;
+use identity_iota::credential::Jwt;
use identity_iota::credential::LinkedDomainService;
-use identity_iota::crypto::KeyPair;
-use identity_iota::crypto::ProofOptions;
+use identity_iota::did::CoreDID;
use identity_iota::did::DIDUrl;
use identity_iota::did::DID;
use identity_iota::iota::IotaClientExt;
@@ -28,6 +28,10 @@ use identity_iota::iota::IotaDID;
use identity_iota::iota::IotaDocument;
use identity_iota::iota::IotaIdentityClientExt;
use identity_iota::resolver::Resolver;
+use identity_iota::storage::JwkDocumentExt;
+use identity_iota::storage::JwkMemStore;
+use identity_iota::storage::JwsSignatureOptions;
+use identity_iota::storage::KeyIdMemstore;
use iota_sdk::client::secret::stronghold::StrongholdSecretManager;
use iota_sdk::client::secret::SecretManager;
use iota_sdk::client::Client;
@@ -52,8 +56,9 @@ async fn main() -> anyhow::Result<()> {
);
// Create a DID for the entity that will issue the Domain Linkage Credential.
- let (_, mut did_document, keypair): (Address, IotaDocument, KeyPair) =
- create_did(&client, &mut secret_manager).await?;
+ let storage: MemStorage = MemStorage::new(JwkMemStore::new(), KeyIdMemstore::new());
+ let (_, mut did_document, fragment): (Address, IotaDocument, String) =
+ create_did_storage(&client, &mut secret_manager, &storage).await?;
let did: IotaDID = did_document.id().clone();
// =====================================================
@@ -88,25 +93,26 @@ async fn main() -> anyhow::Result<()> {
// and can be made available on the domain.
// Create the Domain Linkage Credential.
- let mut domain_linkage_credential: Credential = DomainLinkageCredentialBuilder::new()
- .issuer(Issuer::Url(updated_did_document.id().to_url().into()))
+ let domain_linkage_credential: Credential = DomainLinkageCredentialBuilder::new()
+ .issuer(updated_did_document.id().clone().into())
.origin(domain_1.clone())
.issuance_date(Timestamp::now_utc())
// Expires after a year.
.expiration_date(Timestamp::now_utc().checked_add(Duration::days(365)).unwrap())
.build()?;
- // Sign the credential.
- updated_did_document.sign_data(
- &mut domain_linkage_credential,
- keypair.private(),
- "#key-1",
- ProofOptions::default(),
- )?;
+ let jwt: Jwt = updated_did_document
+ .sign_credential(
+ &domain_linkage_credential,
+ &storage,
+ &fragment,
+ &JwsSignatureOptions::default(),
+ )
+ .await
+ .unwrap();
// Create the DID Configuration Resource which wraps the Domain Linkage credential.
- let configuration_resource: DomainLinkageConfiguration =
- DomainLinkageConfiguration::new(vec![domain_linkage_credential]);
+ let configuration_resource: DomainLinkageConfiguration = DomainLinkageConfiguration::new(vec![jwt]);
println!("Configuration Resource >>: {configuration_resource:#}");
// The DID Configuration resource can be made available on `https://foo.example.com/.well-known/did-configuration.json`.
@@ -134,7 +140,9 @@ async fn main() -> anyhow::Result<()> {
// Fetch the DID Configuration resource
// let configuration_resource: DomainLinkageConfiguration =
- // DomainLinkageConfiguration::fetch_configuration(domain_foo).await.unwrap();
+ // DomainLinkageConfiguration::fetch_configuration(domain_foo.clone())
+ // .await
+ // .unwrap();
// But since the DID Configuration
// resource isn't available online in this example, we will simply deserialize the JSON.
@@ -142,14 +150,14 @@ async fn main() -> anyhow::Result<()> {
DomainLinkageConfiguration::from_json(&configuration_resource_json)?;
// Retrieve the issuers of the Domain Linkage Credentials which correspond to the possibly linked DIDs.
- let linked_dids: Vec<&Url> = configuration_resource.issuers().collect();
+ let linked_dids: Vec = configuration_resource.issuers()?;
assert_eq!(linked_dids.len(), 1);
// Resolve the DID Document of the DID that issued the credential.
let issuer_did_document: IotaDocument = resolver.resolve(&did).await.unwrap();
// Validate the linkage between the Domain Linkage Credential in the configuration and the provided issuer DID.
- let validation_result: Result<(), DomainLinkageValidationError> = DomainLinkageValidator::validate_linkage(
+ let validation_result: Result<(), DomainLinkageValidationError> = DomainLinkageValidator::new().validate_linkage(
&issuer_did_document,
&configuration_resource,
&domain_foo,
@@ -179,7 +187,7 @@ async fn main() -> anyhow::Result<()> {
// Fetch the DID Configuration resource
// let configuration_resource: DomainLinkageConfiguration =
- // DomainLinkageConfiguration::fetch_configuration(domain_foo).await.unwrap();
+ // DomainLinkageConfiguration::fetch_configuration(domain_foo.clone()).await?;
// But since the DID Configuration
// resource isn't available online in this example, we will simply deserialize the JSON.
@@ -187,7 +195,7 @@ async fn main() -> anyhow::Result<()> {
DomainLinkageConfiguration::from_json(&configuration_resource_json)?;
// Validate the linkage.
- let validation_result: Result<(), DomainLinkageValidationError> = DomainLinkageValidator::validate_linkage(
+ let validation_result: Result<(), DomainLinkageValidationError> = DomainLinkageValidator::new().validate_linkage(
&did_document,
&configuration_resource,
&domain_foo,
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index ee9b518e31..fa320d6dc1 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -7,7 +7,7 @@ publish = false
[dependencies]
anyhow = "1.0.62"
-identity_iota = { path = "../identity_iota" }
+identity_iota = { path = "../identity_iota", features = ["memstore"] }
primitive-types = "0.12.1"
rand = "0.8.5"
tokio = { version = "1.20.1", default-features = false, features = ["rt"] }
diff --git a/examples/utils/utils.rs b/examples/utils/utils.rs
index 3ac61a0a03..58d53fa04f 100644
--- a/examples/utils/utils.rs
+++ b/examples/utils/utils.rs
@@ -12,9 +12,14 @@ use identity_iota::iota::IotaClientExt;
use identity_iota::iota::IotaDocument;
use identity_iota::iota::IotaIdentityClientExt;
use identity_iota::iota::NetworkName;
+use identity_iota::storage::JwkDocumentExt;
+use identity_iota::storage::JwkMemStore;
+use identity_iota::storage::KeyIdMemstore;
+use identity_iota::storage::Storage;
use identity_iota::verification::MethodScope;
use identity_iota::verification::VerificationMethod;
+use identity_iota::verification::jws::JwsAlgorithm;
use iota_sdk::client::node_api::indexer::query_parameters::QueryParameter;
use iota_sdk::client::secret::SecretManager;
use iota_sdk::client::Client;
@@ -25,6 +30,32 @@ use rand::distributions::DistString;
pub static API_ENDPOINT: &str = "http://localhost:14265";
pub static FAUCET_ENDPOINT: &str = "http://localhost:8091/api/enqueue";
+pub type MemStorage = Storage;
+
+/// Creates a DID Document and publishes it in a new Alias Output.
+///
+/// Its functionality is equivalent to the "create DID" example
+/// and exists for convenient calling from the other examples.
+pub async fn create_did_storage(
+ client: &Client,
+ secret_manager: &mut SecretManager,
+ storage: &MemStorage,
+) -> anyhow::Result<(Address, IotaDocument, String)> {
+ let address: Address = get_address_with_funds(client, secret_manager, FAUCET_ENDPOINT)
+ .await
+ .context("failed to get address with funds")?;
+
+ let network_name: NetworkName = client.network_name().await?;
+
+ let (document, fragment): (IotaDocument, String) = create_did_document_storage(&network_name, storage).await?;
+
+ let alias_output: AliasOutput = client.new_did_output(address, document, None).await?;
+
+ let document: IotaDocument = client.publish_did_output(secret_manager, alias_output).await?;
+
+ Ok((address, document, fragment))
+}
+
/// Creates a DID Document and publishes it in a new Alias Output.
///
/// Its functionality is equivalent to the "create DID" example
@@ -48,6 +79,29 @@ pub async fn create_did(
Ok((address, document, key_pair))
}
+/// Creates an example DID document with the given `network_name`.
+///
+/// Its functionality is equivalent to the "create DID" example
+/// and exists for convenient calling from the other examples.
+pub async fn create_did_document_storage(
+ network_name: &NetworkName,
+ storage: &MemStorage,
+) -> anyhow::Result<(IotaDocument, String)> {
+ let mut document: IotaDocument = IotaDocument::new(network_name);
+
+ let fragment: String = document
+ .generate_method(
+ storage,
+ JwkMemStore::ED25519_KEY_TYPE,
+ JwsAlgorithm::EdDSA,
+ None,
+ MethodScope::VerificationMethod,
+ )
+ .await?;
+
+ Ok((document, fragment))
+}
+
/// Creates an example DID document with the given `network_name`.
///
/// Its functionality is equivalent to the "create DID" example
diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml
index 3b8d530eeb..06d1547be0 100644
--- a/identity_credential/Cargo.toml
+++ b/identity_credential/Cargo.toml
@@ -31,6 +31,7 @@ thiserror.workspace = true
url = { version = "2.2", default-features = false }
[dev-dependencies]
+iota-crypto = { version = "0.20", default-features = false, features = ["ed25519", "std", "random"] }
proptest = { version = "1.0.0", default-features = false, features = ["std"] }
serde_json.workspace = true
tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread", "macros"] }
diff --git a/identity_credential/src/credential/domain_linkage_configuration.rs b/identity_credential/src/credential/domain_linkage_configuration.rs
index 87a069b5df..d37ba9f946 100644
--- a/identity_credential/src/credential/domain_linkage_configuration.rs
+++ b/identity_credential/src/credential/domain_linkage_configuration.rs
@@ -1,17 +1,22 @@
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-use crate::credential::Credential;
use crate::error::Result;
+use crate::validator::vc_jwt_validation::CredentialValidator;
+use crate::validator::vc_jwt_validation::ValidationError;
use identity_core::common::Context;
+use identity_core::common::Object;
use identity_core::common::Url;
use identity_core::convert::FmtJson;
+use identity_did::CoreDID;
use serde::Deserialize;
use std::fmt::Display;
use std::fmt::Formatter;
use crate::Error::DomainLinkageError;
+use super::Jwt;
+
lazy_static! {
static ref WELL_KNOWN_CONTEXT: Context =
Context::Url(Url::parse("https://identity.foundation/.well-known/did-configuration/v1").unwrap());
@@ -22,8 +27,7 @@ lazy_static! {
/// See:
///
/// Note:
-/// - Only [Linked Data Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#linked-data-proof-format)
-/// is supported.
+/// - Only the [JSON Web Token Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#json-web-token-proof-format)
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(try_from = "__DomainLinkageConfiguration")]
pub struct DomainLinkageConfiguration(__DomainLinkageConfiguration);
@@ -34,8 +38,8 @@ struct __DomainLinkageConfiguration {
/// Fixed context.
#[serde(rename = "@context")]
context: Context,
- /// Linked credentials.
- linked_dids: Vec,
+ /// Linked JWT credentials.
+ linked_dids: Vec,
}
impl __DomainLinkageConfiguration {
@@ -68,7 +72,7 @@ impl Display for DomainLinkageConfiguration {
impl DomainLinkageConfiguration {
/// Creates a new DID Configuration Resource.
- pub fn new(linked_dids: Vec) -> Self {
+ pub fn new(linked_dids: Vec) -> Self {
Self(__DomainLinkageConfiguration {
context: Self::well_known_context().clone(),
linked_dids,
@@ -84,17 +88,22 @@ impl DomainLinkageConfiguration {
}
/// List of Domain Linkage Credentials.
- pub fn linked_dids(&self) -> &Vec {
+ pub fn linked_dids(&self) -> &Vec {
&self.0.linked_dids
}
/// List of the issuers of the Domain Linkage Credentials.
- pub fn issuers(&self) -> impl Iterator {
- self.0.linked_dids.iter().map(|linked_did| linked_did.issuer.url())
+ pub fn issuers(&self) -> std::result::Result, ValidationError> {
+ self
+ .0
+ .linked_dids
+ .iter()
+ .map(CredentialValidator::extract_issuer_from_jwt::)
+ .collect()
}
/// List of domain Linkage Credentials.
- pub fn linked_dids_mut(&mut self) -> &mut Vec {
+ pub fn linked_dids_mut(&mut self) -> &mut Vec {
&mut self.0.linked_dids
}
}
@@ -167,20 +176,20 @@ mod tests {
#[test]
fn test_from_json_valid() {
- const JSON1: &str = include_str!("../../tests/fixtures/dn-config-valid.json");
+ const JSON1: &str = include_str!("../../tests/fixtures/domain-config-valid.json");
DomainLinkageConfiguration::from_json(JSON1).unwrap();
}
#[test]
fn test_from_json_invalid_context() {
- const JSON1: &str = include_str!("../../tests/fixtures/dn-config-invalid-context.json");
+ const JSON1: &str = include_str!("../../tests/fixtures/domain-config-invalid-context.json");
let deserialization_result: Result = DomainLinkageConfiguration::from_json(JSON1);
assert!(deserialization_result.is_err());
}
#[test]
fn test_from_json_extra_property() {
- const JSON1: &str = include_str!("../../tests/fixtures/dn-config-extra-property.json");
+ const JSON1: &str = include_str!("../../tests/fixtures/domain-config-extra-property.json");
let deserialization_result: Result = DomainLinkageConfiguration::from_json(JSON1);
assert!(deserialization_result.is_err());
}
diff --git a/identity_credential/src/credential/domain_linkage_credential_builder.rs b/identity_credential/src/credential/domain_linkage_credential_builder.rs
index d202934326..c9c0090646 100644
--- a/identity_credential/src/credential/domain_linkage_credential_builder.rs
+++ b/identity_credential/src/credential/domain_linkage_credential_builder.rs
@@ -11,8 +11,10 @@ use identity_core::common::Object;
use identity_core::common::OneOrMany;
use identity_core::common::Timestamp;
use identity_core::common::Url;
+use identity_did::CoreDID;
+use identity_did::DID;
-/// Convenient builder to create a spec compliant Linked Data Domain Linkage Credential.
+/// Convenient builder to create a spec compliant Domain Linkage Credential.
///
/// See:
///
@@ -32,16 +34,12 @@ impl DomainLinkageCredentialBuilder {
Self::default()
}
- /// Sets the value of the `issuer`, only the URL is used, other properties are ignored.
+ /// Sets the value of the `issuer`.
///
- /// The issuer will also be set as the `credentialSubject`.
+ /// The issuer will also be set as `credentialSubject.id`.
#[must_use]
- pub fn issuer(mut self, value: Issuer) -> Self {
- let issuer: Url = match value {
- Issuer::Url(url) => url,
- Issuer::Obj(data) => data.id,
- };
- self.issuer = Some(issuer);
+ pub fn issuer(mut self, did: CoreDID) -> Self {
+ self.issuer = Some(did.into_url().into());
self
}
@@ -60,6 +58,8 @@ impl DomainLinkageCredentialBuilder {
}
/// Sets the origin in `credentialSubject`.
+ ///
+ /// Must be a domain origin.
#[must_use]
pub fn origin(mut self, value: Url) -> Self {
self.origin = Some(value);
@@ -69,6 +69,12 @@ impl DomainLinkageCredentialBuilder {
/// Returns a new `Credential` based on the `DomainLinkageCredentialBuilder` configuration.
pub fn build(self) -> Result> {
let origin: Url = self.origin.ok_or(Error::MissingOrigin)?;
+ if origin.domain().is_none() {
+ return Err(Error::DomainLinkageError(
+ "origin must be a domain with http(s) scheme".into(),
+ ));
+ }
+
let mut properties: Object = Object::new();
properties.insert("origin".into(), origin.into_string().into());
let issuer: Url = self.issuer.ok_or(Error::MissingIssuer)?;
@@ -103,22 +109,35 @@ impl DomainLinkageCredentialBuilder {
mod tests {
use crate::credential::domain_linkage_credential_builder::DomainLinkageCredentialBuilder;
use crate::credential::Credential;
- use crate::credential::Issuer;
use crate::error::Result;
use crate::Error;
use identity_core::common::Timestamp;
use identity_core::common::Url;
+ use identity_did::CoreDID;
#[test]
fn test_builder_with_all_fields_set_succeeds() {
- let issuer = Issuer::Url(Url::parse("did:example:issuer").unwrap());
- let _credential: Credential = DomainLinkageCredentialBuilder::new()
+ let issuer: CoreDID = "did:example:issuer".parse().unwrap();
+ assert!(DomainLinkageCredentialBuilder::new()
.issuance_date(Timestamp::now_utc())
.expiration_date(Timestamp::now_utc())
.issuer(issuer)
.origin(Url::parse("http://www.example.com").unwrap())
.build()
- .unwrap();
+ .is_ok());
+ }
+
+ #[test]
+ fn test_builder_origin_is_not_a_domain() {
+ let issuer: CoreDID = "did:example:issuer".parse().unwrap();
+ let err: Error = DomainLinkageCredentialBuilder::new()
+ .issuance_date(Timestamp::now_utc())
+ .expiration_date(Timestamp::now_utc())
+ .issuer(issuer)
+ .origin(Url::parse("did:example:origin").unwrap())
+ .build()
+ .unwrap_err();
+ assert!(matches!(err, Error::DomainLinkageError(_)));
}
#[test]
@@ -134,7 +153,7 @@ mod tests {
#[test]
fn test_builder_no_origin() {
- let issuer = Issuer::Url(Url::parse("did:example:issuer").unwrap());
+ let issuer: CoreDID = "did:example:issuer".parse().unwrap();
let credential: Result = DomainLinkageCredentialBuilder::new()
.issuance_date(Timestamp::now_utc())
.expiration_date(Timestamp::now_utc())
@@ -146,7 +165,7 @@ mod tests {
#[test]
fn test_builder_no_expiration_date() {
- let issuer = Issuer::Url(Url::parse("did:example:issuer").unwrap());
+ let issuer: CoreDID = "did:example:issuer".parse().unwrap();
let credential: Result = DomainLinkageCredentialBuilder::new()
.issuance_date(Timestamp::now_utc())
.issuer(issuer)
diff --git a/identity_credential/src/credential/mod.rs b/identity_credential/src/credential/mod.rs
index d0c6f76a0c..242fd7a240 100644
--- a/identity_credential/src/credential/mod.rs
+++ b/identity_credential/src/credential/mod.rs
@@ -7,7 +7,10 @@
mod builder;
mod credential;
+// TODO: Feature-gate Domain Linkage Types behind its own flag and require flag = ["validator"].
+#[cfg(feature = "validator")]
mod domain_linkage_configuration;
+#[cfg(feature = "validator")]
mod domain_linkage_credential_builder;
mod evidence;
mod issuer;
@@ -25,7 +28,9 @@ mod subject;
pub use self::builder::CredentialBuilder;
pub use self::credential::Credential;
+#[cfg(feature = "validator")]
pub use self::domain_linkage_configuration::DomainLinkageConfiguration;
+#[cfg(feature = "validator")]
pub use self::domain_linkage_credential_builder::DomainLinkageCredentialBuilder;
pub use self::evidence::Evidence;
pub use self::issuer::Issuer;
diff --git a/identity_credential/src/validator/domain_linkage_validator.rs b/identity_credential/src/validator/domain_linkage_validator.rs
index 3bd31a85db..5ef759ab2d 100644
--- a/identity_credential/src/validator/domain_linkage_validator.rs
+++ b/identity_credential/src/validator/domain_linkage_validator.rs
@@ -3,24 +3,56 @@
use crate::credential::Credential;
use crate::credential::DomainLinkageConfiguration;
+use crate::credential::Jwt;
use crate::validator::errors::DomainLinkageValidationError;
use crate::validator::errors::DomainLinkageValidationErrorCause;
-use crate::validator::CredentialValidationOptions;
-use crate::validator::CredentialValidator;
+use crate::validator::vc_jwt_validation::CredentialValidationOptions;
+use crate::validator::vc_jwt_validation::CredentialValidator;
use crate::validator::FailFast;
use identity_core::common::OneOrMany;
use identity_core::common::Url;
use identity_did::CoreDID;
-use identity_did::DID;
use identity_document::document::CoreDocument;
-use serde::Serialize;
+use identity_verification::jws::EdDSAJwsVerifier;
+use identity_verification::jws::JwsVerifier;
+
+use super::vc_jwt_validation::DecodedJwtCredential;
type DomainLinkageValidationResult = Result<(), DomainLinkageValidationError>;
/// A validator for a Domain Linkage Configuration and Credentials.
-pub struct DomainLinkageValidator {}
+
+#[derive(Debug, Clone)]
+pub struct DomainLinkageValidator {
+ validator: CredentialValidator,
+}
impl DomainLinkageValidator {
+ /// Creates a new [`DomainLinkageValidator`] capable of verifying a Domain Linkage Credential issued as a JWS
+ /// using the [`EdDSA`](::identity_verification::jose::jws::JwsAlgorithm::EdDSA) algorithm.
+ ///
+ /// See [`DomainLinkageValidator::with_signature_verifier`](DomainLinkageValidator::with_signature_verifier)
+ /// which enables you to supply a custom signature verifier if other JWS algorithms are of interest.
+ pub fn new() -> Self {
+ Self {
+ validator: CredentialValidator::new(),
+ }
+ }
+}
+
+impl DomainLinkageValidator
+where
+ V: JwsVerifier,
+{
+ /// Create a new [`DomainLinkageValidator`] that delegates cryptographic signature verification to the given
+ /// `signature_verifier`. If you are only interested in `EdDSA` signatures (with `Ed25519`) then the default
+ /// constructor can be used. See [`DomainLinkageValidator::new`](DomainLinkageValidator::new).
+ pub fn with_signature_verifier(signature_verifier: V) -> Self {
+ Self {
+ validator: CredentialValidator::with_signature_verifier(signature_verifier),
+ }
+ }
+
/// Validates the linkage between a domain and a DID.
/// [`DomainLinkageConfiguration`] is validated according to [DID Configuration Resource Verification](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource-verification).
///
@@ -31,8 +63,8 @@ impl DomainLinkageValidator {
/// * `validation_options`: Further validation options to be applied on the Domain Linkage Credential.
///
/// # Note:
- /// - Only [Linked Data Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#linked-data-proof-format)
- /// is supported.
+ /// - Only the [JSON Web Token Proof Format](https://identity.foundation/.well-known/resources/did-configuration/#json-web-token-proof-format)
+ /// is supported.
/// - Only the Credential issued by `issuer` is verified.
///
/// # Errors
@@ -40,32 +72,45 @@ impl DomainLinkageValidator {
/// - `configuration` includes multiple credentials issued by `issuer`.
/// - Validation of the matched Domain Linkage Credential fails.
pub fn validate_linkage>(
+ &self,
issuer: &DOC,
configuration: &DomainLinkageConfiguration,
domain: &Url,
validation_options: &CredentialValidationOptions,
) -> DomainLinkageValidationResult {
- let mut matched_credentials = configuration
- .linked_dids()
- .iter()
- .filter(|credential| credential.issuer.url().as_str() == CoreDocument::id(issuer.as_ref()).as_str());
+ let issuers: Vec = configuration.issuers().map_err(|err| DomainLinkageValidationError {
+ cause: DomainLinkageValidationErrorCause::InvalidJwt,
+ source: Some(err.into()),
+ })?;
- match matched_credentials.next() {
- None => Err(DomainLinkageValidationError {
+ // Multiple credentials for the same issuer are an error.
+ if issuers.iter().filter(|iss| *iss == issuer.as_ref().id()).count() > 1 {
+ return Err(DomainLinkageValidationError {
cause: DomainLinkageValidationErrorCause::InvalidStructure,
source: None,
- }),
- Some(credential) => {
- if matched_credentials.next().is_some() {
- Err(DomainLinkageValidationError {
- cause: DomainLinkageValidationErrorCause::InvalidStructure,
- source: None,
- })
- } else {
- Self::validate_credential(issuer, credential, domain, validation_options)
- }
- }
- }
+ });
+ };
+
+ // Find the index of the issuer in the JWT credentials if present.
+ let (jwt_index, _): (usize, _) = issuers
+ .iter()
+ .enumerate()
+ .find(|(_index, iss)| *iss == issuer.as_ref().id())
+ .ok_or_else(|| DomainLinkageValidationError {
+ cause: DomainLinkageValidationErrorCause::InvalidIssuer,
+ source: None,
+ })?;
+
+ // Validate the credential at the corresponding index.
+ let credential: &Jwt = configuration
+ .linked_dids()
+ .get(jwt_index)
+ .ok_or_else(|| DomainLinkageValidationError {
+ cause: DomainLinkageValidationErrorCause::InvalidIssuer,
+ source: None,
+ })?;
+
+ self.validate_credential(issuer, credential, domain, validation_options)
}
/// Validates a [Domain Linkage Credential](https://identity.foundation/.well-known/resources/did-configuration/#domain-linkage-credential).
@@ -73,25 +118,29 @@ impl DomainLinkageValidator {
/// *`issuer`: issuer of the credential.
/// *`credential`: domain linkage Credential to be verified.
/// *`domain`: the domain hosting the credential.
- pub fn validate_credential>(
+ pub fn validate_credential>(
+ &self,
issuer: &DOC,
- credential: &Credential,
+ credential: &Jwt,
domain: &Url,
validation_options: &CredentialValidationOptions,
) -> DomainLinkageValidationResult {
+ let decoded_credential: DecodedJwtCredential = self
+ .validator
+ .validate(credential, issuer, validation_options, FailFast::AllErrors)
+ .map_err(|err| DomainLinkageValidationError {
+ cause: DomainLinkageValidationErrorCause::CredentialValidationError,
+ source: Some(Box::new(err)),
+ })?;
+
+ let credential: &Credential = &decoded_credential.credential;
+
let issuer_did: CoreDID =
CoreDID::parse(credential.issuer.url().as_str()).map_err(|err| DomainLinkageValidationError {
cause: DomainLinkageValidationErrorCause::InvalidIssuer,
source: Some(Box::new(err)),
})?;
- CredentialValidator::validate(credential, issuer, validation_options, FailFast::AllErrors).map_err(|err| {
- DomainLinkageValidationError {
- cause: DomainLinkageValidationErrorCause::CredentialValidationError,
- source: Some(Box::new(err)),
- }
- })?;
-
if credential.id.is_some() {
return Err(DomainLinkageValidationError {
cause: DomainLinkageValidationErrorCause::ImpermissibleIdProperty,
@@ -171,51 +220,69 @@ impl DomainLinkageValidator {
}
}
+impl Default for DomainLinkageValidator {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::credential::Credential;
use crate::credential::DomainLinkageConfiguration;
use crate::credential::DomainLinkageCredentialBuilder;
- use crate::credential::Issuer;
- use crate::credential::Subject;
+ use crate::credential::Jws;
+ use crate::credential::Jwt;
use crate::validator::domain_linkage_validator::DomainLinkageValidationResult;
use crate::validator::domain_linkage_validator::DomainLinkageValidator;
use crate::validator::errors::DomainLinkageValidationErrorCause;
- use crate::validator::test_utils::generate_document_with_keys;
- use crate::validator::CredentialValidationOptions;
+ use crate::validator::test_utils::generate_jwk_document_with_keys;
+ use crate::validator::vc_jwt_validation::CredentialValidationOptions;
+ use crypto::signatures::ed25519::SecretKey;
use identity_core::common::Duration;
use identity_core::common::Object;
use identity_core::common::OneOrMany;
use identity_core::common::OrderedSet;
use identity_core::common::Timestamp;
use identity_core::common::Url;
- use identity_core::crypto::KeyPair;
- use identity_core::crypto::ProofOptions;
- use identity_did::DID;
+ use identity_did::CoreDID;
use identity_document::document::CoreDocument;
+ use identity_verification::jws::CharSet;
+ use identity_verification::jws::CompactJwsEncoder;
+ use identity_verification::jws::CompactJwsEncodingOptions;
+ use identity_verification::jws::JwsAlgorithm;
+ use identity_verification::jws::JwsHeader;
+ use identity_verification::MethodData;
+ use identity_verification::VerificationMethod;
#[test]
pub(crate) fn test_valid_credential() {
- let (mut credential, document, keypair) = create_valid_credential();
- sign_credential(&mut credential, &document, keypair);
- let validation_result: DomainLinkageValidationResult = DomainLinkageValidator::validate_credential(
+ let (document, secret_key, fragment) = generate_jwk_document_with_keys();
+ let credential: Credential = create_domain_linkage_credential(document.id());
+ let jwt: Jwt = sign_credential_jwt(&credential, &document, &fragment, &secret_key);
+
+ let validation_result: DomainLinkageValidationResult = DomainLinkageValidator::new().validate_credential(
&document,
- &credential,
+ &jwt,
&url_foo(),
&CredentialValidationOptions::default(),
);
+
assert!(validation_result.is_ok());
}
#[test]
pub(crate) fn test_invalid_credential_signature() {
- let (mut credential, document, keypair) = create_valid_credential();
- sign_credential(&mut credential, &document, keypair);
- credential.expiration_date = Some(Timestamp::now_utc().checked_add(Duration::days(10)).unwrap());
- let validation_result: DomainLinkageValidationResult = DomainLinkageValidator::validate_credential(
+ let (document, _secret_key, fragment) = generate_jwk_document_with_keys();
+ let credential: Credential = create_domain_linkage_credential(document.id());
+ let other_secret_key: SecretKey = SecretKey::generate().unwrap();
+ // Sign with `other_secret_key` to produce an invalid signature.
+ let jwt: Jwt = sign_credential_jwt(&credential, &document, &fragment, &other_secret_key);
+
+ let validation_result: DomainLinkageValidationResult = DomainLinkageValidator::new().validate_credential(
&document,
- &credential,
+ &jwt,
&url_foo(),
&CredentialValidationOptions::default(),
);
@@ -227,15 +294,18 @@ mod tests {
#[test]
pub(crate) fn test_invalid_id_property() {
- let (mut credential, document, keypair) = create_valid_credential();
+ let (document, secret_key, fragment) = generate_jwk_document_with_keys();
+ let mut credential: Credential = create_domain_linkage_credential(document.id());
credential.id = Some(Url::parse("http://random.credential.id").unwrap());
- sign_credential(&mut credential, &document, keypair);
- let validation_result: DomainLinkageValidationResult = DomainLinkageValidator::validate_credential(
+ let jwt: Jwt = sign_credential_jwt(&credential, &document, &fragment, &secret_key);
+
+ let validation_result: DomainLinkageValidationResult = DomainLinkageValidator::new().validate_credential(
&document,
- &credential,
+ &jwt,
&url_foo(),
&CredentialValidationOptions::default(),
);
+
assert!(matches!(
validation_result.unwrap_err().cause,
DomainLinkageValidationErrorCause::ImpermissibleIdProperty
@@ -244,15 +314,18 @@ mod tests {
#[test]
pub(crate) fn test_domain_linkage_type_missing() {
- let (mut credential, document, keypair) = create_valid_credential();
+ let (document, secret_key, fragment) = generate_jwk_document_with_keys();
+ let mut credential: Credential = create_domain_linkage_credential(document.id());
credential.types = OneOrMany::One(Credential::