This specification describes a declarative JSON-based query language used by applications to perform requests from wallets and agents. The results of the requests are always wrapped in a Verifiable Presentation.

This draft highlights some of the pending issues that are still to be discussed in the community group. No decision has been taken on the outcome of these issues including whether they are valid. Pull requests with proposed specification text for outstanding issues are strongly encouraged.

Introduction

When working with Verifiable Credentials, Decentralized Identifier (DID) based Authentication, and Authorization Capabilities, a client application often needs to request credential-related objects from a wallet or agent. This document presents a specification that describes the format of those requests.

Note: This specification is unstable at present, and only reflects an effort to get initial interop working, for incubation and implementation experience. Additionally, the intention is to align this spec and the query format to fit into/work with other protocols/messaging formats such as DIDComm.

Request Overview

To make a request for one or more objects wrapped in a Verifiable Presentation, a client constructs a JSON request describing one or more queries that it wishes to perform from the receiver.

{
  query: [{
    type: 'APopularQueryType',
    // query details ...
  }, {
    type: 'AnotherQueryType',
    // query details ...
  }],

  // Challenge that will be digitally signed in the authentication proof
  // that will be attached to the VerifiablePresentation response
  challenge, // Required
  recipients: [
    // an optional key agreement key for encrypting the response if
    // this is supported
  ]
}
      

Query Types

The query type serves as the main extension point mechanism for requests for data in the presentation. This document defines several common query types.

Query By Example

{
  "query": [
    {
      "type": "QueryByExample",
      "credentialQuery": [
        {
          // One or more example query entries
          "required": false, // (Optional) Defaults to 'true' if omitted
          // (Optional) Reason for requesting this credential that
          // may be shown to a user by their wallet software
          "reason": "We need you to prove your eligibility to work.",
          "example": {
            "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/citizenship/v1"],
            "type": "PermanentResidentCard",
            // (Optional) You can request a specific subject id
            "credentialSubject": {
              "id": "...",
              "name": "..."
            },
            // (Optional) Specify only credentials of a particular schema
            "credentialSchema": {
              "id": "urn:foo:1234",
              "type": "SomeType"
            },
            // (Optional) Specify credentials from a particular issuer only
            "trustedIssuer": [
              {
                "required": true,
                "issuer": "urn:some:required:issuer"
              }
            ],
            // (Optional)
            "issuerQuery": [
              //
            ]
          }
        },
        {
          // Another example query
          "type": "AnotherQueryType"
          // ...
        }
      ]
    }
  ],
  "challenge": "3182bdea-63d9-11ea-b6de-3b7c1404d57f",
  // the domain that will be digitally signed in the authentication
  // proof that will be attached to the VerifiablePresentation
  // response, identifying the recipient
  "domain": "jobs.example.com"
}
        

DID Authentication Request

{
    query: [{
      type: 'DIDAuth'
    }],
    challenge: '99612b24-63d9-11ea-b99f-4f66f3e4f81a',
    domain: 'example.com'
  }
}
        

Response:

{
  "@context": ["https://www.w3.org/2018/credentials/v1"],
  "type": "VerifiablePresentation",
  "holder": "did:example:12345",
  "proof": {
    "proofPurpose": "authentication",
    "type": "Ed25519Signature2018",
    "challenge": "99612b24-63d9-11ea-b99f-4f66f3e4f81a",
    "domain": "example.com",
    "created": "2020-06-06T21:05:13Z",
    "verificationMethod": "did:example:12345#z6Mkkg...",
    "jws": "..."
  }
}
        

Authorization Capability Request

This query type would be included in a request to ask for Authorization Capabilities or "zcaps" in the Verifiable Presentation.

{
  query: [{
    type: 'ZcapQuery',
    capabilityQuery: [{
      referenceId: `a-memorable-name`,
      allowedAction: ['read', 'write'],
      invoker: 'did:key:1234',
      delegator: 'did:key:1234'
      invocationTarget: {
        type: 'urn:edv:documents'
      }
    }, {
      referenceId: `another-memorable-name`,
      allowedAction: 'sign',
      invoker: 'did:key:1234',
      delegator: 'did:key:1234',
      invocationTarget: {
        type: 'Ed25519VerificationKey2018',
        proofPurpose: 'assertionMethod'
      }
    }],
    challenge: '111112b24-63d9-11ea-b99f-4f66f3e4f81a'
  }]
}
        

