§ Wallet And Credential Interactions v0.1.0

WACI [wak-ee]

Specification Status: Draft

Editors:
Afshan Aman (Bloom)
Eddie Hedges (Bloom)
Contributors:
Jace Hensley (Bloom)
Participate:
GitHub repo
File a bug
Commit history

§ Abstract

There are interactions between a wallet and relying party that require passing information between the two. WACI provides a standard for these interactions.

§ Status of This Document

WACI is a draft specification under development by Bloom and Affinidi.

§ Interactions

All interactions use the same common blocks:

Two ways of initiating an interaction is for the relying party to display either a QR code or a link to the user. There could be other ways to initiate an interaction but this document will be discussing QR codes and links.

If the user is using an app/webiste on something other than the device that their wallet is on, then they would be able to scan a QR code with the wallet. But if the user is using the device that also has their wallet then they wouldn’t be able to scan a QR code, they would need to be able to click a link that will open their mobile wallet.

There are of course other use cases where you might need one over the other or both. For example, in an email you may want to display both a link and a QR code because you won’t be able to dynamically choose between the two.

§ Payload

Some mediums do not allow for a large amount of data to be sent (e.g a QR code) to support those cases the initial payload contains instructions for fetching the challenge token.

This is payload can be displayed in a QR code or added to a link as a query parameter.

QR Code

Click Me!

§ Token URL Response

The result from GETing the provided challengeTokenUrl. This contains the initial JWT that really starts the interaction.

EXAMPLE
{
  "challengeToken": "{{JWT String}}"
}

§ Challenge Token

EXAMPLE
// Header
{
  "alg": "...",
  "kid": "did:example:ebfeb1f712ebc6f1c276e12ec21#primary"
}
EXAMPLE
// Payload
{
  "jti": "...",
  "iss": "did:example:ebfeb1f712ebc6f1c276e12ec21",
  "aud": "...",
  "callbackUrl": "https://example.com/api/callback-url",
  "purpose": "...",
  "version": "..."
}

§ Callback URL

§ Request

Each interaction will POST data to the callbackUrl:

EXAMPLE
{
  "responseToken": "{{Signed JWT}}",
  "from": "qr" | "link"
}
§ Response Token

The response token is signed by the user and acts as a way to prove ownership of their DID and to pass aditional data back to the relying party.

EXAMPLE
{
  "alg": "...",
  "kid": "did:example:c276e12ec21ebfeb1f712ebc6f1#primary"
}
EXAMPLE
{
  "iss": "did:example:c276e12ec21ebfeb1f712ebc6f1",
  "aud": "did:example:ebfeb1f712ebc6f1c276e12ec21",
  "challenge": "{{CHALLENGE TOKEN}}"
}

§ Reponse

The POST to the provided callbackUrl can return with a simple successful HTTP response or it can return a success with follow up details. A redirectLink that the app will open in a browser or challengeToken that will start a new interaction.

EXAMPLE
{
  "redirectUrl": "https://example.com/redirect-url?id={{Some id that identifies the user}}"
}

This could be used to show a success message or bring them back to the website/app to continue where they left off. Most of the time redirectUrl will only be used when the user is already using their phone (see above).

OR

EXAMPLE
{
  "challengeToken": "{{JWT String}}"
}

This could be used to follow up a request interaction with an offer interaction, or even a chain of request interactions that are based on the previously shared VCs.

§ Token Storage

Because the challenge token is always sent back to the relying party, the token doesn’t need to be stored on creation. And this allows the relying party to not have to worry about someone spamming their API and driving up their storage costs.

But no storage at all can lead to replay attacks. One suggested way to mitigate replay attacks while keeping storage to a minimum is to only store the hash of “used” tokens and have a cron job that cleans this storage based on expiration date of the tokens.

§ Swimlane

Each interaction will be slightly different but will follow this general pattern:

