openid / OpenID4VCI

62 stars 18 forks source link

How are credential copies issued? #93

Closed danielfett closed 2 months ago

danielfett commented 10 months ago

_Originally posted by @danielfett in https://github.com/openid/OpenID4VCI/pull/65#discussion_r1372116662_

The relationship currently described is that in any issuance process, multiple "types" of credentials can be issued (*), and for each "type" there can be multiple instances. An instance is described as "contains different claim values or different subset of claims within the claimset identified by the Credential type"

(issuance process) --- 1:n --- (credential definition*) --- 1:m --- (credential instance)

How is the relationship to batch issuance for unlinkability as suggested in SD-JWT? Is this already covered or should there be a separate mechanism for that? I.e., to have something like this for k copies of the same credential:

(issuance process) --- 1:n --- (credential definition*) --- 1:m --- (credential instance) --- 1:k --- (credential instance copy)

(*) entry in the credentials_supported Credential Issuer metadata - I added a comment suggesting to find a good term for this to use instead of credential type.

danielfett commented 10 months ago

Note: The individual credential instance copies would also need different keys.

danielfett commented 10 months ago

I only now noticed that an issue for this exists already: https://github.com/openid/OpenID4VCI/issues/91

paulbastian commented 10 months ago

My opinions on the matter in the sibling-thread: https://github.com/openid/OpenID4VCI/issues/91#issuecomment-1791115185

paulbastian commented 10 months ago

---------DEPRECATED EXAMPLE------------

Token Response

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

  {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ",
    "token_type": "bearer",
    "expires_in": 86400,
    "c_nonce": "tZignsnFbp",
    "c_nonce_expires_in": 86400,
    "credential_identifiers": [ "CivilEngineeringDegree-2023", "ElectricalEngineeringDegree-2023" ]
  }

---------DEPRECATED EXAMPLE------------ Credential Request

POST /credential HTTP/1.1
Host: server.example.com
Content-Type: application/json
Authorization: BEARER czZCaGRSa3F0MzpnWDFmQmF0M2JW

{
  "CivilEngineeringDegree-2023": {
    "proof": [
      {
        "proof_type": "jwt",
        "jwt": "eyJraWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEva2V5cy8xIiwiYWxnIjoiRVMyNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJzNkJoZFJrcXQzIiwiYXVkIjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iLCJpYXQiOjE1MzY5NTk5NTksIm5vbmNlIjoidFppZ25zbkZicCJ9.ewdkIkPV50iOeBUqMXCC_aZKPxgihac0aW9EkL1nOzM"
      },
      {
        "proof_type": "jwt",
        "jwt": "eyJraWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEva2V5cy8xIiwiYWxnIjoiRVMyNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJzNkJoZFJrcXQzIiwiYXVkIjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iLCJpYXQiOjE1MzY5NTk5NTksIm5vbmNlIjoidFppZ25zbkZicCJ9.ewdkIkPV50iOeBUqMXCC_aZKPxgihac0aW9EkL1nOzM"
      }
    ]
  },
  "ElectricalEngineeringDegree-2023": {
    "proof": [
      {
        "proof_type": "jwt",
        "jwt": "eyJraWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEva2V5cy8xIiwiYWxnIjoiRVMyNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJzNkJoZFJrcXQzIiwiYXVkIjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iLCJpYXQiOjE1MzY5NTk5NTksIm5vbmNlIjoidFppZ25zbkZicCJ9.ewdkIkPV50iOeBUqMXCC_aZKPxgihac0aW9EkL1nOzM"
      }
    ]
  }
}

---------DEPRECATED EXAMPLE------------ Credential Response

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
  "credentials": {
    "CivilEngineeringDegree-2023": {
      "credential": [
        "LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L",
        "LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L"
      ]
    },
    "ElectricalEngineeringDegree-2023": {
      "credential": [
        "LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L"
      ]
    }
  }
}
Sakurann commented 10 months ago

a quick note that a proposal that introduces any changes to the token response (like a new top level claim credential_identifier) would not work for implementations like Microsoft's that re-use existing AS and cannot make breaking changes to that AS. In general, this would limit what ASs can implement VCI which I hope we can avoid.

Sakurann commented 10 months ago

