From c6df11bd69d1ab6fa3927cc984f49241280d94cb Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 10:26:13 -0400 Subject: [PATCH 01/12] Add initial VC API Exchanges docs. --- developers/wallets/exchanges.md | 174 ++++++++++++++++++++++++++++++++ developers/wallets/index.md | 1 + 2 files changed, 175 insertions(+) create mode 100644 developers/wallets/exchanges.md diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md new file mode 100644 index 0000000..1d8df08 --- /dev/null +++ b/developers/wallets/exchanges.md @@ -0,0 +1,174 @@ +--- +layout: subpage +title: "Handling a VC API Exchange in a Web Wallet" +permalink: /developers/wallets/exchanges/ +--- + +## {{ title }} + +> A VC API workflow defines a particular set of steps for exchanging verifiable +> credentials between two parties across a trust boundary. Each step can involve +> the issuance, verification, transmission, or presentation of verifiable +> credentials. + +A Web Wallet may receive an Exchange URL through CHAPI, reading a QR code, or +via some other user initiated transmission. + +Once that URL is received, the Wallet can initiate the exchange (acting as the +_exchange client_) by sending a POST request to that URL. + +Exchange URL: +``` +https://vcapi.example.com/exchanges/12345/ +``` + +To initiate an exchange using VC API, an exchange client performs an HTTP POST +sending a JSON object as the request body. In the simplest case, when the client +has no constraints of its own on the exchange — i.e., it has nothing to request +from the other party — the JSON object is empty ({})--as seen below: + +```http +POST /exchanges/12345/ +Host: vcapi.example.com + +{} +``` + +The workflow service then responds with its own JSON object in the response +body: + +```json +{} +``` + +If the response object is empty (as above), the exchange is complete and nothing +is requested from nor offered to the exchange client. + +If, however, the object includes `verifiablePresentationRequest`, then the +exchange is not yet complete and some *additional information is requested*, as +specified by the contents of the associated verifiable presentation request. + +For example: +```json +{ + "verifiablePresentationRequest": { + "query": [{ + "type": "QueryByExample", + "credentialQuery": [{ + "reason": "Please present proof of citizenship.", + "example": { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://w3id.org/citizenship/v1" + ], + "type": "PermanentResidentCard" + } + }] + }] + } +} +``` + +If the object includes `verifiablePresentation`, then some *information is +offered*, such as verifiable credentials issued to the holder operating the +exchange client (i.e. the Wallet) or verifiable credentials with information +about the exchange server's operator based on the exchange client's request. + +For example: +```json +{ + "verifiablePresentation": { + "@context": [ + "https://www.w3.org/ns/credentials/v2" + ], + "type": ["VerifiablePresentation"], + "verifiableCredential": [{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://w3id.org/citizenship/v1" + ], + "type": ["VerifiableCredential", "PermanentResidentCard"], + // additional properties... + }] + } +} +``` + +If the object includes `redirectUrl`, the *exchange is complete* and the +workflow service recommends that the client proceed to another place to continue +the interaction in another form. + +For example: +```json +{ + "redirectUrl": "https://vcapi.example.com/go-here-next/" +} +``` + +Many Verifiable Credential use cases can be implemented using these basic +primitives. Either party to an exchange is capable of requesting Verifiable +Presentations and of providing one or more Verifiable Credentials that might be +necessary to establish trust and/or gain authorization capabilities, and either +party is capable of presenting credentials that they hold or that they have +issued. Specific workflows can be configured to expect specific presentations +and credentials and to reject deviations from the expected flow of information. + +When a workflow service determines that a particular message is not acceptable, +it raises an error by responding with a `4xx` HTTP status message and a JSON +object that expresses information about the error. + +Below is an example of a typical exchange: + +```mermaid +sequenceDiagram + participant H as Holder + participant W as Holder Coordinator (Wallet) + participant I as Issuer/Verifier Coordinator + autonumber + Note right of H: Start exchange + W->>I: Initiate + Note right of W: POST /workflows/123/exchanges/123 — HTTP request to start exchange (e.g., send credentials, get credentials) + I->>W: Verifiable Presentation Request (VPR) + Note left of I: VPR includes method of interaction, for purposes of exchange + W->>I: Verifiable Presentation (VP) + Note right of W: POST /workflows/123/exchanges/abc — sent via interaction mechanism to meet requirements of exchange + I->>W: Verifiable Presentation + Note left of I: VP includes result of exchange (e.g., VCs), or VPR with new interaction request, or error description +``` + +The exchange client (Wallet) code for a flow like the above may look similar to +the following: + +```js +const receivedExchangeUrl = 'https://vcapi.example.com/workflows/123/exchanges/123'; + +const response = await fetch(receivedExchangeUrl, { + method: 'POST' + body: JSON.stringify({}) +}); +const body = response.json(); + +function checkResponse(body) { + if('verifiablePresentationRequest' in body) { + // use the information in the Verifiable Presentation Request to find a + // credentail that fullfills the request, then send a Verifiable + // Presentation as the response to the interaction endpoint included in the + // Verifiable Presentation Request + const presentation = findCredentialAndCreatePresentation(); + // TODO: properly extract interaction URL + const interactionUrl = body.interact.service[0].serviceEndpoint; + const response = await fetch(interactionUrl, { + method: 'POST', + body: JSON.stringify(presentation) + }); + checkResponse(response.json()); + } + // TODO: should these be exclusive? + if('verifiablePresentation' in body) { + // the Wallet has received a Verifiable Presentation containing one or more + // Verifiable Credentials--validate and verify them per your use case + } + if('redirectUrl' in body) { + // TODO: take the user to the new location OR does the exchange "move"? + } +} \ No newline at end of file diff --git a/developers/wallets/index.md b/developers/wallets/index.md index a8a7c41..c151252 100644 --- a/developers/wallets/index.md +++ b/developers/wallets/index.md @@ -12,6 +12,7 @@ CHAPI integrates easily into digital wallet software, allowing your wallet to re * [Verifiable Credential Storage](#verifiable-credential-storage) * [Verifiable Credential Presentation](#verifiable-credential-presentation) * [DID Authentication with CHAPI](#did-authentication-with-chapi) +* new! [Handling VC API Exchanges](exchanges/) ## Native Wallets * [Wallet Registration](native/#wallet-registration) From 902bd976ada6223afa147b29731b805c778f2c30 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 15:25:03 -0400 Subject: [PATCH 02/12] Remove trailing slashes from URLs. --- developers/wallets/exchanges.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index 1d8df08..1c86fe3 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -19,7 +19,7 @@ _exchange client_) by sending a POST request to that URL. Exchange URL: ``` -https://vcapi.example.com/exchanges/12345/ +https://vcapi.example.com/exchanges/12345 ``` To initiate an exchange using VC API, an exchange client performs an HTTP POST @@ -28,7 +28,7 @@ has no constraints of its own on the exchange — i.e., it has nothing to reques from the other party — the JSON object is empty ({})--as seen below: ```http -POST /exchanges/12345/ +POST /exchanges/12345 Host: vcapi.example.com {} @@ -101,7 +101,7 @@ the interaction in another form. For example: ```json { - "redirectUrl": "https://vcapi.example.com/go-here-next/" + "redirectUrl": "https://vcapi.example.com/go-here-next" } ``` From 2bb9134f223b43dfd68303f2fb314eca601ec461 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 15:28:09 -0400 Subject: [PATCH 03/12] Note the protocols object that CHAPI uses. --- developers/wallets/exchanges.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index 1c86fe3..edcd8b9 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -11,11 +11,24 @@ permalink: /developers/wallets/exchanges/ > the issuance, verification, transmission, or presentation of verifiable > credentials. -A Web Wallet may receive an Exchange URL through CHAPI, reading a QR code, or +A Web Wallet may receive an Exchange URL through CHAPI, by reading a QR code, or via some other user initiated transmission. -Once that URL is received, the Wallet can initiate the exchange (acting as the -_exchange client_) by sending a POST request to that URL. +CHAPI will provide a complete protocols object similar to the following: +```json +{ + "protocols": { + "vcapi": "https://vcapi.example.com/exchanges/12345", + "OIC4VCI": "openid-credential-offer://?..." + } +} +``` + +Alternatively, a QR code will only provide a single URL. + +Once a VC API URL is received (either via CHAPI, a QR code scan, etc.), the +Wallet can initiate the exchange (acting as the _exchange client_) by sending a +POST request to that URL. Exchange URL: ``` From 3d4a33590bbb403f9980ce980faeda09f2c21f47 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 15:55:37 -0400 Subject: [PATCH 04/12] Rework intro to talk about interaction URL. --- developers/wallets/exchanges.md | 44 ++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index edcd8b9..b4aa7d4 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -11,10 +11,11 @@ permalink: /developers/wallets/exchanges/ > the issuance, verification, transmission, or presentation of verifiable > credentials. -A Web Wallet may receive an Exchange URL through CHAPI, by reading a QR code, or -via some other user initiated transmission. +A VC API workflow interaction begins with _either_ a CHAPI event or an +interaction URL via a QR code (or similar user initiated transfer experience). -CHAPI will provide a complete protocols object similar to the following: +The CHAPI event will provide a complete protocols object similar to the +following: ```json { "protocols": { @@ -23,22 +24,30 @@ CHAPI will provide a complete protocols object similar to the following: } } ``` +Alternatively, a QR code can be used to provide a URL which when dereferenced +will result in either a `protocols` (object as above) or an HTML fallback page +to allow the user to continue otherwise. -Alternatively, a QR code will only provide a single URL. +To retrieve a `protocols` object from an _interaction URL_, the Wallet must send +an HTTP GET request including an explicit `Accept: application/json` request +header--which results in the same JSON object as above: -Once a VC API URL is received (either via CHAPI, a QR code scan, etc.), the -Wallet can initiate the exchange (acting as the _exchange client_) by sending a -POST request to that URL. +```http +GET /exchanges/12345/protocols +Host: vcapi.example.com +Accept: application/json -Exchange URL: -``` -https://vcapi.example.com/exchanges/12345 +{ + "protocols": { + "vcapi": "https://vcapi.example.com/exchanges/12345", + "OIC4VCI": "openid-credential-offer://?..." + } +} ``` -To initiate an exchange using VC API, an exchange client performs an HTTP POST -sending a JSON object as the request body. In the simplest case, when the client -has no constraints of its own on the exchange — i.e., it has nothing to request -from the other party — the JSON object is empty ({})--as seen below: +Once a VC API exchange URL is acquisitioned from `protocols.vcapi`, a POST +request is sent with a configuration object (which may be empty) to begin the +exchange: ```http POST /exchanges/12345 @@ -47,12 +56,7 @@ Host: vcapi.example.com {} ``` -The workflow service then responds with its own JSON object in the response -body: - -```json -{} -``` +A response will be returned by the exchanger... If the response object is empty (as above), the exchange is complete and nothing is requested from nor offered to the exchange client. From 399161ccd1deb54ffcd22f7bb6179e505f28d099 Mon Sep 17 00:00:00 2001 From: BigBlueHat Date: Tue, 10 Sep 2024 15:59:23 -0400 Subject: [PATCH 05/12] Apply suggestions from code review. Co-authored-by: Dave Longley --- developers/wallets/exchanges.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index b4aa7d4..b10562e 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -157,35 +157,33 @@ The exchange client (Wallet) code for a flow like the above may look similar to the following: ```js -const receivedExchangeUrl = 'https://vcapi.example.com/workflows/123/exchanges/123'; +const receivedExchangeUrl = 'https://vcapi.example.com/workflows/abc/exchanges/123'; const response = await fetch(receivedExchangeUrl, { method: 'POST' body: JSON.stringify({}) }); -const body = response.json(); +const body = await response.json(); function checkResponse(body) { if('verifiablePresentationRequest' in body) { // use the information in the Verifiable Presentation Request to find a - // credentail that fullfills the request, then send a Verifiable - // Presentation as the response to the interaction endpoint included in the + // credential that fulfills the request, then send a Verifiable + // Presentation a message back to the exchange endpoint // Verifiable Presentation Request - const presentation = findCredentialAndCreatePresentation(); - // TODO: properly extract interaction URL - const interactionUrl = body.interact.service[0].serviceEndpoint; - const response = await fetch(interactionUrl, { + const verifiablePresentation = findCredentialAndCreatePresentation(); + const response = await fetch(receivedExchangeUrl, { method: 'POST', - body: JSON.stringify(presentation) + body: JSON.stringify({verifiablePresentation}) }); checkResponse(response.json()); } // TODO: should these be exclusive? if('verifiablePresentation' in body) { // the Wallet has received a Verifiable Presentation containing one or more - // Verifiable Credentials--validate and verify them per your use case + // Verifiable Credentials--use them per your use case } if('redirectUrl' in body) { - // TODO: take the user to the new location OR does the exchange "move"? + // take the user to the new location } } \ No newline at end of file From c723a31094777c7c7cb3dde33f8deeaa3d5563d9 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 16:01:02 -0400 Subject: [PATCH 06/12] Remove TODO question about exclusive. --- developers/wallets/exchanges.md | 1 - 1 file changed, 1 deletion(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index b10562e..b870a6f 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -178,7 +178,6 @@ function checkResponse(body) { }); checkResponse(response.json()); } - // TODO: should these be exclusive? if('verifiablePresentation' in body) { // the Wallet has received a Verifiable Presentation containing one or more // Verifiable Credentials--use them per your use case From 1c6994bae5367a6b98b0daa7cb1654e3c7d9075d Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 16:03:20 -0400 Subject: [PATCH 07/12] Make workflow example URLs consistent. --- developers/wallets/exchanges.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index b870a6f..df16b47 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -19,7 +19,7 @@ following: ```json { "protocols": { - "vcapi": "https://vcapi.example.com/exchanges/12345", + "vcapi": "https://vcapi.example.com/workflows/abc/exchanges/12345", "OIC4VCI": "openid-credential-offer://?..." } } @@ -144,11 +144,11 @@ sequenceDiagram autonumber Note right of H: Start exchange W->>I: Initiate - Note right of W: POST /workflows/123/exchanges/123 — HTTP request to start exchange (e.g., send credentials, get credentials) + Note right of W: POST /workflows/abc/exchanges/123 — HTTP request to start exchange (e.g., send credentials, get credentials) I->>W: Verifiable Presentation Request (VPR) Note left of I: VPR includes method of interaction, for purposes of exchange W->>I: Verifiable Presentation (VP) - Note right of W: POST /workflows/123/exchanges/abc — sent via interaction mechanism to meet requirements of exchange + Note right of W: POST /workflows/abc/exchanges/abc — sent via interaction mechanism to meet requirements of exchange I->>W: Verifiable Presentation Note left of I: VP includes result of exchange (e.g., VCs), or VPR with new interaction request, or error description ``` From 3c20814a1a059ef9db5a06e51558e3a102782ef1 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 16:05:04 -0400 Subject: [PATCH 08/12] Clarify redirectUrl effecting the user. --- developers/wallets/exchanges.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index df16b47..e368b5b 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -112,8 +112,8 @@ For example: ``` If the object includes `redirectUrl`, the *exchange is complete* and the -workflow service recommends that the client proceed to another place to continue -the interaction in another form. +workflow service recommends that the client sent the user to another place to +continue the interaction. For example: ```json From bce5fc35a069a423f9bd7bfe03b6d06a3c1e0180 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Tue, 10 Sep 2024 16:27:15 -0400 Subject: [PATCH 09/12] Make all exchange URLs match. --- developers/wallets/exchanges.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index e368b5b..732826a 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -19,7 +19,7 @@ following: ```json { "protocols": { - "vcapi": "https://vcapi.example.com/workflows/abc/exchanges/12345", + "vcapi": "https://vcapi.example.com/workflows/abc/exchanges/123", "OIC4VCI": "openid-credential-offer://?..." } } @@ -33,13 +33,13 @@ an HTTP GET request including an explicit `Accept: application/json` request header--which results in the same JSON object as above: ```http -GET /exchanges/12345/protocols +GET /workflows/abc/exchanges/123/protocols Host: vcapi.example.com Accept: application/json { "protocols": { - "vcapi": "https://vcapi.example.com/exchanges/12345", + "vcapi": "https://vcapi.example.com/workflows/abc/exchanges/123", "OIC4VCI": "openid-credential-offer://?..." } } @@ -50,7 +50,7 @@ request is sent with a configuration object (which may be empty) to begin the exchange: ```http -POST /exchanges/12345 +POST /workflows/abc/exchanges/123 Host: vcapi.example.com {} @@ -148,7 +148,7 @@ sequenceDiagram I->>W: Verifiable Presentation Request (VPR) Note left of I: VPR includes method of interaction, for purposes of exchange W->>I: Verifiable Presentation (VP) - Note right of W: POST /workflows/abc/exchanges/abc — sent via interaction mechanism to meet requirements of exchange + Note right of W: POST /workflows/abc/exchanges/123 — sent via interaction mechanism to meet requirements of exchange I->>W: Verifiable Presentation Note left of I: VP includes result of exchange (e.g., VCs), or VPR with new interaction request, or error description ``` From 30e212d956f29c4af6ed443440eafd3f017700f9 Mon Sep 17 00:00:00 2001 From: BigBlueHat Date: Wed, 11 Sep 2024 13:59:27 -0400 Subject: [PATCH 10/12] Improve query reason explanation. Co-authored-by: Dave Longley --- developers/wallets/exchanges.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index 732826a..5b5d141 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -72,7 +72,7 @@ For example: "query": [{ "type": "QueryByExample", "credentialQuery": [{ - "reason": "Please present proof of citizenship.", + "reason": "We require proof of residency to onboard you.", "example": { "@context": [ "https://www.w3.org/ns/credentials/v2", From 6c51f3b4c80fe917ad139653b78a0d1b9847b8e8 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Wed, 11 Sep 2024 15:25:05 -0400 Subject: [PATCH 11/12] Cite the VC API spec for intro quote. --- developers/wallets/exchanges.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index 5b5d141..bde0ccc 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -6,10 +6,16 @@ permalink: /developers/wallets/exchanges/ ## {{ title }} +
+ > A VC API workflow defines a particular set of steps for exchanging verifiable > credentials between two parties across a trust boundary. Each step can involve > the issuance, verification, transmission, or presentation of verifiable > credentials. +
+ from VC API Workflows & Exchanges +
+
A VC API workflow interaction begins with _either_ a CHAPI event or an interaction URL via a QR code (or similar user initiated transfer experience). From a7bf93d0c8fadc024c58b07b889e4aa278bd4ec3 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Wed, 11 Sep 2024 15:33:46 -0400 Subject: [PATCH 12/12] Add link to Verifiable Presentation Req spec. --- developers/wallets/exchanges.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/developers/wallets/exchanges.md b/developers/wallets/exchanges.md index bde0ccc..f3acb78 100644 --- a/developers/wallets/exchanges.md +++ b/developers/wallets/exchanges.md @@ -71,6 +71,10 @@ If, however, the object includes `verifiablePresentationRequest`, then the exchange is not yet complete and some *additional information is requested*, as specified by the contents of the associated verifiable presentation request. +> Read more about possible values in the +> [Verifiable Presentation Requests](https://w3c-ccg.github.io/vp-request-spec/#overview) +> specification. + For example: ```json {