sequenceDiagram title: Interaction (QR) activate Wallet Wallet ->>+ Relying Party's Interface: Scan QR Code Relying Party's Interface -->>- Wallet: Retrieve `challengeTokenUrl` Wallet ->>+ Relying Party: GET `challengeTokenUrl` Relying Party -->> Wallet: Return `challengeToken` Wallet ->> Wallet: Verify/decode `challengeToken` Wallet ->> Wallet: Create/sign a `responseToken`, with `challengeToken` as the `challenge` Wallet ->> Relying Party: POST the `responseToken` to `challengeToken`'s `callBackUrl` Relying Party ->> Relying Party: Verify the `responseToken` Relying Party ->> Relying Party: Verify the `responseToken`'s challenge token (valid JWT, signed by Relying Party, and not used before) Relying Party -->>- Wallet: Return success opt `redirectUrl` or `challengeToken` is provided alt `redirectUrl` is provided Wallet ->> Browser: Open `redirectUrl` else `challengeToken` is provided Wallet ->> Wallet: Start new interaction end end deactivate Wallet
sequenceDiagram title: Interaction (Link) User ->>+ Relying Party's Interface: Click link Relying Party's Interface ->>- Wallet: Open Wallet with deep link activate Wallet Wallet ->> Wallet: Parse deep link Wallet ->>+ Relying Party: GET `challengeTokenUrl` Relying Party -->> Wallet: Return `challengeToken` Wallet ->> Wallet: Verify/decode `challengeToken` Wallet ->> Wallet: Create/sign a `responseToken`, with `challengeToken` as the `challenge` Wallet ->> Relying Party: POST the `responseToken` to `challengeToken`'s `callBackUrl` Relying Party ->> Relying Party: Verify the `responseToken` Relying Party ->> Relying Party: Verify the `responseToken`'s challenge token (valid JWT, signed by Relying Party, and not used before) Relying Party -->>- Wallet: Return success opt `redirectUrl` or `challengeToken` is provided alt `redirectUrl` is provided Wallet ->> Browser: Open `redirectUrl` else `challengeToken` is provided Wallet ->> Wallet: Start new interaction end end deactivate Wallet

§ Offer/Claim

The offer/claim interaction is for the use case where an issuer wants to give credential(s) to the user.

§ Challenge Token

An example of an offer challenge token has the following properties (in addition to the base properties):

EXAMPLE
// Header
{
  "alg": "...",
  "kid": "did:example:ebfeb1f712ebc6f1c276e12ec21#primary"
}
EXAMPLE
// Payload
{
  "jti": "...",
  "iss": "did:example:ebfeb1f712ebc6f1c276e12ec21",
  "aud": "...",
  "callbackUrl": "https://example.com/api/callback-url",
  "purpose": "offer",
  "version": "0.1",
  "credential_manifest": {
    "issuer": {
      /* ... */
    },
    "output_descriptors": [
      /* ... */
    ],
    "presentation_definition": {
      /* ... */
    }
  }
}

§ Callback URL

§ Request

The offer/claim interaction follows the standard Callback URL Request payload, but the responseToken's is specific to the this interaction.

EXAMPLE
{
  "responseToken": "{{Signed JWT}}",
  "from": "qr" | "link"
}
§ Response Token

In addition to the standard responseToken the offer/claim interaction adds verifiable_presentation to the payload.

EXAMPLE
// Header
{
  "alg": "...",
  "kid": "did:example:c276e12ec21ebfeb1f712ebc6f1#primary"
}
EXAMPLE
// Payload
{
  "iss": "did:example:c276e12ec21ebfeb1f712ebc6f1",
  "aud": "did:example:ebfeb1f712ebc6f1c276e12ec21",
  "challenge": "{{CHALLENGE TOKEN}}",
  "verifiable_presentation": {
    /* ... */
    "type": ["VerifiablePresentation", "PresentationSubmission"],
    "presentation_submission": {
      /* ... */
    }
    /* ... */
  }
}