To the question... As discussed during DCP WG call, one information that needs to be communicated between the issuer and the wallet is how many copies of credentials need to be issued.

I am not sure the issuer cares which endpoint the wallet uses for batch issuance, when the issuer supports both credential and batch credential endpoint. and i am not sure issuer can force the wallet to use either of the endpoints for batch issuance because there is no guarantee the wallet supports both endpoints. so in reality the issuers that want to do batch issuance will have to support batch issuance on both endpoints. as longs as we keep both endpoints of course. it might be simpler to have one credential endpoint and a flag whether the issuer supports batch issuance at that endpoint or not - i don't know yet.

I would imagine it is the issuer who dictates the number of copies, but I have also seen an implementation in ISO mDL world where the user can choose how many copies (not sure if this is a hard requirements though)

jogu commented 10 months ago

I would imagine it is the issuer who dictates the number of copies, but I have also seen an implementation in ISO mDL world where the user can choose how many copies (not sure if this is a hard requirements though)

I imagine it might be a bit of negotiation. Probably the issuer somehow tells the wallet the maximum number of copies it's willing to issue, and the wallet decides how many to actually request (by supplying the requisite number of holder proofs?). Certainly the issuer shouldn't fully dictate the number of copies as the wallet may hit limits on the number of distinct private keys it can create in the secure enclave, etc.

I firmly believe that (at least for the "average person on the street") the complexities of having multiple copies of credentials should be hidden from the user as much as possible, so if the user has any input at all on the number of copies it should be hidden deep in an "advanced settings" screen that normal users can ignore.

paulbastian commented 9 months ago

most recent ideas for Credential Request

POST /credential HTTP/1.1 Host: server.example.com Content-Type: application/json Authorization: BEARER czZCaGRSa3F0MzpnWDFmQmF0M2JW

{
  "credential_encryption_jwk": "...",
  "credential_response_encryption_alg": "...",
  "credential_response_encryption_enc": "...",
  "credential_requests": [
    //uses credential_identifier returned from the Token Endpoint. issues 2 copies of a credential
    {
      "credential_identifier" : "CivilEngineeringDegree-2023",
      "proofs": [
        {
          "proof_type": "jwt",
          "jwt": "..."
        },
        {
          "proof_type": "jwt",
          "jwt": "..."
        }
      ]
    },
    //issues one copy of a credential
    {
      "format" : "mdoc_mso",
      "proofs": [
        {
          "proof_type": "cwt",
          "jwt": "..."
        }
      ]
    },
    //issues 2 copies of the same credential
    {
      "format" : "sd-jwt+vc",
      "proofs": [
        {
          "proof_type": "jwt",
          "jwt": "..."
        },
        {
          "proof_type": "jwt",
          "jwt": ""
        }
      ]
    }
  ]
}
paulbastian commented 9 months ago

most recent ideas for Credential Response

HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store

{
  "credential_responses": [
    //issuer was able to successfully issue both copies of the credential
    {
      "credentials": [
        "eyJraWQiOiJkaWQ6ZXhhbXBsZTpl...C_aZKPxgihac0aW9EkL1nOzM",
        "eyJraWQiOiJkaWQ6ZXhhbXBsZTpl...C_aZKPxgihac0aW9EkL1nOzM"
      ]
    },
   //issuer wants to defer issuance of this credential
    {
      "transaction_id": "8xLOxBtZp8"
    },
    //issuer was not able to successfully issue both copies of the credential

    {
      "error": "unsupported_credential_format",
      "error_description": "..."
    }
  ],
  "c_nonce": "fGFF7UkhLa",
  "c_nonce_expires_in": 86400
}

note: i removed the REQUIRED format from credential response, I think its unnecessary. Also single credentials are still returned as an error

tlodderstedt commented 9 months ago

most recent ideas for Credential Request

multiple proofs ask for multiple instances of the same credential?

tlodderstedt commented 9 months ago

a quick note that a proposal that introduces any changes to the token response (like a new top level claim credential_identifier) would not work for implementations like Microsoft's that re-use existing AS and cannot make breaking changes to that AS. In general, this would limit what ASs can implement VCI which I hope we can avoid.

I think this is why we came up with the idea back in MV to only issue credential identifiers with authorization details, right?

