Open dhh1128 opened 4 years ago
@dhh1128 Issued credentials have to flow to holders. I fully agree with that statement, which is why in our Issuer API, the internal system component provides identity attributes to the VC Issuer, and the VC Issuer then transfers the VC to the VC Holder. If any internal component wants to know what VCs have been issued, there is an audit trail for this.
@dhh1128 @David-Chadwick I agree with your comments, but should explain that delivery of the credential to a Holder was considered out-of-scope for the initial version of this API. This API would be only one part of an Issuer system, other parts would then deliver the credential to the Holder, using DIDComm, CHAPI, or some other mechanism.
So when this API returns a credential, the idea wasn't necessarily that the credential would "flow to an internal caller", or that it would be "retained by the Issuer" (see https://github.com/w3c-ccg/vc-issuer-http-api/issues/11). The idea was only that whatever architectural model or protocol or format you are using, at some point the credential has to be created, and that's what this API enables, understanding that this would be only one step of a bigger system.
This API could probably be modified to also incorporate delivery of the credential to the Holder, but as I said the initial proponents felt that this was out of scope.
This comes back to @David-Chadwick's very good point that an architectural model is currently missing (see https://github.com/w3c-ccg/vc-issuer-http-api/issues/9)..
@peacekeeper: I think I may not have made my concern clear. Let me try again.
This set of issuer APIs is motivated by the DHS asking for something common that it can ask all issuing systems to support, so it can switch from one system to another. Its intent is to impose interoperability constraints on the whole industry.
And the specific API at /credential/{ref} is built for internal callers. Therefore it does make the credential "flow to an internal caller"; its assumption is that all issuing systems should expose an endpoint, callable internally, that fetches an issued credential. I get that the expectation is for this internal caller to turn around and give it to the holder--not necessarily to retain what it receives. I still claim that this is a bad idea, from a security perspective; we are guaranteeing that issuing systems have a vulnerable place in their workflow where credentials can be siphoned off before they reach their holder. I can think of mischief that could take advantage of that. Evernym's issuance system design does not currently support giving a credential to an internal caller first (or ever); the only path for issuance is to send it directly to the holder during an outward-facing protocol. Internally, the data for a credential is assembled, the signature is assembled, and then the two are brought together and shipped only outward. There is no "call an internal API to get the credential, and then turn around and give it to the holder."
Now, I don't think the behavior of giving the cred to an internal caller who then gives it outward is that big of a vulnerability. I just think it's suboptimal practice, and we shouldn't build an API that, in the name of interop, precludes better practice. I don't want DHS to tell me it doesn't like Evernym's software because it doesn't support GET /credential/{ref} ("Why don't you support this? Everybody else does. You're not compliant.") My concern would go away if we said that when supporting the issuer APIs as a whole, the specific API at GET /credential/{ref} is optional for good reason; what's required in this set of API is the status call and so forth. But right now, GETting /credential/{ref} appears to be the only API whereby issuance is imagined to happen, and the most important call in the entire issuance API set.
@dhh1128 Where is an issuer's private signing key kept when using Evernym? If it is held by the issuer, wouldn't the signed credential originate from the issuer, not from Evernym, and so the signed credential would be sent directly from the issuer to the holder? Apologies if I'm asking something silly - even so, could you point me at a description of Evernym's flow?
@jchartrand That is a very deep question. It's not something I can explain in a paragraph; it would take more like a page or two, and we're not prepared to discuss all the internals publicly yet. That will change soon, I think. So let me put a bookmark in your question temporarily, and hopefully I can come back to it with better info later.
For now, I'll just say that it's important to think about two different deployment models for issuing systems: SaaS and on-prem. In the on-prem model, which we believe is easier to secure, there is no "Evernym" as a separate entity from the issuer. The issuer is running software from Evernym, and it holds keys. But it's software under the issuer's control. The whole workflow is owned by the issuer. Evernym isn't part of the process anywhere. In the SaaS model, it gets trickier...
Fair enough - looking forward to hearing more about it!
@dhh1128
Therefore it does make the credential "flow to an internal caller"
Internally, the data for a credential is assembled, the signature is assembled, and then the two are brought together and shipped only outward
I don't think the difference is that big. In the first case (Issuer API), the created credential is created and returned via an HTTP API call. In the second case (Evernym), the credential is still created somehow via a local SDK function call, perhaps evernymVcxCreateCredential()
or similar. I guess the difference is that in the latter case, only Evernym software would "see" the created credential, deliver it, and forget it. In the Issuer API case, the created credential may also be "seen" by other parts of an Issuer's infrastructure. I agree this could be a weakness if the different components are not integrated in a secure way. But it could also add flexibility, e.g. perhaps the Issuer wants to do different things with the created credential, or support different mechanisms for delivering it?
right now, GETting /credential/{ref} appears to be the only API whereby issuance is imagined to happen
No, if you read the description of this API call, you will see that it is optional. The "main" API call (the only one that is not optional) is POSTing to /credential, which returns the issued credential in the response body. We could easily amend that to say that this call will either return the credential in the response body, or deliver it directly to the Holder without returning it to the caller. I think this would make a lot of sense, and perhaps address your concerns?
I actually thought about this possibility when drafting the API, the only reason why it's not there is because - as I said - the group felt that initially the delivery step should be out of scope, but I'd be more than happy to work with you on a PR to change that.
But it could also add flexibility, e.g. perhaps the Issuer wants to do different things with the created credential, or support different mechanisms for delivering it?
Right. I'm not arguing that it's bad to have such a feature; I was just uncomfortable if we were relying on it too heavily, such that it became a strong requirement to support it.
We could easily amend that to say that this call will either return the credential in the response body, or deliver it directly to the Holder without returning it to the caller. I think this would make a lot of sense, and perhaps address your concerns?
Yes, that would make me more comfortable. Thanks for reminding me that POSTing to /credential was the expected flow; I had seen that before but forgotten.
We could easily amend that to say that this call will either return the credential in the response body, or deliver it directly to the Holder without returning it to the caller. I think this would make a lot of sense, and perhaps address your concerns?
Yes, that would make me more comfortable. Thanks for reminding me that POSTing to /credential was the expected flow; I had seen that before but forgotten.
@dhh1128 - do you have input on how this API can support the direct delivery to the Holder? ie, is it a parameter called "holdercallback" and then an agent endpoint, or is it something that would come from the subject's DID? Just looking for ideas on how to support.
@dhh1128 "Evernym's issuance system design does not currently support giving a credential to an internal caller first (or ever); the only path for issuance is to send it directly to the holder during an outward-facing protocol." This is exactly the same design as our system, so Evernym is not alone in this approach.
do you have input on how this API can support the direct delivery to the Holder? ie, is it a parameter called "holdercallback" and then an agent endpoint, or is it something that would come from the subject's DID? Just looking for ideas on how to support.
Since the holder has a DID that must be known to the issuer, it should always be possible to communicate with the holder using an endpoint in their DID doc. We are not planning to use a holder callback because we don't want to require the holder to be running a web server.
@dhh1128
To answer the question in the issue title:
Why do we believe it's a good idea to return an issued credential to an internal caller?
Separation of concerns.
Consider the case of Azure Key Vault with Hardware backed keys. Your access to this system is via an internal API. Your credential issuer software calls this api with the data that needs to be signed to create a VC, and constructs the finished credential. Same thing is true of WebKMS or Ursa Enclave... both are internal apis, which help provide cryptographic operations... the resulting credential formation and transmission to the holder might happen a number of different ways.
Once your software has the finalized credential, it still needs to transmit it to the holder.
As noted above that layer was considered out of scope.
As you noted, you could transmit the credential to the holder using DIDComm and the service endpoint in their did document, or via CHAPI if they were present on a web page.
Somewhat related WebKMS issue: https://github.com/w3c-ccg/webkms/issues/4
@peacekeeper
Does this API (and specifically POST /credential) assume that the private signing key is always stored with (or controlled by) the issuing 'authority', e.g., with Everynm?
The credential is never signed locally by the issuer (on-premises) with this POST?
I don't think this interface precludes calling out to other internal or public apis...
For example, the issuer API might validate the user input, and then call a web wallet on a mobile device, where the credential would be actually created, and then forwarded back.
In such a theoretical scenario:
UPort App -> Website -> External Issuer API -> Internal Issuer API -> Evernym App... (responses fulfilled using various transports, HTTP, CHAPI, DIDComm, webrtc, etc...)
The issuer could be a mobile wallet based DID... replacing Evernym App
with UPort App
or Ursa Enclave Cloud Agent, or whatever would leave the downstream side intact.
Its worth noting that adoption of a suitable bridge between mobile wallets / web wallets and http apis like this api is a major missing piece of standardized infrastructure (CHAPI / DIDComm)... so solving the issuer http api side of this makes a lot of sense...
That bridge might becomes less necessary as DIDComm / CHAPI or pure p2p use cases mature over time, but I think it's likely that centralized control over issuance of credentials and use of http will be useful while we wait for that... especially if hardware backed key management is a part of the issuance process.
I agree with @OR13 that the API doesn't preclude calling out to other APIs. However, the sequence of apps that Orie proposed does uncover an important consideration about ordering and connection. It is this:
When does the state for a credential issuance process (including the credential's identifier, the subject's DID, the subject's preferences about credential format, payment status, etc) get created?
The assumption of this API, so far, I think, is that it gets created when you POST to /credential. Before that, there's not any recognition that the same workflow is underway, so the parts to the left of -> Internal Issuer API are considered independent. Hardly any state can transfer forward.
I agree that we should support working this way. But I think we must also support working in a way where the workflow is recognized as a single organic whole that started earlier than the -> Internal Issuer API call. A simple way to do this would be to let the POST to /credential supply state (a credential ID, the DID of the holder, and other state that has already accumulated). Right now we can supply a credential template, but that's supposed to be only repeatable stuff, not a unique identifier. And we can supply a subject reference, but that assumes we're just looking up the subject in an internal database; if Alice the holder has 2 DIDs, or 0 DIDs, in our database, then subject reference won't transfer state correctly.
interesting! sounds like you are suggesting an alternative where the client creates the VC identifier and the issuer just checks to see that it is not taken and creates the credential... or in a more complex scenario, the client might submit a signed credential which would by signed again by the issuer (Proof Set or Proof Chain)...
I feel like we are uncovering an assumption about the state of the client which is worth exploring... How much control over the credential should be the responsibility of the issuer vs the software system that posts against the endpoint (might be the subject / holder, or an agent, or some other software system).
I think it might be worth sketching a data model for the post that leaned the other way, towards the subject / holder / agent.... and their autonomy.... in such a case, the issuer is essentially just verifying and co-signing... the subject / holder would need all the information to make the request correctly... which they might not actually be allowed to have access too. Feels like we might need an exemplar use case here.
That perspective (client creates the VC identifier) is one view to take on what I suggested, and I would be supportive of exploring it. But that's not really what motivated my comment.
In the issuance protocol described in Aries RFC 0036, the protocol doesn't necessarily start with the issuer; it could start with the holder. This is the case where Alice shows up at the Driver's License office and says, "Hey, I lost my driver's license. Can you issue me a new one?" Now, we can certainly analyze the interaction in two parts: part 1 is everything leading up to a POST to /credential, and part 2 is everything from the POST onward. But that creates an artificial boundary that might not always map onto how software gets built. What I'm believing is that in some cases, something in the issuer's own system might already know that an issuance is underway, before the POST to /credential, and might want to pass that state on. I hadn't really thought about the client supplying the state.
i will try to formulate something
We discussed this on the 2021-08-03 call and @dmitrizagidulin and @bumblefudge agreed to write up the discussion in the group in this issue and then see if @dhh1128 agrees with the path forward.
Status on this, @dmitrizagidulin and @bumblefudge?
We discussed this on the 2022-03-29 call. @dmitrizagidulin summarized that the issue is about internal vs. external API -- who is going to use this? Back-end only APIs, Wallet APIs? Microcosm of entire spec, no actionable item yet. Conversation has progressed since initial API. Protect every endpoint with authz, shifts backend vs. public conversation a bit. @dlongley said we could close issue by saying "internal caller might be entity that passes VC to holder". @dmitrizagidulin said that's how DCC has VC API implemented. Wallet requests credential from Issuer, Issuer calles this API endpoint, then returns it back to wallet. @TallTed said APIs are designed as "external", but that's a deployment decision, everything should be designed as if it was "external". @mprorock generally two practices -- return status code 200/201 -- everything is fine, or error code w/ message. Reason for status code is delayed operation... if it's not error return, return object that was created, you might do something immediately (such as put in a wallet).
Summary: The VC API is a collection of HTTP APIs that can be implemented by systems that would like to perform the role of issuer, verifier, or holder. The APIs are designed such that all of them do authz of one kind or another, either directly on the endpoint (via OAuth2 (at least 4 implementations) or ZCAPS (at least 1 implementation)). Certain APIs are expected to be deployed "behind the firewall" while others can be deployed "at the firewall". All APIs are designed such that they could all be placed "at the firewall", but current deployment has endpoints such as credential issuance, listing, and retrieval as "behind the firewall", and things like presentation exchange "at the firewall".
@dhh1128 -- the group discussed this today and feels like it answered the question with some of the above (minutes forthcoming). Please let us know if you're ok with this answer. We plan to close this issue in 7 days if we don't hear back from you by then.
@msporny I see this is marked as pending closed, does that mean the decision is to treat the VC API as an internal style API in the context of flows like the DCC usecase? E.g the endpoint definition that the wallet interacts with in that case is out of scope for this specification?
@tplooker wrote:
@msporny I see this is marked as pending closed, does that mean the decision is to treat the VC API as an internal style API in the context of flows like the DCC usecase?
I added a "Summary" above as the notes from the call were pretty terrible.
In general, the VC API covers every action that you need for basic interaction within the Issuer/Holder/Verifier ecosystem. It covers all APIs necessary to issue a VC from an Issuer -> Holder. It covers all APIs necessary for a Holder to manage their VCs. It covers all APIs necessary for a presentation between a Holder -> Verifier. Some of these APIs, like /credentials/issue, which is typically authz'd by OAuth2 or ZCAPs and exposed on the Issuer Service (an "internal" system component), is expected to be called by the Issuer App (an "external" system component, exposed to the Internet). However, some system configurations, like SaaS, might expose /credentials/issue directly via the Issuer App and still do authz via OAuth2 or ZCAPs.
E.g the endpoint definition that the wallet interacts with in that case is out of scope for this specification?
It depends on what part of the wallet we're talking about. I know that the Traceability folks are using the VC API as their "wallet interface" of sorts. I know that Digital Bazaar is using the exchanges endpoints as the mechanism to move VPs from Issuer -> Holder or from Holder -> Verifier when CHAPI isn't utilized (like when doing credential refresh). Digital Bazaar, however, is using EDVs as the interface that the Holder uses to store/retrieve their VCs and is then using VC API as the mechanism to create presentations from those VCs (we aren't using the VC API credential/presentation storage/retrieval endpoints to do that like the Traceability folks are doing).
Does that answer your question?
Does that answer your question?
Sort of, bear with me :).
I still don't seem to see how an e2e flow would work in the case of say issuing a personal identity credential (e.g DL or PRC). To me this flow involves three entities, the wallet, issuer and End-User. Typically unless you are assuming the wallet is handed some token of authorization that identifies who the End-User is and proves the wallet has the authority to request the credential from the issuer, then this has to be done within the protocol flow. I understand that designing of authorization protocols is out of scope for this spec and that its relationship is optional, but how do they relate when they need to?
Say you are using plain OAuth2 or OIDC to do user auth and issue an access_token, does the wallet include this somehow in a request to the interact API? Does that make the VC API a resource server of the IDP? What scopes need to be granted to get this access_token or does it not matter?
Maybe im missing the point, I'm just struggling to see how the protocol flow works e2e for that case?
I still don't seem to see how an e2e flow would work in the case of say issuing a personal identity credential (e.g DL or PRC).
This is one of those discussions that we haven't been able to get to the heart of (yet). At the center of the discussion is the question of how an Issuer/Verifier ensures the proper level of authn/authz before handing a VC over to a Holder.
One approach is to place some level of trust by the Issuer/Verifier in the IdP/Holder software authz mechanism. The OAuth2/OIDC approach is where you register a software client with the Issuer/Verifier. I'll note that many of the "internal" VC API endpoints use OAuth2 (or ZCAPs) for software client authz.
Another approach is to state there there should not be trust in the IdP/Holder software authz mechanism for two reasons 1) because it doesn't protect against the types of attacks that matter, and 2) it has negative effects on market competition in the ecosystem. The former could be argued to be more important than the latter.
Rather than have that discussion in this issue, which is related (but tangentially), I've raised a new issue (#279) to explore why the /exchanges endpoint does authn/authz of the Holder in the message exchange rather than solely depending on OAuth2 on the API endpoint to perform the authz.
The group discussed this on the 2023-04-11 VC API telecon:
@jandrieu noted that we have had a lot of discussion around the difference between an "Issuer Coordinator" and "Issuer Service" and have moved away from the language related to "internal vs. external". We have also ensured that implementation of any of these mechanisms is optional and have provided one way in which interoperability can be achieved (e.g. implement the "issuer service API" or implement the "issuer coordinator API"). It was also noted that we don't have the concept of a "wallet" and those boundaries are less well defined... wrt. OAuth2 -- we haven't talked about how OAuth2 and OIDC are supported, except that exchanges have been demonstrated to support that.
If we were to document how VC API works w/ OAuth2 or OID4, then we might address the question from @tplooker.
@dlongley noted wrt. making OID4 work w/ exchanges, Digital Bazaar has already implemented that, perhaps this is a matter of documenting how OID4 can be implemented with exchanges and that would allow us to close this issue out.
@jandrieu noted that a PR for this might require implementation guidance... if you want to integrate OID4 w/ VC API Exchanges, a section on how you do that would answer Tobias' question in terms of flow. We might need Exchangers to be integrated, might be in an example of how you use exchanges. An exchanger is probably a first class component that has its own endpoints and we haven't worked through that stuff yet.
The next step here is to raise a PR that details how OID4 can be integrated w/ VC API Exchanges.
Issued credentials have to flow to holders. This API requires issuers to also support flowing them to an internal caller for some reason. I am not aware of any architectural reason that's rooted in use cases, why this should be so. The protocol for issuance that's imagined in Aries RFC 0036 does not preclude sending an issued credential to an internal destination, but neither does it require that.