Skip to main content
Version: 1.4

Update DID Documents

You can extend DID Documents 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.

Properties​

You can specify the following properties for a verification method:

  • id: A DID URL for the verification method. You can specify it 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: A multi-base 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. You can use it to specify the purpose of the verification method.

Relationships​

The Identity Framework supports the following relationships:

Verification methods can be either embedded or referenced. Referencing verification methods allows them to be used by more than one verification relationship. When you create 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. You can also add verification relationships afterward using references.

note

Updates to the DID Document are done through a state transition of the Alias Output by its state controller. The public key or address of the state controller does not need to be a verification method in the DID Document, since it is defined in the containing Alias Output.

Services​

Services allow you to add 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.

Properties​

You can specify the following properties for a service:

  • id: A DID URL for referencing the service in the DID document. You can specify it 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.

Create Identity​

Before you can update anything, you will need to create an Identity.

  // Create a new client to interact with the IOTA ledger.
let client: Client = Client::builder()
.with_primary_node(API_ENDPOINT, None)?
.finish()
.await?;

// Create a new secret manager backed by a Stronghold.
let mut secret_manager: SecretManager = SecretManager::Stronghold(
StrongholdSecretManager::builder()
.password(Password::from("secure_password".to_owned()))
.build(random_stronghold_path())?,
);

// Create a new DID in an Alias Output for us to modify.
let storage: MemStorage = MemStorage::new(JwkMemStore::new(), KeyIdMemstore::new());
let (_, document, fragment_1): (Address, IotaDocument, String) =
create_did(&client, &mut secret_manager, &storage).await?;
let did: IotaDID = document.id().clone();

This creates and publishes an Alias Output containing a DID Document with one verification method.

{
"doc": {
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"verificationMethod": [
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"crv": "Ed25519",
"x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY"
}
}
]
},
"meta": {
"created": "2023-11-16T20:40:03Z",
"updated": "2023-11-16T20:40:03Z",
"governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd",
"stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd"
}
}

Add a Verification Method​

  // Insert a new Ed25519 verification method in the DID document.
let fragment_2: String = document
.generate_method(
&storage,
JwkMemStore::ED25519_KEY_TYPE,
JwsAlgorithm::EdDSA,
None,
MethodScope::VerificationMethod,
)
.await?;

This creates a new verification method that includes a newly generated Ed25519 public key.

{
"doc": {
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"verificationMethod": [
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"crv": "Ed25519",
"x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY"
}
},
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"crv": "Ed25519",
"x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo"
}
}
]
},
"meta": {
"created": "2023-11-16T20:40:03Z",
"updated": "2023-11-16T20:40:03Z",
"governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd",
"stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd"
}
}

Notice that these changes to the document are not published yet.

Add Verification Relationships​

You can attach verification relationships to a verification method by referencing its fragment.

// Attach a new method relationship to the inserted method.
document.attach_method_relationship(
&document.id().to_url().join(format!("#{fragment_2}"))?,
MethodRelationship::Authentication,
)?;

This will add Authentication relationship to the verification method with the fragment key-1. Note that Authentication references the already included key-2 verification method:

{
"doc": {
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"verificationMethod": [
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"crv": "Ed25519",
"x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY"
}
},
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"crv": "Ed25519",
"x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo"
}
}
],
"authentication": [
"did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE"
]
},
"meta": {
"created": "2023-11-16T20:40:03Z",
"updated": "2023-11-16T20:40:03Z",
"governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd",
"stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd"
}
}

Add a Service​

You can also add custom properties can to a service by setting properties:

  // Add a new Service.
let service: Service = Service::from_json_value(json!({
"id": document.id().to_url().join("#linked-domain")?,
"type": "LinkedDomains",
"serviceEndpoint": "https://iota.org/"
}))?;
assert!(document.insert_service(service).is_ok());
document.metadata.updated = Some(Timestamp::now_utc());

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

{
"doc": {
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"verificationMethod": [
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE",
"crv": "Ed25519",
"x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY"
}
},
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"crv": "Ed25519",
"x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo"
}
}
],
"authentication": [
"did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE"
],
"service": [
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#linked-domain",
"type": "LinkedDomains",
"serviceEndpoint": "https://iota.org/"
}
]
},
"meta": {
"created": "2023-11-16T20:40:03Z",
"updated": "2023-11-16T20:40:08Z",
"governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd",
"stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd"
}
}

Remove a Verification Method​

You can also remove verification methods at any time using the following snippet:

// Remove a verification method.
let original_method: DIDUrl = document.resolve_method(fragment_1.as_str(), None).unwrap().id().clone();
document.purge_method(&storage, &original_method).await.unwrap();

This removes the original verification method with the fragment key-1.

{
"doc": {
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"verificationMethod": [
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a",
"type": "JsonWebKey",
"publicKeyJwk": {
"kty": "OKP",
"alg": "EdDSA",
"kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE",
"crv": "Ed25519",
"x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo"
}
}
],
"authentication": [
"did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE"
],
"service": [
{
"id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#linked-domain",
"type": "LinkedDomains",
"serviceEndpoint": "https://iota.org/"
}
]
},
"meta": {
"created": "2023-11-16T20:40:03Z",
"updated": "2023-11-16T20:40:08Z",
"governorAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd",
"stateControllerAddress": "tst1qrjsnlg6nqd2kdzx4q880nl74jtrcajm7ae57zazl0l7ye09ahh4x6z9gtd"
}
}

Publish Your Updates​

Publish the updated DID Document inside the Alias Output taking into account the increase in the storage deposit needed.

  // Resolve the latest output and update it with the given document.
let alias_output: AliasOutput = client.update_did_output(document.clone()).await?;

// Because the size of the DID document increased, we have to increase the allocated storage deposit.
// This increases the deposit amount to the new minimum.
let rent_structure: RentStructure = client.get_rent_structure().await?;
let alias_output: AliasOutput = AliasOutputBuilder::from(&alias_output)
.with_minimum_storage_deposit(rent_structure)
.finish()?;

// Publish the updated Alias Output.
let updated: IotaDocument = client.publish_did_output(&secret_manager, alias_output).await?;

Full Example Code​

examples/0_basic/1_update_did.rs
loading...