§ Response

In addition to the standard Callback URL Response payload, the offer/claim flow adds verifiable_presentation:

EXAMPLE
{
  "verifiable_presentation": [
    {
      /* ... */
      "type": ["VerifiablePresentation", "CredentialFulfillment"],
      "credential_fulfillment": {
        /* ... */
      }
      /* ... */
    }
  ]
}

OR

EXAMPLE
{
  "verifiable_presentation": [
    {
      /* ... */
      "type": ["VerifiablePresentation", "CredentialFulfillment"],
      "credential_fulfillment": {
        /* ... */
      }
      /* ... */
    }
  ]
  "redirectUrl": "https://example.com/redirect-url?id={{Some id that identifies the user}}"
}

OR

EXAMPLE
{
  "verifiable_presentation": [
    {
      /* ... */
      "type": ["VerifiablePresentation", "CredentialFulfillment"],
      "credential_fulfillment": {
        /* ... */
      }
      /* ... */
    }
  ]
  "challengeToken": "{{JWT String}}"
}

§ Swimlane

sequenceDiagram title: Offer/Claim (QR) activate Wallet Wallet ->>+ Issuer's Interface: Scan QR Code Issuer's Interface -->>- Wallet: Retrieve `challengeTokenUrl` Wallet ->>+ Issuer: GET `challengeTokenUrl` Issuer -->> Wallet: Return `challengeToken` Wallet ->> Wallet: Verify/decode `challengeToken` alt `challengeToken`'s `credential_manifest` contains a `presentation_definition` Wallet ->> Wallet: Collect VCs that are described in the `challengeToken`s `presentation_definition` Wallet ->> Wallet: Create VP containing the VCs, with `challengeToken` as the `challenge` Wallet ->> Wallet: Create/sign a `responseToken` containing the VP, with `challengeToken` as the `challenge` else `challengeToken` is provided Wallet ->> Wallet: Create/sign a `responseToken`, with `challengeToken` as the `challenge` end Wallet ->> Issuer: POST `responseToken` to `challengeToken`'s `callBackUrl` Issuer ->> Issuer: Verify `responseToken` Issuer ->> Issuer: Verify `responseToken`'s `challenge` (valid JWT, signed by issuer, and not used before) Issuer -->>- Wallet: Return list of signed credentials Wallet ->> Wallet: Store credentials opt `redirectUrl` or `challengeToken` is provided alt `redirectUrl` is provided Wallet ->> Browser: Open `redirectUrl` else `challengeToken` is provided Wallet ->> Wallet: Start new interaction end end deactivate Wallet
sequenceDiagram title: Offer/Claim (Link) User ->>+ Issuer's Interface: Click link Issuer's Interface ->>- Wallet: Open Wallet with deep link activate Wallet Wallet ->> Wallet: Parse deep link Wallet ->>+ Issuer: GET `challengeTokenUrl` Issuer -->> Wallet: Return `challengeToken` Wallet ->> Wallet: Verify/decode `challengeToken` alt `challengeToken`'s `credential_manifest` contains a `presentation_definition` Wallet ->> Wallet: Collect VCs that are described in the `challengeToken`s `presentation_definition` Wallet ->> Wallet: Create VP containing the VCs, with `challengeToken` as the `challenge` Wallet ->> Wallet: Create/sign a `responseToken` containing the VP, with `challengeToken` as the `challenge` else `challengeToken` is provided Wallet ->> Wallet: Create/sign a `responseToken`, with `challengeToken` as the `challenge` end Wallet ->> Issuer: POST `responseToken` to `challengeToken`'s `callBackUrl` Issuer ->> Issuer: Verify `responseToken` Issuer ->> Issuer: Verify `responseToken`'s `challenge` (valid JWT, signed by issuer, and not used before) Issuer -->>- Wallet: Return list of signed credentials Wallet ->> Wallet: Store credentials opt `redirectUrl` or `challengeToken` is provided alt `redirectUrl` is provided Wallet ->> Browser: Open `redirectUrl` else `challengeToken` is provided Wallet ->> Wallet: Start new interaction end end deactivate Wallet

