Skip to main content

Linked Verifiable Presentations

Introduction​

The Decentralized Identity Foundation (DIF)'s Linked Verifiable Presentations (Linked VP) specification, allows users to make verifiable credentials (VC) publicly discoverable through their DID document.

Specification Overview​

Linked VPs rely on DID services of type LinkedVerifiablePresentation that store either one or a set of Unique Resource Locators (URL) to a Verifiable Presentation (VP).

Here is a sample DID Document employing this mechanism:

{
"@context": ["https://www.w3.org/ns/did/v1", "https://identity.foundation/linked-vp/contexts/v1"],
"id": "did:example:123",
"verificationMethod": [
{
"id": "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"type": "JsonWebKey2020",
"controller": "did:example:123",
"publicKeyJwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ"
}
}
],
"service": [
{
"id": "did:example:123#foo",
"type": "LinkedVerifiablePresentation",
"serviceEndpoint": ["https://bar.example.com/verifiable-presentation.jsonld"]
},
{
"id": "did:example:123#baz",
"type": "LinkedVerifiablePresentation",
"serviceEndpoint": "ipfs://bafybeihkoviema7g3gxyt6la7vd5ho32ictqbilu3wnlo3rs7ewhnp7lly/verifiable-presentation.jwt"
}
]
}

By querying the DID document's services of type LinkedVerifiablePresentation, one can easily discover any VP the DID controller decided to publicly share. In the example above those are: https://bar.example.com/verifiable-presentation.jsonld and ipfs://bafybeihkoviema7g3gxyt6la7vd5ho32ictqbilu3wnlo3rs7ewhnp7lly/verifiable-presentation.jwt.

Example: Linking a VP hosted on an IOTA Network within an IOTA Notarization​

In this example we are going to showcase how to host a VP on the IOTA Ledger leveraging IOTA Notarization and linking it from the holder's DID document through an URL-like IOTA Resource Locator (IRL).

caution

The concepts of creating VCs and VPs won't be covered in this page, please refer to their corresponding pages.

Having created a JWT-encoded VP containing a set of JWT-encoded VCs, an IOTA Notarization to store it on the ledger can be created like so:

let notarized_vp = notarization_client
.create_locked_notarization()
.with_state(State::from_string(jwt_vp.into(), None))
.with_immutable_description("My VP".to_owned())
.finish()?
.build_and_execute(&notarization_client)
.await?
.output;

Now that the VP is hosted on-chain we can link it to its holder's DID document:

let notarized_vp_url: Url = notarized_vp
.iota_resource_locator_builder(notarization_client.network()) // This requires notarization's feature "irl".
.data()
.into(); // This requires identity's feature "irl".
let service_url: DIDUrl = did_document.id().clone().join("#linked-vp")?;
let linked_verifiable_presentation_service = LinkedVerifiablePresentationService::new(service_url, [notarized_vp_url], Object::new())?;
did_document.insert_service(linked_verifiable_presentation_service.into())?;

let updated_did_document = identity_client
.publish_did_document_update(did_document, TEST_GAS_BUDGET)
.await?;

If we inspect the services of updated_did_document we would see something similar to this JSON object:

{
"id": "did:iota:fb230e5b:0x1222d84b5598234387bde3baa43ed8d3dbd24e23cfc8db9708412c748962a63f#linked-vp",
"type": "LinkedVerifiablePresentation",
"serviceEndpoint": "iota:fb230e5b/0x215e1425343e20fc69c8bacd5e783d3b3c81e3c86408b9b8357247860d0a376d/state/data"
}

The value of the serviceEndpoint property is an IRL that references the state.data property of the Move object having ID 0x215e1425343e20fc69c8bacd5e783d3b3c81e3c86408b9b8357247860d0a376d (which in this instance is the ID of the Notarization that was previously created) and located on a custom IOTA network.

To fetch or dereference an IRL, the Resolver implemented in the iota-caip library can be used:

let custom_network = IotaNetwork::custom(identity_client.network().as_ref()).expect("valid IOTA network);
let resolver = Resolver::new_with_custom_networks([(custom_network, custom_network_endpoint)]);
let jwt_vp = serde_json::from_value(resolver.resolve(&notarized_vp.to_iota_resource_locator()).await?)?;

Full Example​

examples/1_advanced/11_linked_verifiable_presentation.rs
loading...