Usage Scenarios

This request query format is intended to be used in a variety of protocols and usage scenarios.

Browser - Credential Handler API (CHAPI)

The Credential Handler API (CHAPI) specification enables in-browser Javascript applications to communicate with wallet providers for the purpose of issuing Verifiable Credentials and requesting Verifiable Presentations. Interested implementers are encouraged to look at the Credential Handler Polyfill repository for further discussion and examples.

CHAPI is an extension of the Credential Management API, and includes the following:

A VerifiablePresentation is used to both store or present VerifiableCredentials. When storing a VerifiableCredential, the VerifiablePresentation does not need to be signed.

WebCredential

CHAPI provides a single derived class, the WebCredential that forms the basis for any sort of credential data that is provided over the Web, i.e., via a "Credential Handler" that a origin has registered in the user's browser when the user visited that origin's website.

const webCredential = new WebCredential('VerifiablePresentation', {
  '@context': 'https://www.w3.org/2018/credentials/v1',
  ...presentation
});
          

Requesting and Storing Credentials

Using CHAPI, a web application can get() and store() credentials without knowing anything about the user's wallet. This is intentional; for privacy reasons, the client app must not be able to query any information (without user consent) about which wallets or credential handlers a user may have installed (otherwise, fingerprinting and other attacks would be possible).

get()

A web app (a Relying Party or verifier) can request a credential using credentials.get() during a user gesture event, for example when the user pushes a button on a page that requires identity attributes or authentication.

const credentialQuery = {
  web: {
    VerifiablePresentation: {
      query: {
        type: 'QueryByExample',
        credentialQuery: {
          // an optional reason for requesting this credential that
          // may be shown to a user by their wallet software
          reason: 'We need you to prove your eligibility to work.',
          example: {
            '@context': [
              'https://www.w3.org/2018/credentials/v1',
              'https://w3id.org/citizenship/v1'
            ],
            type: 'PermanentResidentCard'
          }
        }
      },
      // a 128-bit randomly generated value encoded as a string (use a UUID);
      // it will be digitally signed in the authentication proof
      // that will be attached to the VerifiablePresentation response
      challenge: '3182bdea-63d9-11ea-b6de-3b7c1404d57f',
      // the domain that must be digitally signed in the authentication
      // proof that will be attached to the VerifiablePresentation
      // response, identifying the recipient
      domain: 'jobs.example.com'
    }
  }
};
const webCredential = await navigator.credentials.get(credentialQuery);

if(!webCredential) {
  console.log('no presentation received');
}

// Response:

null // if the user cancels

// or a WebCredential with these attributes/values:
{
  "type": "web",
  "dataType": "VerifiablePresentation",
  "data": {
    // Verifiable Presentation goes here, containing the credentials
    // that the user agreed to share, or `null` if the user canceled
  }
}

const {data: presentation} = webCredential;
// send `presentation` to server for forwarding to verifier API
          

store()

A web app (for example, a credential issuer such as a university or institution) can ask to store a credential during a user gesture event, for example when the user pushes a button to receive a credential.

const result = await navigator.credentials.store(webCredential);
if(!result) {
  console.log('store credential operation canceled');
}

// Response:

null // if the user cancels

// or a WebCredential with these attributes/values:
{
  "type": "web",
  "dataType": "VerifiablePresentation",
  "data": {
    // Verifiable Presentation goes here, containing the credentials
    // that the user agreed to store
  }
}
          

Mobile Applications

TBD

Peer to Peer

TBD. (The intention of this spec is to align the query format with existing protocols and messaging formats such as DIDComm.)

Security and Privacy Considerations

There are a number of security and privacy considerations that implementers will want to take into consideration when implementing this specification.

Consideration

TBD

Acknowledgements

The Working Group would like to thank the following individuals for reviewing and providing feedback on the specification (in alphabetical order):

TBD...