§ Request/Share

The request/share interaction is for the use case where an verifier wants a user to share credential(s) with them.

§ Challenge Token

An example of a request challenge token has the following properties (in addition to the base properties):

EXAMPLE
// Header
{
  "alg": "...",
  "kid": "did:example:ebfeb1f712ebc6f1c276e12ec21#primary"
}
EXAMPLE
// Payload
{
  "jti": "...",
  "iss": "did:example:ebfeb1f712ebc6f1c276e12ec21",
  "aud": "...",
  "callbackUrl": "https://example.com/api/callback-url",
  "purpose": "request",
  "version": "0.1",
  "presentation_definition": {
    // ...
  }
}

§ Callback URL

§ Request

In addition to the standard Callback URL Request payload, the offer/claim flow adds presentation

EXAMPLE
{
  "responseToken": "{{Signed JWT}}",
  "from": "qr" | "link"
}
§ Response Token

In addition to the standard responseToken the offer/claim interaction adds verifiable_presentation to the payload.

EXAMPLE
// Header
{
  "alg": "...",
  "kid": "did:example:c276e12ec21ebfeb1f712ebc6f1#primary"
}
EXAMPLE
// Paylaod
{
  "iss": "did:example:c276e12ec21ebfeb1f712ebc6f1",
  "aud": "did:example:ebfeb1f712ebc6f1c276e12ec21",
  "challenge": "{{CHALLENGE TOKEN}}",
  "verifiable_presentation": {
    /* ... */
    "type": ["VerifiablePresentation", "PresentationSubmission"],
    "presentation_submission": {
      /* ... */
    }
    /* ... */
  }
}

§ Response

The request/share flow does not add anything to the Callback URL Response.

§ Swimlane

sequenceDiagram title: Request/Share (QR) activate Wallet Wallet ->>+ Verifier's Interface: Scan QR Code Verifier's Interface -->>- Wallet: Retrieve `challengeTokenUrl` Wallet ->>+ Verifier: GET `challengeTokenUrl` Verifier -->> Wallet: Return `challengeToken` Wallet ->> Wallet: Verify/decode `challengeToken` Wallet ->> Wallet: Collect VCs that are described in the `challengeToken`s `presentation_definition` Wallet ->> Wallet: Create VP containing the VCs, with `challengeToken` as the `challenge` Wallet ->> Wallet: Create/sign a `responseToken` containing the VP, with `challengeToken` as the `challenge` Wallet ->> Verifier: POST the `responseToken` to `challengeToken`'s `callBackUrl` Verifier ->> Verifier: Verify the `responseToken` Verifier ->> Verifier: Verify the `responseToken`'s challenge token (valid JWT, signed by verifier, and not used before) Verifier -->>- Wallet: Return success opt `redirectUrl` or `challengeToken` is provided alt `redirectUrl` is provided Wallet ->> Browser: Open `redirectUrl` else `challengeToken` is provided Wallet ->> Wallet: Start new interaction end end deactivate Wallet
sequenceDiagram title: Request/Share (Link) User ->>+ Verifier's Interface: Click link Verifier's Interface ->>- Wallet: Open Wallet with deep link activate Wallet Wallet ->> Wallet: Parse deep link Wallet ->>+ Verifier: GET `challengeTokenUrl` Verifier -->> Wallet: Return `challengeToken` Wallet ->> Wallet: Verify/decode `challengeToken` Wallet ->> Wallet: Collect VCs that are described in the `challengeToken`s `presentation_definition` Wallet ->> Wallet: Create VP containing the VCs, with `challengeToken` as the `challenge` Wallet ->> Wallet: Create/sign a `responseToken` containing the VP, with `challengeToken` as the `challenge` Wallet ->> Verifier: POST the `responseToken` to `challengeToken`'s `callBackUrl` Verifier ->> Verifier: Verify the `responseToken` Verifier ->> Verifier: Verify the `responseToken`'s challenge token (valid JWT, signed by verifier, and not used before) Verifier -->>- Wallet: Return success opt `redirectUrl` or `challengeToken` is provided alt `redirectUrl` is provided Wallet ->> Browser: Open `redirectUrl` else `challengeToken` is provided Wallet ->> Wallet: Start new interaction end end deactivate Wallet

