openid / OpenID4VP

52 stars 18 forks source link

Mechanism for trust frameworks to limit the credentials & claims that a verifier is allowed to request #232

Open jogu opened 3 weeks ago

jogu commented 3 weeks ago

https://github.com/openid/OpenID4VP/issues/182 and https://github.com/openid/OpenID4VP/issues/189 both propose solutions to an underlying problem - that ecosystems / trust frameworks want to have some way to restrict the credentials and claims within those credentials that a verifier can request.

Unfortunately the mechanisms mentioned in both issues are incompatible with the browser API, as they would require the wallet to fetch an external network resource in order to know if it has any credentials that match the request, and (on Android) the wallet must be able to tell the OS/browser what credentials it has that match the request in a sandboxed request matcher without doing any network operations to avoid leaking information about the request to all installed wallets without user consent (or possibly #189 requires comparison of the presentation exchange JSON, which is akin to canonicalisation of JSON and a known interoperability issue).

This issue hence avoids suggesting a full solution, but I observe that for compatibility with the browser API it seems like the solution must take one of two forms:

  1. The full information as to what the verifier is allowed to request is included inline in the request, in a way that the wallet can later verify if it is genuine (e.g. a JWT signed by a third party that contains the query), or:
  2. Some kind of permission language is defined that allows a wallet (once a credential has been selected) to check if a request is permitted for this verifier or not.

I think initially we should focus on whether there are any ways other than '1' or '2' to achieve this, and try to figure out which of those two solutions is preferred.

alenhorvat commented 2 weeks ago

Hi. @jogu I will continue with the discussion here.

Thanks @alenhorvat ! I think what you are suggesting is a variant of my second proposal (essentially a 'permissions' language of some kind) on https://github.com/openid/OpenID4VP/issues/232 except it's basically using some of the properties of presentation exchange as the permissions language, which I think is slightly different from the original proposal above but I'm not 100% clear on the original proposal yet?

Actually, you need both properties 1 and 2. In 1, if my understanding is correct, you define how information is passed from the verifier to the wallet. For this, we tested the 3 options I mentioned

Your point 2, in my understanding, defines that we need a permission language to learn whether the wallet allows to share the VC (if the RP met the requirements); For this, we put the policy in the VC itself (it's a design option we took, works for the cases we have, it has limitations);

@peppelinux , if I understood you well, you proposed that this point is reversed: instead of defining the policies in a VC, policies are defined externally and hosted by the RP. I believe the VC should in any case (regardless of the format, ...) contain information whether or not it is expected from the RP to prove they have the right to request the VC.

I don't think "information is fetched from the .well-known (as described here)" works with the browser API as the information in the well-known is necessary to know whether the wallet has any credentials that match the request, but you can't fetch the well-known until the user has selected a credential?

Correct, hence, we included an option where the information is embedded in the authentication request. We put it in the client metadata. We also need this for cases when wallet might have limited access to the web. I don't know the details of the browser API that's planned, but I'm more than happy to look into it.

jogu commented 2 weeks ago

Thanks @alenhorvat

You can read about the browser API here: https://openid.github.io/OpenID4VP/openid-4-verifiable-presentations-wg-draft.html#name-openid4vp-profile-for-the-w - it's pretty important as it solves issues like cross-device flow security and helping the user experience (rather than having to pick a wallet, the user picks a credential they want to share). However that also imposes limitations (like the one I mentioned of the wallet not being able to access the intranet whilst checking if it has a a credential that matches a request or not).

alenhorvat commented 2 weeks ago

rather than having to pick a wallet, the user picks a credential they want to share

The model I mentioned could address this requirement. (additional information is embedded in the request)

like the one I mentioned of the wallet not being able to access the intranet whilst checking if it has a a credential that matches a request or not

Specification currently states: The signed request allows the Wallet to authenticate the Verifier using a trust framework other than the Web PKI utilized by the browser.

IMO, in this case, the wallet should be able to execute additional logic. Or?

jogu commented 2 weeks ago

IMO, in this case, the wallet should be able to execute additional logic. Or?

The wallet can execute additional logic but only after the user has selected the credential they want to share and the wallet takes control of the UX. So like you say everything needed for figuring out if a credential matches the request or not needs to be embedded in the request. (So in the case of a signed request it is possible that the user can select a credential, then the wallet is launched and is able to verify the signature on the request and it turns out to be invalid, meaning the wallet has to abort the process. But this shouldn't be a common case.)

alenhorvat commented 2 weeks ago

Thank you @jogu . It's clear now.

If we want to make the process fail-proof, additional policy language should be defined and be part of the VC metadata that's shared between the wallet and the browser (I guess the wallet will need to know the type of the VCs user's wallets have + their presentation/sharing policies). I guess it will boil down to the password selector today.

If the browser API WG manages to define/agree upon these policies, it will importantly impact the ecosystems and their trust models. IMO it's an important feature (unlike passwords and passkeys, VCs are not domain-bound and risk of over-asking is real), so looking into evolution of the topic.

jogu commented 2 weeks ago

If the browser API WG manages to define/agree upon these policies,

I think the browser API wg is currently leaving it to the underlying VC protocols (i.e. OID4VP) to define mechanisms like this.

alenhorvat commented 2 weeks ago

This implies that browser will only pass the information to the wallet and will not process it, or?

Is anywhere summarised what metadata the browser would have from the wallet and how that metadata is exchanged? Or the browser simply asks all the registered wallets to respond to a request?

jogu commented 2 weeks ago

You can read about how the browser API implementation on Android currently works in detail here: https://github.com/WICG/digital-credentials/wiki/HOWTO%3A-Try-the-Prototype-API-in-Chrome-Android

The Android implementation is along the lines 'the browser [actually technically an OS component I think] simply asks all the registered wallets to respond to a request', but it does this in a way that the wallets run in a heavily sandboxed process so that doing this doesn't leak the user's request to every wallet (for privacy reasons).

David-Chadwick commented 2 weeks ago

@jogu Your proposal 1. sounds good to me. This is an alternative to the TRAIN method that we implemented. Instead of the wallet going to the trust infrastructure to check that the RP's request is trustworthy, instead you are proposing that this snippet of the trust framework is signed by the root of trust and given back to the RP for it to include in its request to the wallet. Note however that the wallet will still need to make a callout to the trust infrastructure to ensure that the signed snippet has not been revoked since it was issued. Otherwise an RP that once was trustworthy and was given the signed snippet, but which later went rogue and whose snippet was revoked, could still request PII from any wallet that does not check the revocation status of the snippet. Alternatively the RP must go to the trust framework before making a request to the wallet, to obtain a short lived non-revocable snippet to include in its request to the wallet. The wallet then does not need to check the revocation status of the signed snippet.

jogu commented 2 weeks ago

Exactly right @David-Chadwick - thanks.

peppelinux commented 1 week ago

yes mates, the wallet needs to evaluate the trust with the RP, common checks like:

These two previous can be considered for granted despite any technical solution or more or less evolved trust framework. These two previous requires the wallet to update its definitions about a fresh or expired, more or less known RP by fetching remote resources .

this is for RP authentication.

therefore we enter in the authorization realm, considering two implementation strategies:

embedded policies reminds me the experience I had several years ago in the R&E implementation using SAML2 and the so called Entity Categories, in short a unique identifier that demonstrate an RP using a particular framework and therefore requesting a specific set of user attributes, without necessarly make them explicit in the request. There reason why embedded policies reminds me that is because using this approach i na rela world of large scale deployments we therefore will be forced to use RP categories to prevent the need to include in an issued credential the never endinf list of thousands of RP and therefore the requirement to update this list everytime a new RP joins the federation.

therefore embedded policies might add some encoded symbol pointing to some value and requirement to be configured in a trust framework and therefore implemented.

another important concern about the embedded policies is that if something changes in the list, or categories, of the allowed RP therefore this might require the revocation of all the already issued credentials for the update of the "embedded" policies. This might push us in the dark side of the embedded approach that is often used only for some particular, small and not scalable nor flexible approach.

the reason why I believe that using manifests such signed metadata is that:

David-Chadwick commented 1 week ago

Concerning the contents of the signed snippet that the RP sends to the wallet, I would propose the presentation_definition_uri and an integrity protection attribute - see the W3C VCDM relatedResource property (https://www.w3.org/TR/vc-data-model-2.0/#integrity-of-related-resources) since this is both compact, resolvable by the wallet and integrity protected. The full presentation_definition will still need to be in the request so that the browser can determine which credential(s) is being requested without making a callout, but the wallet can check that the browser-selected credential is the same as the one that it would have selected using the signed snippet. In this way there does not need to be any complex comparisons of presentation definitions which @jogu referred to here https://github.com/openid/OpenID4VP/issues/189#issuecomment-2305358039

jogu commented 1 week ago

Running the match again as per @David-Chadwick's suggestion seems like an interesting solution, certainly one I hadn't considered.

It means acknowledging that the verifier can use a different PE in the request than the one that is ultimately used/permitted. I have a niggling feeling this introduced some possible issue, but given the end result is that the verifier should only receive a credential/claims that it is permitted to receive I'm struggling to see what the issue could be.

I think there is a possibility of misleading the user, or we need a very carefully described process for how this works, e.g. if the verifier is authorised to receive both name & DoB from driving licenses, and has a presentation_definition_uri that makes that query, but then the verifier makes a query that requests only the name then this mustn't result in a situation where the user thinks they've agreed to share their name only but then what actually happens is both their name & DoB get shared.

jogu commented 1 week ago

@peppelinux For clarity, could you please clearly state whether you're in favour of option 1, or of option 2, or are suggesting a new third option?

alenhorvat commented 1 week ago

@jogu , can you please provide an example for your proposal 2?

In my understanding, 1 is the way information is passed from the RP to the wallet (which I believe we all agree upon), and 2 refers to how the information is expressed, i.e., as proposed by @David-Chadwick.

David-Chadwick commented 1 week ago

What I am proposing is method 1. The RP sends the full presentation_definition that allows the browser to choose the credentials without any callout, whilst the signed snippet (which is probably a JWT) allows the wallet to verify that the RP is genuinely entitled to receive this PII.

@jogu

e.g. if the wallet is authorised to receive both name & DoB from driving licenses,

I think you meant verifier/RP in this paragraph when you wrote wallet. To answer your implied question, I think the rule that should be enforced by the wallet, is that whatever credential the browser chooses should be a subset of what the wallet determines is authorised by trust framework (via the signed snippet). Otherwise the wallet should reject the request (or defer to the user to ask if they want to release their PII to an unauthorised verifier)

jogu commented 1 week ago

@alenhorvat The difference I see between 1 and 2 is that in 1 we can apply permissions at the matching stage then later verify they are correct permissions.

In 2, permissions are only applied after the user has picked a credential. David's suggestion is an example of 2 I believe, but it uses PE (fetched from a presentation_definition_uri) as what I called the 'permissions language'. (Although from later comments it seems like I've misunderstood what David meant.)

Another example of 2 (provided for clarifying what option 2 is, and I'm not suggesting it's an actual proposal we should take forward) would be defined say a permission language fetched from say https://centraldirectory.usa/.well-known//permissions that contains, say:

{
    "iso_mdl": {
        "given_name",
        "date_of_birth"
    }
}

(please don't nitpick the field names I picked :-) )

Then this would mean that verified with that client_id is permitted to fetch only ISO mdl and is only permitted to request the given_name and/or data_of_birth fields. This structure wouldn't be included in the request but would be fetched and evaluated by the wallet after the credential has been selected by the user.

jogu commented 1 week ago

I think you meant verifier/RP in this paragraph when you wrote wallet.

@David-Chadwick I did, sorry - I've edited my comment to fix that.

alenhorvat commented 1 week ago

Thanks @jogu.

Option 1 is possible if there's an agreement on a common "policy language" and the RP passes the request query + proofs that it can make those requests. (*see below)

The example you showcased (for the 2nd case) could also be embedded in the request (and signed by a root authority responsible for that domain) - this I believe boils down to the suggestions in this discussion; Of course you'd need to put more information in a central registry (I personally would avoid having such a central registry for operational and privacy purposes, but it's up to the use case to decide).

David-Chadwick commented 1 week ago

@jogu I believe your example of 2. would need to be more complex to comply with GDPR as it will need to be per RP service and not per client_id (assuming the RP has the same client_id for the different services that it offers). So an RP which offers, say, a Read DB service and a Write DB service would probably want more PII for the latter than the former.

peppelinux commented 1 week ago

@jogu according to my implementation experience with metadata policy language using openid federation I'd go for the option 2, with the evidence that in my vision option 2 applies on third party attested metadata.

using federation we apply policy upone the RP metadata, matching obviously its client_id

jogu commented 4 days ago

@jogu I believe your example of 2. would need to be more complex to comply with GDPR as it will need to be per RP service and not per client_id (assuming the RP has the same client_id for the different services that it offers). So an RP which offers, say, a Read DB service and a Write DB service would probably want more PII for the latter than the former.

It almost certainly does need to be more complex, though I'm not sure that's a good example, I'm pretty sure we should only attach permissions to client_id and people shouldn't be sharing client_ids between services that have differing requirements (at least that would be the standard OAuth position). GDPR is I believed still satisfied anyway, as the client can still do data minimisation by asking for only some of the fields, or did you mean a different aspect of GDPR?

David-Chadwick commented 3 days ago

The issue is not that the client can do data minimisation, but rather that the trust infrastructure ensures that the client's request does request minimum data. Can we ensure that the RP uses a different client_id for each service?

jogu commented 2 days ago

The issue is not that the client can do data minimisation, but rather that the trust infrastructure ensures that the client's request does request minimum data. Can we ensure that the RP uses a different client_id for each service?

You said it made it more complex to comply with GDPR though. To my knowledge GDPR doesn't require that the trust infrastructure ensures clients request minimum data, it's sufficient (to comply with GDPR) that the GDPR-regulated entity receiving the data is able to request only what it needs. (I'm not saying we shouldn't meet this requirement, just that it's not a requirement that comes from GDPR.)

David-Chadwick commented 2 days ago

Being able to do something, and actually doing it are not the same thing. Probably most RPs can say that they are capable or requesting minimum data, but in reality they may try to collect more than is necessary. The user will not necessarily be able to differentiate between the two, which is where the trust framework steps in. The wallet can ensure that the RP acts properly because the trust framework will say what the minimum data is. If more is asked for the wallet can either refuse the request or ask the user if they want to provide this extra data (i.e. give user consent).

jogu commented 2 days ago

We're in agreement I think. My only comment was that it's not required by law, I agree that it's a sensible requirement to consider.

David-Chadwick commented 2 days ago

Surely asking for more than the minimum is breaking the law? Otherwise why have the law?

jogu commented 2 days ago

The verifier is required by law not to ask for more than they need, they can already comply with the law in OID4VP today.

OID4VP is not required to provide a technical mechanism to allow a third party to try and force the verifier to comply with the law via technical mechanisms. It could choose to do so, but it is not required to do so by GDPR, and the lack of such a measure does not in any way mean that OID4VP cannot be used in countries where GDPR is in force. OAuth and OpenID Connect have no such measures to 'enforce GDPR' and are widely used in countries where GDPR is applicable.

(There may be other, more localised, laws that potentially require extra features.)

David-Chadwick commented 2 days ago

Now I understand what you meant to say. Your statement "it's not required by law" was ambiguous. It could mean, the RP is not required by law (my interpretation) or OpenID4VP is not required by law (your intention). So we do agree now.