Skip to main content

Update DID Documents

DID Documents can be extended by adding Verification Methods, Services and custom properties. A verification method adds public keys, which can be used to digitally sign things like a DID message or a verifiable credential, while a service can provide metadata around the identity via URIs.

Verification Methods

As demonstrated by the example below, the Iota identity framework offers easy-to-use methods for adding verification methods.

The following properties can be specified for a verification method:

  • id: a DID URL for the verification method. It can be specified by setting the fragment;
  • type: specifies the type of the Verification Method. The framework supports Ed25519 and X25519 key types. This property is automatically filled by the framework when specifying the verification material.
  • publicKeyMultibase: multibase encoded public key which concludes the verification material. This can be automatically generated by the framework or manually provided by users.

Verification Relationships

Verification relationships express the relationship between the DID subject and the verification method. It can be used to specify the the purpose of the verification method.

The following relationships are supported by the Identity Framework:

Verification methods can be either embedded or referenced. Referencing verification methods allow them to be used by more than one verification relationship. Upon creating a verification method using the identity framework, specifying the MethodScope option will result in an embedded verification method. Leaving that option unset will create the verification method as a map entry of the verificationMethod property. Verification relationships can be added afterwards using references.

warning

Any update to the DID document must be signed using a verification method with capability invocation relationship to be valid. Removing all capability invocation verification methods disallows any further updates to the document.

Services

Services allow adding other ways of communicating with the DID subject. An endpoint included in the DID Document can offer a way of reaching services for different purposes like authentication, communicating, and discovery.

The following properties can be specified for a service:

  • id: a DID URL for referecing the service in the DID document. It can be specified by setting the fragment.
  • type: a string used to maximize interoperability between services. The framework does not perform any checks on the content of this string.
  • serviceEndpoint: a URL that points to the service endpoint.

Example

The following example demonstrates adding verification methods and services to a DID Document.

Creating Identity

The Example above starts by creating an identity using the account.

let mut account: Account = Account::builder()
.storage(stronghold)
.create_identity(IdentitySetup::default())
.await?;

This will create a DID document and publish it to the tangle.

{
"doc":{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"capabilityInvocation":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X"
}
]
},
"meta":{
"created":"2022-04-13T09:27:48Z",
"updated":"2022-04-13T09:27:48Z"
}
}

The created document only contains one verification method with capabilityInvocation relationship. This method is used to sign the DID Document for publication to the Tangle. The signature proves that the publisher of the document is in control over the capability invocation keys and is allowed to create, update or delete the DID Document.

Any future updates to the DID Document in this example will be signed using this verification method. The Account will automatically sign each update with this method so individual updates don't have to be explicitly signed.

Furthermore, it's possible to rotate a capability Invocation key. In this case, the Account will sign next update with a key which was valid in the previous state of the DID Document. Afterwards it will use the first (oldest) of the remaining capability invocation keys as a default signing method. Other capability invocation keys can still be explicitly specified to sign an update. These can be set in PublishOptions.

Note that the Account does not allow removing all capability invocation keys.

Adding Verification Methods

Another verification method can be added to the DID document using the Account:

account
.update_identity()
.create_method()
.content(methodcontent::generateed25519)
.fragment("my-next-key")
.apply()
.await?;

The code above creates a new verification method that includes a newly generated Ed25519 public key, signs the updated document using the private key of the default capabilityInvocation verification method and publishes the document to the tangle.

Since the MethodScope is not specified, the verification method will be created in the verificationMethod map. The updated DID Document will look as follows:

{
"doc":{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"verificationMethod":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z2Zthec5siTfxCjPwZUHGDGybKNy9oc3ZYeftvEE2nEL3"
}
],
"capabilityInvocation":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X"
}
]
},
"meta":{
"created":"2022-04-13T09:27:48Z",
"updated":"2022-04-13T09:28:06Z"
}
}

Adding Verification Relationships

Verification relationship can be attached to a verification method by referencing its fragment.

account
.update_identity()
.attach_method_relationship()
.fragment("my-next-key")
.relationships(vec![
MethodRelationship::CapabilityDelegation,
MethodRelationship::CapabilityInvocation,
])
.apply()
.await?;

This will add CapabilityDelegation and CapabilityInvocation relationships to the created verification method with the fragment my-next-key. The capabilityInvocation property now has both an embedded and a referenced verification method.

{
"doc":{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"verificationMethod":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z2Zthec5siTfxCjPwZUHGDGybKNy9oc3ZYeftvEE2nEL3"
}
],
"capabilityDelegation":[
"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key"
],
"capabilityInvocation":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X"
},
"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key"
]
},
"meta":{
"created":"2022-04-13T09:27:48Z",
"updated":"2022-04-13T09:28:23Z"
}
}

Adding a Service

Similar to verification methods, services can be added to a DID Document.

account
.update_identity()
.create_service()
.fragment("my-service-1")
.type_("MyCustomService")
.endpoint(Url::parse("https://example.com")?)
.apply()
.await?;

In JavaScript, the endpoint property type is a string, this must be a valid URL, otherwise an error will be thrown. Additionally, custom properties can be added to a service by setting properties in both Rust and JavaScript.

The updated Document with the newly created service looks as follows.

{
"doc":{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"verificationMethod":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z2Zthec5siTfxCjPwZUHGDGybKNy9oc3ZYeftvEE2nEL3"
}
],
"capabilityDelegation":[
"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key"
],
"capabilityInvocation":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X"
},
"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-next-key"
],
"service":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-service-1",
"type":"MyCustomService",
"serviceEndpoint":"https://example.com/"
}
]
},
"meta":{
"created":"2022-04-13T09:27:48Z",
"updated":"2022-04-13T09:28:34Z"
}
}

Removing a Verification Method

Verification methods and/or their relationships can be removed from the DID Document. The following code removes the verification method that we created previously.

account
.update_identity()
.delete_method()
.fragment("my-next-key")
.apply()
.await?;
{
"doc":{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"capabilityInvocation":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#sign-0",
"controller":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf",
"type":"Ed25519VerificationKey2018",
"publicKeyMultibase":"z5k7vzMVuXXj8MJDcfzP2owvc8xKBA6BBsAkFf1GSNu2X"
}
],
"service":[
{
"id":"did:iota:6T4PRHWp7bNsKaBWr1gVtUBQfLaxAKqKGAeJWFBZkMyf#my-service-1",
"type":"MyCustomService",
"serviceEndpoint":"https://example.com/"
}
]
},
"meta":{
"created":"2022-04-13T09:27:48Z",
"updated":"2022-04-13T09:29:03Z"
}
}

Notice that the capabilityDelegation and verificationMethod properties are also removed from the DID Document since they became empty after the only verification method they contained and referenced was removed.

Furthermore and similar to deleting verification methods, services can be deleted using account.update_identity().delete_service()... in Rust and account.deleteService(..) in JavaScript.

tip

In this example, a message is published to the tangle every time the document is updated. These messages can be unnecessary. Instead, one message can be published that contains all the updates to the DID Document. See the lazy example for Rust and lazy example for JS to learn more about lazy publishing.