§ Credential Manifest (Working Copy)

Because the Credential Manifest spec is just a strawman at the moment we will rely on a “frozen” copy of the spec outline below. The WACI spec will not be considered stable until the Credential Manifest spec is, but we want to be able to implement early prototypes against something so we need a version of the spec to base it off of.

This is the CM spec as of 02/22/2021.

Alterations in the spec are colored in green


§ Credential Manifest

Specification Status: Strawman

Latest Draft: identity.foundation/credential-manifest

Editors:
Daniel Buchner (Microsoft)
Brent Zundel (Evernym)
Participate:
GitHub repo
File a bug
Commit history

§ Abstract

For User Agents (e.g. wallets) and other service that wish to engage with Issuers to acquire credentials, there must exist a mechanism for assessing what inputs are required from a Subject to process a request for credential(s) issuance. The Credential Manifest is a common data format for describing the inputs a Subject must provide to an Issuer for subsequent evaluation and issuance of the credential(s) indicated in the Credential Manifest.

Credential Manifests do not themselves define the contents of the output credential(s), the process the Issuer uses to evaluate the submitted inputs, or the protocol Issuers, Subjects, and their User Agents rely on to negotiate credential issuance.

§ Status of This Document

Credential Manifest is a draft specification being developed within the Decentralized Identity Foundation (DIF), and intended for ratification as a DIF recommended data format. This spec will be updated to reflect relevant changes, and participants are encouraged to contribute at the following repository location: https://github.com/decentralized-identity/credential-manifest

§ Terminology

Decentralized Identifiers
Unique ID URI string and PKI metadata document format for describing the cryptographic keys and other fundamental PKI values linked to a unique, user-controlled, self-sovereign identifier in a target system (i.e. blockchain, distributed ledger).
Claim
An assertion made about a Subject. Used as an umbrella term for Credential, Assertion, Attestation, etc.
Issuer
Issuers are entities that issue credentials to a Holder.
Holder
Holders are entities that recieve credentials from Issuers, possibly first submitting proofs the the Issuer to satisfy the requirements described in a Presentation Definition.
Output Descriptor
Output Descriptors are used by an Issuer to describe the credentials they are offering to a Holder. See Output Descriptor
Output Descriptor Object
Output Descriptor Objects are populated with properties describing the Claims the Issuer is offering the Holder
Credential Fulfillment
Credential Fulfillments are objects embedded within target claim negotiation formats that unify the presentation of Claims to a Holder in accordance with the output an Issuer specified in a Credential Manifest. See Credential Fulfillment.

§ Resource Definition

Credential Manifests are a resource format that defines preconditional requirements, Issuer style preferences, and other facets User Agents utilize to help articulate and select the inputs necessary for processing and issuance of a specified credential.

