Skip to content

Commit

Permalink
Merge pull request #2894 from dfinity/vc-docs
Browse files Browse the repository at this point in the history
Draft: Verifiable Credentials docs
  • Loading branch information
jessiemongeon1 committed Jun 5, 2024
2 parents 49661f2 + a0f5ec7 commit 6dfdbc3
Show file tree
Hide file tree
Showing 9 changed files with 664 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
keywords: [intermediate, concept, identity, credentials, verifiable credentials]
---

import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow";
import useBaseUrl from "@docusaurus/useBaseUrl";

# How it works

<MarkdownChipRow labels={["Intermediate", "Concept"]} />

## Overview

Verifiable credentials (VCs) on ICP rely on Internet Identity (II), which provides distinct identities that preserve unlinkability for each dapp. A dapp utilizing verifiable credentials can’t ask for a user's credentials from the [issuer](/docs/current/developer-docs/identity/verifiable-credentials/overview#terminology) of the verifiable credential directly. When a [relying party](/docs/current/developer-docs/identity/verifiable-credentials/overview#terminology) wants to obtain a credential from an issuer, it must request it through an [identity provider](/docs/current/developer-docs/identity/verifiable-credentials/overview#terminology) such as Internet Identity.

Internet Identity acts as a communication link between the relying party and issuer API, connecting with the issuer API and asking for the credentials for the user. After receiving the credentials, they are forwarded to the relying party.

<div class="text--center">
<img
src={useBaseUrl("/img/docs/vcs-1.png")}
alt="Verifiable Credential Workflow"
width="800"
/>
</div>

## User's perspective

From the user's perspective, the relying party opens a new window to allow the credential, which is closed when the flow finishes.

<div class="text--center">
<img
src={useBaseUrl("/img/docs/vcs-2.png")}
alt="Verifiable Credential Workflow"
width="800"
/>
</div>

## Developer's perspective

From the developer’s perspective, the interaction between dapps uses window messages and calls to canisters:

1. The user accesses the relying party and starts the process to request credentials. This may be an action such as logging into a dapp or accessing a private service that requires authorization.

2. The relying party starts the flow with Internet Identity (II):

a. The relying party opens a new II window and waits for a window message.

b. The frontend of II sends a window message to start the request process.

c. The relying party replies with the credentials request and issuer data for the request.

3. The II frontend calls the issuer canister to retrieve the consent message.

4. The II frontend shows all the details to the user: the relying party, the consent message, and the issuer URL, then waits for the user action.

5. The user decides whether to allow or revoke the action.

a. If allowed, the II frontend requests the ID alias for the II backend.

6. The II backend calculates an ID alias which is an opaque identifier to keep unlinkability between the issuer and the relying party.

7. The II frontend requests the credential from the issuer.

8. The issuer receives the request for the credential, validates the call from II, and replies with a verifiable presentation if the user fulfills the credential's requirements.

9. The II frontend receives the credential from the issuer and forwards it to the relying party, closing the window.

10. The relying party verifies the credentials.

<div class="text--center">
<img
src={useBaseUrl("/img/docs/vcs-3.png")}
alt="Verifiable Credential Workflow"
width="800"
/>
</div>

For more details, check out the [specification in Internet Identity’s repository](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md).

## Next steps

- [Learn how to become an issuer](issuer.mdx).

- [Learn how to become a relying party](relying-party.mdx).
168 changes: 168 additions & 0 deletions docs/developer-docs/identity/verifiable-credentials/issuer.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
---
keywords: [intermediate, tutorial, identity, credentials, verifiable credentials, issuer]
---

import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow";
import useBaseUrl from "@docusaurus/useBaseUrl";

# Become an issuer

<MarkdownChipRow labels={["Intermediate", "Tutorial"]} />

## Overview

An **issuer** is a service or app that can issue a verifiable credential (VC) to a user. On ICP, an issuer is an exposed API that recieves calls from an identity provider and does not trigger any workflows itself. To become an issuer, your canister must implement the issuer API as described in the [verifiable credential spec](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md).

Issuers receive credentials requests from relying partys through an identity provider such as Internet Identity (II). The end-user must allow or revoke the communication before the credential is issued.

Internet Identity is used as the identity provider in this tutorial.

## Development of the issuer API

The issuer API consists of four endpoints:

