w3c-ccg / vc-api

A specification for an HTTP API used to issue and verify Verifiable Credentials.
https://w3c-ccg.github.io/vc-api
Other
123 stars 47 forks source link

PROPOSAL: Interactive Issue and Re-Issue VC API flow #245

Closed dmitrizagidulin closed 2 years ago

dmitrizagidulin commented 2 years ago

There is a number of use cases for VC issuing that require one or more interactive request-response rounds, where an Issuer requires certain pre-requisites before actually issuing requested VCs.

Use Case Examples

For example, consider a typical Re-Issue flow.

  1. Client: "Issuer, I would like an expired credential to be re-issued."
  2. Issuer: "Great, please present me a copy of the expired credential, a fresh instance of DIDAuth, and supporting VCs X, Y, and Z. You may submit them to the following endpoint."
  3. Client: (POSTs to the designated endpoint) "Here are your required pre-requisites, in a VP."
  4. Issuer: "Here is you re-issued credential (wrapped in a VP, as usual)."

Similarly, consider this situation, that's frequently encountered during "onramp" deployments, where VC issuing needs to interop with an existing identity management system. A typical example of this is the issuing of an educational credential (such as for a completion of a course).

  1. Client: "Issuer, I would like to receive a credential for the completion of a course."
  2. Issuer: "Great, please provide the following pre-requisites: a) an instance of DIDAuth (with the DID that you want the credential issued to)., b) an instance of an OpenID Connect ID Token or Access Token from the university's OIDC provider (so that we can verify in our databases which student is requesting the credential, if they're eligible, etc)., and c) a VC receipt / proof of payment. Please provide them as a VP, to the following endpoint."
  3. Client: "I have performed the OpenID Connect authorization flow, and received the required access token. Here are the pre-requisites, including the access token, in a VP signed by a DID, serving as an example of DID Auth."
  4. Issuer: "Here is your proof of course completion credential."

These examples only have one round of back-and-forth negotiation between, but hopefully it's clear that multiple iterations of this process can also occur, before a VC gets issued.

Existing Techniques and Components

This proposal takes into consideration several existing tech ecosystems, for inspiration / prior art.

W3C CCG Credential Handler API (CHAPI)

The Credential Handler API (CHAPI) protocol provides a minimal transport pipe (a .get() and a .store() API), for a web application to communicate with a server-side wallet. By using the VP Request Spec for a data model, an app developer can put together a web application flow that provides iterative back-and-forth negotiation between a client app and the user's wallet, to perform DIDAuth and to ask for and receive pre-requisites before finally issuing (via .store()) a credential.

The CHAPI protocol focuses on the communication between a general purpose web app (playing either an Issuer or a Verifier role) and a wallet, and does not have an explicit notion of "issuer, please issue a given VC". This proposal re-uses the VP Request Spec for a data model, and extends the capabilities of CHAPI-using wallets to be able to interact with server-side Issuers implementing the VC API.

DIF WACI Presentation Exchange

DIF's WACI Presentation Exchange (which is based on the DIF Presentation Exchange spec), and specifically the Presentation Definition and the Submission Requirements sections, allow Verifiers to request pre-requisites (including VCs) from Holders/Presenters. WACI Pres. Exchange intends to use the Aries RFC 0454: Present Proof Protocol 2.0, which allows for multiple request/response rounds between the Holder and Verifier (using propose-presentation / request-presentation messages), using the threading mechanism (?) of DIDComm2 for state management/continuation.

As the name suggests, this protocol focuses on Holder to Verifier interaction, but not to Issuer.

Aries RFC 0453: Issue Credential Protocol 2.0

Similarly to WACI Presentation Exchange's use of Aries 0454, the Aries RFC 0453: Issue Credential Protocol 2.0 allows for an iterative cycle of credential requests (from the Holder) and credential offers (from the Issuer). Although there is a ~payment-request decorator mechanism (for an Issuer to require payment for the issuance), there does not seem to be an explicit mechanism for Issuer to specify pre-requisite credentials before issuing the requested one.

OIDF SIOP v2 and OIDC for VP

The OpenID Connect Self-Issued (SIOP) v2 and the OpenID Connect for Verifiable Presentations protocols can be used by a client application to request an Issuer to issue VCs. SIOP does not specify a mechanism for iterative negotiation, however -- there's no provision for an issuer to specify pre-requisites.