jogu commented 9 months ago

most recent ideas for Credential Request

multiple proofs ask for multiple instances of the same credential?

Correct.

Sakurann commented 9 months ago

One of the ways forward that minimizes breaking changes would be

It would be important to reach out to the current implementers of a Batch Endpoint. I reached out to an ISO WG working on 23220-3 (profile of VCI to issue mdocs) and does not look like anyone has implemented a Batch Endpoint, yet.

Few remaining questions I have are

tplooker commented 9 months ago

IMO as an alternative perspective on this topic, I've always been of the opinion that the credential endpoint as it stands can be used to issue multiple copies of a credential by simply making multiple requests. This may appear as less efficient in some usecases, but it also keeps implementations simple in the event single credentials are being issued and avoids the significant possible draw backs which come with batch endpoints in general (such as partial failures and maintaining transactional integrity). I would recommend reading my original break down of #8 I still believe a solution that uses the credential endpoint alone but also returning a credential version from each response is the easiest way for a wallet to obtain multiple copies AND refresh copies over time.

tplooker commented 9 months ago

add metadata for how many copies of the credentials the issuer supports issuance

Can you elaborate on the usecase here?

jogu commented 9 months ago

Can you elaborate on the usecase here?

When using an endpoint that allows the wallet to request many copies of a credential at the same time, it's really helpful if the wallet has an idea how many copies the issuer is willing to issue in a single call so the wallet has an idea on the number of proofs to include.

bc-pi commented 9 months ago

@tplooker's alternative perspective that the credential endpoint can be used to issue multiple copies of a credential by simply making multiple requests resonates.

tplooker commented 9 months ago

Can you elaborate on the usecase here?

When using an endpoint that allows the wallet to request many copies of a credential at the same time, it's really helpful if the wallet has an idea how many copies the issuer is willing to issue in a single call so the wallet has an idea on the number of proofs to include.

Thanks I understand that part, I guess I'm trying to understand why an issuer would want to cap this number and communicate this cap to a wallet, for example why does an authorization server not do this with access tokens?

jogu commented 9 months ago

I guess I'm trying to understand why an issuer would want to cap this number and communicate this cap to a wallet, for example why does an authorization server not do this with access tokens?

Basic OAuth2 pretty much hardcodes the number of valid access tokens the client can obtain per authentication at 1 so that's a major difference :-)

I think that this mechanism is trying to avoid generating unnecessary proofs. One proof is needed per credential instance/copy that the wallet wants to obtain, and they're potentially expensive to generate (creating a new key, signing with that key - potentially at a remote HSM), i.e. consume various resources (entropy, time, HSM storage, battery, network transmission). It seems best to me not to generate proofs in the case that the issuer will definitely discard/ignore them.