EXAMPLE
{
  "id": "WA-DL-CLASS-A",
  "version": "0.1.0",
  "issuer": {
    "id": "did:example:123?linked-domains=3",
    "name": "Washington State Government",
    "styles": {
      "thumbnail": {
        "uri": "https://dol.wa.com/logo.png",
        "alt": "Washington State Seal"
      },
      "hero": {
        "uri": "https://dol.wa.com/people-working.png",
        "alt": "People working on serious things"
      },
      "background": {
        "color": "#ff0000"
      },
      "text": {
        "color": "#d4d400"
      }
    }
  },
  "output_descriptors": [
    {
      "schema": [{
        "uri": "http://washington-state-schemas.org/1.0.0/driver-license.json"
      }],
      "display": {
        "title": {
          "path": ["$.name", "$.vc.name"],
          "text": "Washington State Driver License"
        },
        "subtitle": {
          "path": ["$.class", "$.vc.class"],
          "text": "Class A, Commercial"
        },
        "description": {
          "text": "License to operate a vehicle with a gross combined weight rating (GCWR) of 26,001 or more pounds, as long as the GVWR of the vehicle(s) being towed is over 10,000 pounds."
        },
        "properties": [
          {
            "path": ["$.donor", "$.vc.donor"],
            "label": "Organ Donor"
          }
        ]
      },
      "styles": {
        "thumbnail": {
          "uri": "https://dol.wa.com/logo.png",
          "alt": "Washington State Seal"
        },
        "hero": {
          "uri": "https://dol.wa.com/happy-people-driving.png",
          "alt": "Happy people driving"
        },
        "background": {
          "color": "#ff0000"
        },
        "text": {
          "color": "#d4d400"
        }
      }
    }
  ],
  "presentation_definition": {
    // As defined in the Presentation Exchange specification
  }
}

§ General Composition

Credential Manifests are JSON objects composed as follows:

§ Output Descriptor

Output Descriptors are objects used to describe the Claims a Issuer if offering to a Holder.

Output Descriptor Objects contain schema URI that links to the schema of the offered output data, and information about how to display the output to the Holder.

EXAMPLE
{
  "output_descriptors": [
    {
      "id": "driver_license_output",
      "schema": "https://schema.org/EducationalOccupationalCredential",
      "display": {
        "title": {
          "path": ["$.name", "$.vc.name"],
          "text": "Washington State Driver License"
        },
        "subtitle": {
          "path": ["$.class", "$.vc.class"],
          "text": "Class A, Commercial"
        },
        "description": {
          "text": "License to operate a vehicle with a gross combined weight rating (GCWR) of 26,001 or more pounds, as long as the GVWR of the vehicle(s) being towed is over 10,000 pounds."
        },
        "properties": [
          {
            "path": ["$.donor", "$.vc.donor"],
            "label": "Organ Donor"
          }
        ]
      },
      "styles": {
        "thumbnail": {
          "uri": "https://dol.wa.com/logo.png",
          "alt": "Washington State Seal"
        },
        "hero": {
          "uri": "https://dol.wa.com/happy-people-driving.png",
          "alt": "Happy people driving"
        },
        "background": {
          "color": "#ff0000"
        },
        "text": {
          "color": "#d4d400"
        }
      }
    }
  ]
}
§ Output Descriptor Object

Output Descriptor Objects are composed as follows:

§ styles properties

Within a Credential Manifest, there are two areas where styling affordances are provided: under the issuer property, where the Issuer expresses information about themselves - including how a User Agent should style UI that represents the Issuer, and under the credential property, where the Issuer expresses information about the credntial itself - including how a User Agent should style UI for the credential itself. Under each of these areas an implementer MAY include a styles property, and if present, its value must be an object composed of the following properties:

§ display properties

The credential property of a Credential Manifest is an object that MAY contain a display property defining various content and data pointers for representation of a credential in UI. The properties in the object use Display Mapping Objects to assign text and data about the credential to common UI presentation elements, either by selecting data from the credential itself or providing it directly. The display object is constructed as follows

§ Display Mapping Objects
EXAMPLE
{
  "display": {
    "title": {
      "path": ["$.name", "$.vc.name"],
      "text": "Washington State Driver License"
    },
    "properties": [
      {
        "path": ["$.vision_aid", "$.vc.vision_aid"],
        "label": "Vision aid required"
      },
      {
        "path": ["$.donor", "$.vc.donor"],
        "label": "Organ Donor"
      }
    ]
  }
}