IETF GNAP

GNAP, although not explicitly an issue credentials protocol, has the Request Continuation mechanism, which allows for iterative negotiation between requester and authorization server.

VC API Iterative Issue Flow

This proposal extends the VC API Issue endpoint with the ability for the Issuer to require pre-requisites from the requester of the credential.

1. Requester (e.g. Wallet) Starts Issue Flow

The requester makes a POST request to the /credentials/issue endpoint, requesting an issuance of an CourseCompletionCredential

POST /credentials/issue
Host: issuer.example.com
Content-Type: application/json

{
  "credential": {
    "type": [ "VerifiableCredential", "CourseCompletionCredential" ]
  }
}

2. Issuer replies with a VP Request

The issuer responds with a VP Request object containing the pre-requisites that it must have before issuing.

{
  "query": [
    {
        // A request for the controller's DID
        type: 'DIDAuth'
    },
    {
      "type": 'QueryByExample',
      "credentialQuery": [
        {
          "reason": "Please provide a pre-requisite credential.",
          "trustedIssuer": [{
              "issuer": "issuer.example.com"
          }],
          "example": {
            "type": "APreRequisiteCredential"
          }
       },
       {
          "trustedIssuer": [{
              "issuer": "op.example.com"
          }],
          "example": {
            "type": "OpenIdConnectIdTokenCredential"
          }
      }
  }],
  "domain": "issuer.example.com",
  "challenge": "99612b24-63d9-11ea-b99f-4f66f3e4f81a",
  "interact": {
    "service": [{
      "type": "VcApiPresentationService2021",
      // The ID on the end is bound to this particular request
      "serviceEndpoint": "https://issuer.example.com/active-flows/123456"
    }]
  }
}

Note the interact.service.serviceEndpoint property in the Issuer response, showing where this iterative flow can be continued.

3. Requester (Wallet) Continues Flow (Provides Pre-Reqs)

The requester composes the response (assembles the pre-reqs requested by the issuer), packages it in a VP, and POSTs it to the interact.service.serviceEndpoint.

POST /active-flows/123456
Host: issuer.example.com

{
  "@context": "https://www.w3.org/2018/credentials/v1",
  "type": "VerifiablePresentation",
  "verifiableCredential": [
    {  // APreRequisiteCredential goes here },
    {  // OpenIdConnectIdTokenCredential goes here }
  ],
  "proof": {
     // The requester signs this VP with the controller's "authentication" key, fulfilling the DIDAuth request
  }
}

4. Issuer Responds With Issued VC

If the submitted pre-reqs satisfy the issuer, it issues and returns the VCs, in the usual format of the VC API spec.

Using Interactive Issue Flow for Re-Issuing

This same mechanism can also be used for Re-Issuing expiring credentials (provided that the Issuer supports this mechanism).

1. Refresh Service Discovery

If a VC can be re-issued after expiration, it includes the refreshService property, which specifies the VP API Issuer endpoint where a new VC can be requested.

Example VC:

{
  "@context": [],
  "id": "http://example.gov/credentials/3732",
  "type": ["VerifiableCredential", "AnExpiringCredential"],
  "issuer": "did:example:123",
  "issuanceDate": "2020-03-16T22:37:26.544Z",
  "credentialSubject": {}
  "refreshService": {
    "id": "<credential.id>#refreshService",
    "type": "VerifiableCredentialRefreshService2021",
    "url": "https://issuer.example.com/credentials/issue",
  },
  "proof": { .. }
}

A wallet wanting a re-issued VC looks in the refreshService.url property, so it knows where to ask for re-issue.

2. Requester (Wallet) Starts Re-Issue Flow

The requester (wallet) makes a POST to the issuer endpoint it discovered in the previous step.

POST /credentials/issue
Host: issuer.example.com

{
  "credential": {
    "type": [ "VerifiableCredential", "AnExpiringCredential" ]
  }
}

3. Issuer replies with pre-reqs

Issuer requests a copy of the expired credential.

{
  "query": [
    {
        // Optional request for the controller's DID
        type: 'DIDAuth'
    },
    {
      "type": 'QueryByExample',
      "credentialQuery": [
        {
          "reason": "Please provide the expired credential.",
          "trustedIssuer": [{
              "issuer": "issuer.example.com"
          }],
          "example": {
            "type": "AnExpiringCredential"
          }
       }
  }],
  "domain": "issuer.example.com",
  "challenge": "99612b24-63d9-11ea-b99f-4f66f3e4f81a",
  "interact": {
    "service": [{
      "type": "VcApiPresentationService2021",
      "serviceEndpoint": "https://issuer.example.com/active-flows/123456"
    }]
  }
}

And the flow proceeds as with the regular Issue flow above.

dlongley commented 2 years ago

Another note for use cases where multiple rounds of exchange are required:

Instead of the issuer responding with a VP that contains the credentials requested after one round of negotation, it may also respond with another VPR and another service endpoint for continued interaction. This could go on for as many rounds as are needed for a particular use case. The basic architecture doesn't need to change to support this.

bumblefudge commented 2 years ago

Nit: Technically WACI-PEx uses Present Proof v3, which is essentially identical to PresentProof v2 except that is also allows the new attachment types needed to speak Pres-Ex-ese. I believe the plan is to finish WACI-PEx and then move PPv3 (which might, by that point, include more additions or breaking changes) to the Aries community for ratification qua Aries RFC.

Also issuance has been added to the scope for the next tranche of work, but it's very early-days/strawman at the moment.

dmitrizagidulin commented 2 years ago

@bumblefudge - nice, makes sense.

troyronda commented 2 years ago

@dmitrizagidulin @bumblefudge Latest WACI text now includes an issuance description (very recent change):

https://identity.foundation/waci-presentation-exchange/#issuance

and

https://identity.foundation/waci-presentation-exchange/#issuance-2

msporny commented 2 years ago

Great write up, thank you @dmitrizagidulin. I have a few minor questions:

an instance of an OpenID Connect ID Token or Access Token from the university's OIDC provider (so that we can verify in our databases which student is requesting the credential, if they're eligible, etc)

How does the client get that and send it along? I get that we might not care right now and it's probably just a placeholder... but, it also sounds like something the ecosystem might want to appear sooner than later. Do we have to create a new VC type for this in some other group? What timeline are we thinking here?

interact / interact.service / etc.

I expect interact is a new field that's needed in the VPR spec? What is the value range of this field? Looks like you could squint and shove a DID Document into that field? Are we just re-using DID Core terminology here, even though this is all JSON? What spec does this get defined in, VPR and then VC-API just picks it up and uses it from there?

POST /credentials/issue

The issue endpoint is probably not the one we want because it's intended to be a "MUST be authorized" endpoint. We probably want to kick off a new workflow (as described in the VC API use cases): https://w3c-ccg.github.io/vc-api-use-cases/index.html#refresh-expired-over-age-token (but even that description is a bit wonky right now).

I think what we may want to hit is something like: POST \<arbitrary-path>/workflows/\<specific-refresh-workflow-name>

That then flows into "VC API Iterative Issue Flow" step 2, or "Using Interactive Issue Flow for Re-Issuing" step 3. So, the calls would be:

VC API Iterative Issue Flow

  1. POST \<arbitrary-path>/workflows/\<specific-issuance-workflow-name> (post data should probably be VP Request)
  2. Response is a VP Request with query by example and interact to /active-flows/123456
  3. POST VP to /active-flows/123456
  4. Response is the credentials you requested in a VP

Using Interactive Issue Flow for Re-Issuing

  1. Refresh Service Discovery via existing VC
  2. Goto VC API Iterative Issue Flow step 1 (above if DID Auth needed) ... or only steps 1 and 4 are needed if DID Auth is not required.

Is there any reason not to go the route above? It feels more compose-able than overloading the /issue endpoint to something we didn't intend it to do (unauthenticated interactions).

dmitrizagidulin commented 2 years ago

@msporny

an instance of an OpenID Connect ID Token or Access Token from the university's OIDC provider (so that we can verify in our databases which student is requesting the credential, if they're eligible, etc)

How does the client get that and send it along? I get that we might not care right now and it's probably just a placeholder... but, it also sounds like something the ecosystem might want to appear sooner than later. Do we have to create a new VC type for this in some other group? What timeline are we thinking here?

The DCC wallet project is in the process of deploying that use case now, so, I totally agree that it should appear sooner. So, the way it goes is:

A QR code (say, sent to a student via email) contains a deep link (though see my slides about why that is highly problematic), that looks like this:

dccrequest://request?                  // DCC: mobile app deep link
    &auth_type=<auth_type>             // authentication protocol type (e.g. OIDC authorization code, SAML, etc)
    &issuer=<issuer>                   // Key of the auth configuration
    &vc_request_url=<vc_request_url>   // DCC: verifiable credential request url

This gives a mobile wallet (or any custom protocol deep link handler) the following parameters:

  1. auth_type (currently only code, which stands for 'OpenID Connect Authorization Code Flow`) - this determines what existing authentication protocol to initiate.
  2. issuer - for OpenID Connect, this is the issuer/provider URL that an OIDC client uses to perform OpenID Provider Discovery.
  3. vc_request_url - this is the URL of this proposed VC-API issue endpoint.

So, the app gets the deep link, decodes the params, looks at auth_type to see which authentication client it needs to reach for. Since it's currently only code, it fires up an OIDC client, performs Provider Discovery (using the issuer url), and kicks off an OIDC Authorization Code Flow. (Which typically gets intercepted and handled by the mobile OS).

At the end of the OIDC protocol flow, the user gets redirected back to the wallet app, and the wallet app receives an Access Token, and optionally an ID Token.

So now, the wallet has all the pieces it needs to go over to Issue VC-API interactive flow. It makes a POST to the issue endpoint (which, incidentally, it gets from the vc_request_url deep link param) and requests its credential. And it uses the OIDC Access Token as authentication (using the Authorization: Bearer <access token> header).

Does that help?

dmitrizagidulin commented 2 years ago

POST /credentials/issue

The issue endpoint is probably not the one we want because it's intended to be a "MUST be authorized" endpoint.

In this particular use case (described above) it IS authorized (via an Authorization header).

In terms of whether to re-use the /credentials/issue endpoint, or ask the issuer to come up with workflow-specific API endpoint names.. I dunno, I think it might be easier to reuse /issue. Especially since, as you suggest, the POST body would be the same (in a regular Issue request, vs an interactive one).

dmitrizagidulin commented 2 years ago

interact / interact.service / etc.

I expect interact is a new field that's needed in the VPR spec? What is the value range of this field? Looks like you could squint and shove a DID Document into that field? Are we just re-using DID Core terminology here, even though this is all JSON? What spec does this get defined in, VPR and then VC-API just picks it up and uses it from there?

Yeah, exactly. interact would be a new field, that would need to be added to the VP Request spec. Incidentally, the interact terminology was borrowed from the GNAP spec section 2 -- it's used in the same way, to denote a way where an interactive flow can be continued. In terms of value range - the proposal just has it be an object with a service field.

And yes, the service terminology was borrowed from the DID spec, because it seemed like an already agreed-upon way to denote API endpoints.

jrhender commented 2 years ago

@dmitrizagidulin Thanks for this proposal 🙇 . I have a question if you have time.

You mention that, after the authentication protocol flow:

So now, the wallet has all the pieces it needs to go over to Issue VC-API interactive flow. It makes a POST to the issue endpoint (which, incidentally, it gets from the vc_request_url deep link param) and requests its credential.

And the QR code example you provided is

dccrequest://request?                  // DCC: mobile app deep link
    &auth_type=<auth_type>             // authentication protocol type (e.g. OIDC authorization code, SAML, etc)
    &issuer=<issuer>                   // Key of the auth configuration
    &vc_request_url=<vc_request_url>   // DCC: verifiable credential request url

How does the requester wallet know what to put in the body of the POST /credentials/issue request? I'm guessing that it in your example it knows to request a dcc credential because of the dccrequest protocol?

dmitrizagidulin commented 2 years ago

@jrhender

How does the requester wallet know what to put in the body of the POST /credentials/issue request?

That's a really great question. (And I was pondering this too, the other day.)

We're going to need to have a conversation about it, as a working group and as a community.

Basically, there's only a few fundamental options, to this question.

  1. The requester (like the wallet) knows what credentials to ask for due to some out of band app-specific mechanism, or because the user asked the wallet specifically. (Which doesn't really answer your question of "so what is that mechanism?")
  2. The wallet knows what VCs to ask for because the user is asking to re-issue an existing expired VC.
  3. The Issuer can notify the wallet that SOME credentials are available, but does not tell which ones. (That's mostly the current DCC QR Code / example.)
  4. The Issuer notifies the wallets listing exactly which types of credentials are available (or awaiting issue).

So, option 2 is easy -- it's a re-issue flow, so the wallet knows exactly which credentials expired (and ask for them specifically).

With option 3, the implication is that the wallet receives a "some credentials available" message from the issuer, and when the wallet starts the Issue flow, the Issuer will know which ones to issue. Incidentally, this is the model that many banking and hospital systems adopt (when you get an email that says "A new message from your bank is available", but you have to log in to actually see what it is). Except in those cases, there's a UI that a person looks at, and some inbox that is tracking which messages are read/unread. Doesn't exactly apply here to this proposal. I suspect this is what the current VC API's /presentations/available sort of means to address? (it's not clear from the spec). However, that's the simplest option, and the model that the DCC Issuers currently take. (Basically, they issue everything the user is "eligible for", and the wallet rejects duplicates).

And option 4 would mean that the issuer includes the VC types available in the initial QR code / deep link. And while you wouldn't want to send this message in an email, due to privacy concerns, there are valid use cases for it -- for example, in a learning management system (like Canvas), a user navigates to a particular class, and is presented with a QR code for the completion of that class only. (So, the issuer system knows exactly which credential is available). To support this, we would need to add another parameter to the QR code / deep link. Something like:

dccrequest://request?                  
    &auth_type=code
    &issuer=https://issuer.example.com
    &vc_request_url=https://issuer.example.com/credentials/issue
    &type_available=ExampleCredential1
    &type_available=CourseCompletionCredential2
jrhender commented 2 years ago

@dmitrizagidulin Thanks for the breakdown of the options.

Re option 3,

I suspect this is what the current VC API's /presentations/available sort of means to address? (it's not clear from the spec).

I'm not sure I'm following the parallel between option 3 and VC API's /presentations/available. My understanding of /presentations/available is that it is for a (client) holder wishing to making a presentation to notify another (server) holder that they have a presentation "available". I'm getting this understanding mainly from the API docs as I think the 3.4.3 Presentation Availability Section in the spec doesn't make this that clear (as you might have been saying). I'm guessing that maybe you meant that something like a GET /credentials/available could be used by a requester to ask an issuer what credentials they can offer them?

dmitrizagidulin commented 2 years ago

@jrhender - ahhh, you're right, I missed the API docs section of /presentations/available!

So yeah, maybe it would make sense to propose another endpoint, /credentials/available, for the Issuer to host.

OR13 commented 2 years ago

We support a version of this, that bridges CHAPI and the VC API... we call it "Verifiable Business Cards"... https://w3c-ccg.github.io/traceability-vocab/#VerifiableBusinessCard

They are a way to make a credential which announces a service endpoint that Holders can use to present to.

msporny commented 2 years ago

There is now a spec proposal for this issue here: https://digitalbazaar.github.io/vc-refresh-2021/

peacekeeper commented 2 years ago

Is this issue being addressed by https://github.com/w3c-ccg/vc-api/pull/254 and https://github.com/w3c-ccg/vc-api/pull/255 ? Can this issue be closed once those PRs are merged ?

OR13 commented 2 years ago

I don't think the proposed spec actually addresses this issue.

This issue is to track support for issuer -> holder flows in this repo.

msporny commented 2 years ago

This issue is to track support for issuer -> holder flows in this repo.

You can perform Issuer -> Holder flows using the Workflow APIs. Keep in mind that a Workflow API can be implemented by any actor in the ecosystem. It's typically implemented by the Issuer (e.g., re-issuance workflow). I can be implemented by the Verifier (e.g., standard verification workflow), or it can be implemented by the Holder (e.g., "I have new VCs for you" workflow).

That said, perhaps I'm not understanding why #254 and #255 don't address this issue; we should discuss in more detail on a VC API call.

OR13 commented 2 years ago

I think #255 handles these questions sufficient to move forward.

It implies that any holder can present to a verifier, any credential, according to a worklow.

for a sufficient definition of "worflow endpoints + request / response types"... this would allow for issuer to present to subject / holders.... eventually.

msporny commented 2 years ago

This issue has been addressed in PR #255 and PR #258, and PR #261.

The text in the current spec now implements what was discussed in this issue:

https://w3c-ccg.github.io/vc-api/#initiate-exchange https://w3c-ccg.github.io/vc-api/#continue-exchange

Closing. Please re-open if anyone feels like we haven't addressed the interactive issuer/re-issue VC API flow concerns that this issue raised.