[It's probably not such a problem if we follow your one http request == issue one credential copy suggestion.]

tplooker commented 9 months ago

Basic OAuth2 pretty much hardcodes the number of valid access tokens the client can obtain per authentication at 1 so that's a major difference :-)

Well it somewhat depends on the grant type as to whether a token request is repeat-able for example 'client_credentals' you can just make repeated requests to get new tokens.

I think that this mechanism is trying to avoid generating unnecessary proofs. One proof is needed per credential instance/copy that the wallet wants to obtain, and they're potentially expensive to generate (creating a new key, signing with that key - potentially at a remote HSM), i.e. consume various resources (entropy, time, HSM storage, battery, network transmission). It seems best to me not to generate proofs in the case that the issuer will definitely discard/ignore them.

That makes sense and personally I dont think we want to limit issuers from applying these sorts of policies to protect themselves I guess Im just wary as to whether we need an interoperable piece of metadata to communicate this constraint yet, feels like complexity we could avoid to begin with until we validate it is actually an issue.

[It's probably not such a problem if we follow your one http request == issue one credential copy suggestion.]

Agreed

jogu commented 9 months ago

@tplooker's alternative perspective that the credential endpoint can be used to issue multiple copies of a credential by simply making multiple requests resonates.

I definitely appreciate the simplicity of that solution.

It is a tradeoff, and I believe there would be a notable difference in the time it takes to issue 10 credential copies in a single API call vs the time to do so by doing 10 API calls (both in terms of round trips and the amount of processing/data retrieval on the backend). I don't have any actual empirical data to back up that believe though. We heard on the call today that at least one real deployed systems do use the "multiple copies in a single API call" but I guess we didn't probe into why they decided the extra complexity was worth it.

tplooker commented 9 months ago

It is a tradeoff, and I believe there would be a notable difference in the time it takes to issue 10 credential copies in a single API call vs the time to do so by doing 10 API calls (both in terms of round trips and the amount of processing/data retrieval on the backend). I don't have any actual empirical data to back up that believe though. We heard on the call today that at least one real deployed systems do use the "multiple copies in a single API call" but I guess we didn't probe into why they decided the extra complexity was worth it.

Agreed there are tradeoffs here but as I said above conversely batch endpoints aren't without issue too, for example what if I requested 10 credentials and the 8th fails to generate? Do I retry generating it, what if the error isn't recoverable? Do I return 8 or an error, if I return an error what do I do with the 8 credentials I generated? Throw them away, revoke them perhaps both?

paulbastian commented 9 months ago

To give the prespective what is drafted in ISO currently, this is the issuer metadata:

{
    "format": "mdoc",
    "doctype": "org.iso.18013.5.1.mDL",
    "policy": {
        "one_time_use": true,
        "batch_size": 10
    },
    "cryptographic_suites_supported": [
        "ES256"
    ],
    "cryptographic_curves_supported": [
        1
    ],
    "claims": {
        "org.iso.18013.5.1": {
            "given_name": {
                "display": [
                    {
                        "name": "Given Name",
                        "locale": "en-US"
                    },
                    {
                        "name": "名前",
                        "locale": "ja-JP"
                    }
                ]
            }
        }
    }
}

Look for the policy claim

jogu commented 9 months ago

Agreed there are tradeoffs here but as I said above conversely batch endpoints aren't without issue too, for example what if I requested 10 credentials and the 8th fails to generate? Do I retry generating it, what if the error isn't recoverable? Do I return 8 or an error, if I return an error what do I do with the 8 credentials I generated? Throw them away, revoke them perhaps both?

Yeah, I think/hope we're in agreement - it's mainly a complexity vs speed tradeoff? (And my understanding of the suggestion is that it would probably be optional for the issuer to support an endpoint that generates more than one credential, and it would be optional for the wallet to request more than one credential, so the complexity only comes into play when both parties have opted into it - although we also can't ignore the difficultly of getting the design of the batch request/response right in the specification.)

To answer your question based on the latest suggested respsonse format (Paul's https://github.com/openid/OpenID4VCI/issues/93#issuecomment-1805495347 ) I think the answer varies depending on if it's 10 different credentials or 10 credential instances.

If 10 different credentials: you can return 7 credentials and 3 errors (or 9 and 1 error, depending on what happens with credentials 9 and 10).

If 10 credential instances/copies: you could just return 7 successfully created instances. (Or I guess you could error out if that was your preference. I can't currently think of a reason you wouldn't return the 7 instances - especially as trying to revoke them might also fail, but I guess there could be one.)

tplooker commented 9 months ago

To give the prespective what is drafted in ISO currently, this is the issuer metadata:

I might be missing some detail here, for example where this policy is described. But in any case it doesn't come across that intuitively to me what the purpose is, for example is this metadata saying wallets MUST only request in batches of 10 or up to? The "one_time_use" flag i assume means a wallet can present a credential only once to an RP, how does an issuer enforce this? Or does it simply rely on the wallet to do so.

It feels too early or a premature optimisation to have metadata like this IMO as its not critical to get interoperability between a wallet and an issuer which is the primary intent of this metadata document.

jogu commented 6 months ago

This was discussed on the EU friendly DCP WG call today.

Participants seemed happy with updating the batch credential endpoint to use the request/response proposals in Paul's comments here: https://github.com/openid/OpenID4VCI/issues/93#issuecomment-1805479854 hence I'll mark this as ready for PR.

The issue of issuer metadata for the maximum number of copies the issuer is willing to issue in a single call was brought up again. It seems sensible to me to have some metadata somewhere that indicates this - without that the wallet may prepare more and send more proofs than can ever be used, which wastes battery / network resource / time / etc.