The Display Mapping Objects are JSON objects constructed as follows:

§ Resource Location

Credential Manifests should be retrievable at known, semantic locations that are generalized across all entities, protocols, and transports. This specification does not stipulate how Credential Manifests must be located, hosted, or retrieved, but does advise that Issuers SHOULD make their Credential Manifests available via an instance of the forthcoming semantic personal datastore standard being developed by DIF, W3C, and other groups (e.g. Identity Hubs).

§ Credential Fulfillment

Credential Fulfillments are objects embedded within target Claim negotiation formats that express how the outputs presented as proofs to a Holder are provided in accordance with the outpus specified in a Credential Manifest. Embedded Credential Fulfillment objects MUST be located within target data format as the value of a credential_fulfillment property, which is composed and embedded as follows:

EXAMPLE
{
  // NOTE: VP, OIDC, DIDComm, or CHAPI outer wrapper properties would be here

  "credential_fulfillment": {
    "id": "a30e3b91-fb77-4d22-95fa-871689c322e2",
    "manifest_id": "32f54163-7166-48f1-93d8-ff217bdb0653",
    "descriptor_map": [
      {
        "id": "banking_output_2",
        "format": "jwt_vc",
        "path": "$.verifiableCredential[0]"
      },
      {
        "id": "employment_output",
        "format": "ldp_vc",
        "path": "$.verifiableCredential[1]"
      },
      {
        "id": "citizenship_output_1",
        "format": "ldp_vc",
        "path": "$.verifiableCredential[2]"
      }
    ]
  }
}

§ Processing of path_nested Entries

EXAMPLE
{
  "credential_fulfillment": {
    "id": "a30e3b91-fb77-4d22-95fa-871689c322e2",
    "manifest_id": "32f54163-7166-48f1-93d8-ff217bdb0653",
    "descriptor_map": [
      {
        "id": "banking_output_2",
        "format": "jwt_vp",
        "path": "$.outerClaim[0]",
        "path_nested": {
          "id": "banking_output_2",
          "format": "ldp_vc",
          "path": "$.innerClaim[1]",
          "path_nested": {
            "id": "banking_output_2",
            "format": "jwt_vc",
            "path": "$.mostInnerClaim[2]"
          }
        }
      }
    ]
  }
}

When the path_nested property is present in a Credential Fulfillment object, process as follows:

  1. For each Nested Submission Traversal Object in the path_nested array:
    1. Execute the JSONPath expression string on the Current Traversal Object, or if none is designated, the top level of the Embed Target.
    2. Decode and parse the value returned from JSONPath execution in accordance with the Claim Format Designation specified in the object’s format property. If the value parses and validates in accordance with the Claim Format Designation specified, let the resulting object be the Current Traversal Object
    3. If present, process the next Nested Submission Traversal Object in the current path_nested property.
  2. If parsing of the Nested Submission Traversal Objects in the path_nested property produced a valid value, process it as the submission against the Output Descriptor indicated by the id property of the containing Output Descriptor Mapping Object.

§ Embed Targets

The following section details where the Credential Fulfillment is to be embedded within a target data structure, as well as how to formulate the JSONPath expressions to select the Claims within the target data structure.

§ Embed Locations

The following are the locations at which the credential_manifest object MUST be embedded for known target formats. For any location besides the top level of the embed target, the location is described in JSONPath syntax.

Target Location
VP top-level

§ JSON Schema