1. [`vc_consent_message`](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md#1-consent-message): Responds with a message of consent to be displayed to the user to receive their approval of issuing the credentials.

2. [`derivation_origin`](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md#2-derivation-origin): Returns the URL used to derive the user's principal for the issuer.

3. [`prepare_credential`](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md#3-prepare-credential): Checks the validity of the request and prepares the actual credential requested.

4. [`get_credential`](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md#4-get-credential): Issues the actual credential requested by the user.

### Step 1: Obtain a consent message (`vc_consent_message`)

First, the identity provider sends a request to the issuer API endpoint `vc_consent_message` to get the consent message to show the user. A consent message is text shown to the user to confirm that they allow the issuing of a VC.

An example implementation in Rust can be found below:

```rust
pub fn get_vc_consent_message_en(
credential_spec: &CredentialSpec,
) -> Result<Icrc21ConsentInfo, Icrc21Error> {
match verify_credential_spec(credential_spec) {
Ok(SupportedCredentialType::EarlyAdopter(since_year)) => Ok(Icrc21ConsentInfo {
consent_message: format!("You became an early adopter in {}.", since_year),
language: "en".to_string(),
}),
Ok(SupportedCredentialType::EventAttendance(event_name)) => Ok(Icrc21ConsentInfo {
consent_message: format!("You have attended the event {}.", event_name),
language: "en".to_string(),
}),
Err(err) => Err(Icrc21Error::ConsentMessageUnavailable(Icrc21ErrorInfo {
description: format!("Credential spec not supported: {:?}", err),
})),
}
}
```

### Step 2: Implement a derivation origin (`derivation_origin`)

Next, the issuer API endpoint `derivation_origin` is called by the identity provider to obtain a URL to be used as the derivation origin for user's principal.

If the issuer doesn't rely on alternative derivation origins, then it must return the canister's URL: `https://<issuer-canister-id>.icp0.io`.

If the issuer relies on the [alternative derivation origins](/docs/current/references/ii-spec#alternative-frontend-origins) feature, the endpoint must return the same value for the `derivationOrigin` when logging in.

The returned value is subject to verification via .well-known/ii-alternative-origins, as describe in the [alternative origins documentation](/docs/current/references/ii-spec#alternative-frontend-origins).


An example implementation in Rust can be found below:

```rust
use ic_cdk::update;
use vc_util::issuer_api::{DerivationOriginData, DerivationOriginError, DerivationOriginRequest};

#[update(name = "derivation_origin")]
async fn vc_derivation_origin(
_req: DerivationOriginRequest,
) -> Result<DerivationOriginData, DerivationOriginError> {
let dfx_network = option_env!("DFX_NETWORK").unwrap();
let origin = match dfx_network {
"local" => format!("http://{}.localhost:4943", ic_cdk::api::id()),
"ic" => format!("https://{}.icp0.io", ic_cdk::api::id()),
_ => {
return Err(DerivationOriginError::Internal(
"Invalid DFX_NETWORK".to_string(),
))
}
};
Ok(DerivationOriginData { origin })
}
```

:::info
The returned derivation origin is subject to verification via `.well-known/ii-alternative-origins`, as described in the [feature description](https://internetcomputer.org/docs/current/references/ii-spec#alternative-frontend-origins).
:::

### Step 3: Prepare the credentials (`prepare_credential`)

Before a credential can be issued, the request for the credential must be checked for validity using the `prepare_credential` API endpoint. The objective of this endpoint is to check the validity of the request and update the `certified_data` with a new root hash that includes the signature on the credential. Upon success, the actual credential is prepared.

For more details on how to validate the request or create the actual credential, check out the [VC specification](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md).

An example implementation can be found in the [verifiable credentials playground example](https://github.com/dfinity/vc-playground/blob/b2f8cd5f5397b7b355b6503df5af3789834b79e9/issuer/src/main.rs#L557).

:::info
The value of `prepared_context` is used to transfer information between the `prepare_credential` and `get_credential` steps. It is up to the issuer API to decide on the content of this field since the issuer API creates `prepared_context` and is the only entity that consumes it.

For example, when using [canister signatures](https://internetcomputer.org/docs/current/references/ic-interface-spec#canister-signatures)
`prepared_context` contains a time stamped and unsigned VC, for which the canister signature will be available through the `get_credential` call.
:::

### Step 4: Issue the credential (`get_credential`)

Lastly, the issuer API endpoint `get_credential` is used to issue the credential requested by the user. The same checks used to prepare the credential are ran again, with an additional verify step to confirm that `prepared_context` is consistent with `prepare_credential`. If successful, the issuer API returns the signed credential in JWT-format.

An example implementation can be found in the [verifiable credentials playground example](https://github.com/dfinity/vc-playground/blob/b2f8cd5f5397b7b355b6503df5af3789834b79e9/issuer/src/main.rs#L557).

## Recommended conventions

An issuer may follow the convention below for an easier verification of the returned credentials.

Given a credential specification such as:

```
"credentialSpec": {
"credentialType": "SomeVerifiedProperty",
"arguments": {
"argument_1": "value_1",
"another_argument": 42,
}
```

The returned JWT should contain a property named by the value of `credentialType` in `credentialSubject` from the specification, with key-value entries listing the arguments from the specification, namely:

```
"SomeVerifiedProperty": {
"argument_1": "value_1",
"another_argument": 42,
}
```

For example, for a `VerifiedAdult` credential, you would use the following credential specification:

```
"credentialSpec": {
"credentialType": "VerifiedAdult",
"arguments": {
"minAge": 18,
}
```

A compliant issuer would issue a VC that contains `credentialSubject` with the property:

```
"VerifiedAdult": {
"minAge": 18,
}
```

## Resources

- [Verifiable credentials specification](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md).

- [Relying party and issuer demo](https://github.com/dfinity/vc-playground/blob/main/issuer/src/main.rs).

- [Verifiable credentials playground: Issuer](https://metaissuer.vc/).

- [Verifiable credentials playground: Relying party](https://relyingparty.vc/).
51 changes: 51 additions & 0 deletions docs/developer-docs/identity/verifiable-credentials/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
keywords: [intermediate, concept, identity, credentials, verifiable credentials]
---

import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow";

# Overview

<MarkdownChipRow labels={["Intermediate", "Concept"]} />

A credential is a document or certificate that validates an individual's identity or qualifications. Credentials are a part of daily life, with common examples including:

- A driver's license that qualifies an individual to operate a motor vehicle.
- A university degree that can be used to assert a level of education.
- A government-issued passport enabling travel between countries.

These are examples of physical credentials, as they have a physical component that must be presented for validation. Credentials can also be digital, relying on authentication using cryptographic signatures such as canister signatures on ICP.

Verifiable credentials (VCs) are a type of digital credential that enables individuals, organizations, or things (such as devices) to authenticate cryptographically.

The technical implementation of VCs on ICP can be found in the [specification](https://github.com/dfinity/internet-identity/blob/main/docs/vc-spec.md). It is recommended you first read the rest of this documentation, as it includes technical details.

## Benefits

Verifiable credentials are designed to be tamper-evident, privacy-preserving, and interoperable across different systems and contexts, such as from one ICP dapp to another, or from an ICP dapp to a web2 application. By using verifiable credentials, you will have a mechanism to express these sorts of credentials on ICP in a way that is cryptographically secure, privacy-preserving, and machine-verifiable.

## Terminology

When learning about and using VCs, there are four primary terms to familiarize yourself with:

- **User**: Users earn credentials, a piece of data that certifies information about the user, from an issuer.

- **Issuer**: An issuer is an entity (a dapp, organization, government, etc.) that verifies information about the user.

- **Relying party**: A different dapp (organization, government, etc.) which requests certified credentials for that user from the issuer. Since the relying party must verify the received credentials, it is also commonly referred to as the "verifier".

- **Identity provider**: An entity that (in this case, Internet Identity) creates a temporary identifier for the user when the issuer passes the credential to the relying party, so that the relying party and the issuer never learn the user's identifier on the other entity's service.

## How to use verifiable credentials

To use verifiable credentials there are two workflows. First, you can [become an issuer](issuer.mdx) of verifiable credentials. By issuing verifiable credentials, you can empower users to control and share their credentials selectively, enhancing privacy and giving users greater autonomy over their digital identity.

The second workflow allows you to [become a relying party](relying-party.mdx) for verifiable credentials. By requiring verifiable credentials from your application's users, you can ensure that the claims users make are verified by parties (issuers) that you trust, while preserving the user’s privacy.

## Next steps

- [Learn how verifiable credentials work](how-it-works.mdx).

- [Learn how to become an issuer](issuer.mdx).

- [Learn how to become a relying party](relying-party.mdx).
Loading

0 comments on commit 6dfdbc3

Please sign in to comment.