The following JSON Schema Draft 7 definition summarizes the rules above:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Credential Fulfillment",
  "type": "object",
  "properties": {
    "credential_fulfillment": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "manifest_id": { "type": "string" },
        "descriptor_map": {
          "type": "array",
          "items": { "$ref": "#/definitions/descriptor" }
        }
      },
      "required": ["id", "manifest_id", "descriptor_map"],
      "additionalProperties": false
    }
  },
  "definitions": {
    "descriptor": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "path": { "type": "string" },
        "path_nested": {
          "type": "object",
            "$ref": "#/definitions/descriptor"
        },
        "format": {
          "type": "string",
          "enum": ["jwt", "jwt_vc", "jwt_vp", "ldp", "ldp_vc", "ldp_vp"]
        }
      },
      "required": ["id", "path", "format"],
      "additionalProperties": false
    }
  },
  "required": ["credential_fulfillment"],
  "additionalProperties": false
}

§ Appendix

§ Embed Target Examples

§ Credential Fulfillment
EXAMPLE
{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://identity.foundation/credential-manifest/fulfillment/v1"
  ],
  "type": [
    "VerifiablePresentation",
    "CredentialFulfillment"
  ],
  "credential_fulfillment": {
    "id": "a30e3b91-fb77-4d22-95fa-871689c322e2",
    "manifest_id": "32f54163-7166-48f1-93d8-ff217bdb0653",
    "descriptor_map": [
      {
        "id": "banking_output_2",
        "format": "jwt_vc",
        "path": "$.verifiableCredential[0]"
      },
      {
        "id": "employment_output",
        "format": "ldp_vc",
        "path": "$.verifiableCredential[1]"
      },
      {
        "id": "citizenship_output_1",
        "format": "ldp_vc",
        "path": "$.verifiableCredential[2]"
      }
    ]
  },
  "verifiableCredential": [
    {
      "comment": "IN REALWORLD VPs, THIS WILL BE A BIG UGLY OBJECT INSTEAD OF THE DECODED JWT PAYLOAD THAT FOLLOWS",
      "vc": {
        "@context": "https://www.w3.org/2018/credentials/v1",
        "id": "https://eu.com/claims/DriversLicense",
        "type": ["EUDriversLicense"],
        "issuer": "did:example:123",
        "issuanceDate": "2010-01-01T19:73:24Z",
        "credentialSubject": {
          "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
          "accounts": [
            {
              "id": "1234567890",
              "route": "DE-9876543210"
            },
            {
              "id": "2457913570",
              "route": "DE-0753197542"
            }
          ]
        }
      }
    },
    {
      "@context": "https://www.w3.org/2018/credentials/v1",
      "id": "https://business-standards.org/schemas/employment-history.json",
      "type": ["VerifiableCredential", "GenericEmploymentCredential"],
      "issuer": "did:foo:123",
      "issuanceDate": "2010-01-01T19:73:24Z",
      "credentialSubject": {
        "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
        "active": true
      },
      "proof": {
        "type": "EcdsaSecp256k1VerificationKey2019",
        "created": "2017-06-18T21:19:10Z",
        "proofPurpose": "assertionMethod",
        "verificationMethod": "https://example.edu/issuers/keys/1",
        "jws": "..."
      }
    },
    {
      "@context": "https://www.w3.org/2018/credentials/v1",
      "id": "https://eu.com/claims/DriversLicense",
      "type": ["EUDriversLicense"],
      "issuer": "did:foo:123",
      "issuanceDate": "2010-01-01T19:73:24Z",
      "credentialSubject": {
        "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
        "license": {
          "number": "34DGE352",
          "dob": "07/13/80"
        }
      },
      "proof": {
        "type": "RsaSignature2018",
        "created": "2017-06-18T21:19:10Z",
        "proofPurpose": "assertionMethod",
        "verificationMethod": "https://example.edu/issuers/keys/1",
        "jws": "..."
      }
    }
  ],
  "proof": {
    "type": "RsaSignature2018",
    "created": "2018-09-14T21:19:10Z",
    "proofPurpose": "authentication",
    "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1",
    "challenge": "1f44d55f-f161-4938-a659-f8026467f126",
    "domain": "4jt78h47fh47",
    "jws": "..."
  }
}
Table of Contents