Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can verifiers that support multiple trust models/ecosystems know how to authenticate to the wallet? #248

Open
jogu opened this issue Sep 6, 2024 · 14 comments

Comments

@jogu
Copy link
Collaborator

jogu commented Sep 6, 2024

This is split out from #219 :

For item 3 "what if verifier wants to pass multiple trust models, hoping one is supported by the wallet?"

This is implicitly mentioned in the WG10 ask since the 18013-5 request structure supports multiple RP authentication signatures.

Not having the ability to support multiple RP authentication signatures would mean that it's not possible to request documents that use different trust models, or request documents from issuers (e.g. regions) that have different trust models. Not having those abilities would be a significant limitation of the protocol.

Most of the consequences and possible solutions are similar to the ones mentioned in item 1, since having multiple RP authentication signatures seems to not be supported by the JAR specification.

The proposal addresses this by defining an array of verifier_authentication elements, each of which can contain a signature for a trust model. As the elements that are signed by these structures is dynamic (see item 1), this mechanism supports both the scenario of signing the encryption information, as well as also signing the vp_query.

This doesn't seem to be an mdl specific thing, there is a general problem that a verifier must submit a request to the wallet without knowing anything about the wallet, and, for example, obtaining an mdl regardless of where the mdl was issued/what wallet it is in means:

  1. The verifier may support more than one client_id_scheme and cannot know which one(s) the wallet(s) installed on the user's device support (e.g. for mdl USA states likely require x509_san_dns, some parts of the EU may well use OID Federation instead, and for non-mdl cases there may be even more deviation - this may also mean that the client_ids differ as, for example, there isn't a client_id that is valid for both x509_san_dns and openid federation)
  2. For each client_id_scheme, the verifier may have more than one way to authenticate, e.g. for x509_san_dns they may have a x509 certificate suitable for use in California and a second one suitable for use in the EU.
@jogu
Copy link
Collaborator Author

jogu commented Sep 6, 2024

And to copy across Torsten's comment with one idea for a solution:

This proposal is based on a discussion with @jogu @martijnharing @awoie @danielfett @bc-pi:

We would like to omit canonicalization of the request, that's why the basis of the proposal is to use the multi signature feature provided by the JWS JSON Serialization.

The OID4VP request

{
  "expected_origins": [
    "https://origin1.example.com",
    "https://origin2.example.com"
  ],
  "response_type": "vp_token",
  "response_mode": "w3c_dc_api.jwt",
  "nonce": "n-0S6_WzA2Mj",
  "client_metadata": {
    "vp_formats": {
      "vc+sd-jwt": {
        "sd-jwt_alg_values": [ "PS256" ],
        "kb-jwt_alg_values": [ "PS256" ]
      }
    },
    "jwks": {
      "keys": [
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
          "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
          "use": "enc",
          "kid": "1"
        }
      ]
    }
  },
  "presentation_definition" or "vp_query": {}
}

would not contain client_id and client_id_scheme. It would be put into the payload element of the JSON Serialization structure.

    const credential = await navigator.identity.get({
        digital: {
            providers: [{
                        protocol: "openid4vp",
                        request: {
                          "payload": "eyAiaXNzIjogImh0dHBzOi8...NzY4Mzc4MzYiIF0gfQ",
                          "signatures": [
                           {
                              "protected": "eyJhbGciOiJFUzI1NiJ9",
                              "header": {
                                "client_id": "987647789",
                                "client_id_scheme": "x509_san_dns"
                              },
                              "signature": "PFwem0Ajp2Sag...T2z784h8TQqgTR9tXcif0jw"
                          },
                          {
                              "protected": "eyJhbGciOiJFUzI1NiJ9",
                              "header": {
                                "client_id": "https://rp.federation.eu",
                                "client_id_scheme": "entity_id"
                             },
                            "signature": "irgtXbJGwE2wN4Lc...2TvUodsE0vaC-NXpB9G39cMXZ9A"
                          }
                        ]}

Every "signatures" object has the additional data for a certain RP authentication credential (protected or unprotected).
Underneath every "signatures" object, the "signature" object has the respective signature over "payload" and "protected".
"protected" could contain the response encryption key, so it is signed and authenticated.

Open Questions:

  • Would we adopt this for the traditional OID4VP flow and the DC API?
  • Would we adopt this and still support signed requests for a single client id with JWS Compact Serialization?

@paulbastian
Copy link
Contributor

paulbastian commented Sep 10, 2024

I support this.

Would we adopt this for the traditional OID4VP flow and the DC API?

yes

Would we adopt this and still support signed requests for a single client id with JWS Compact Serialization?

I prefer less code paths

@bc-pi
Copy link
Member

bc-pi commented Sep 12, 2024

Would we adopt this for the traditional OID4VP flow and the DC API?

Only in the context of the advanced request URI POST retrieval mode (sorry I can't remember the terminology).

Would we adopt this and still support signed requests for a single client id with JWS Compact Serialization?

Yes, keep JWS Compact Serialization.

@tplooker
Copy link

tplooker commented Sep 16, 2024

In the sample provided by @jogu above, if the same key pair is used to sign the request in both the "client_id_scheme" = "x509_san_dns" and "client_id_scheme" = "entity_id" we have to sign the request twice and the client technically identifies in two different ways even though it is the same software.

Personally I think this will led to a piece of client software proliferating how they identify with wallets when in actual reality the only thing different is how the client is choosing to authenticate its request toward a wallet.

What I believe is a simpler model is the following.

const credential = await navigator.identity.get({
        digital: {
            providers: [{
                        protocol: "openid4vp",
                        request: {
                          "payload": "eyAiaXNzIjogImh0dHBzOi8...NzY4Mzc4MzYiIF0gfQ", // client_id in here
                          "signatures": [
                          {
                            "protected": "eyJhbGciOiJFUzI1NiJ9", // X5C parameter in here and even a verifier attestation in jwt header if required.
                            "signature": "irgtXbJGwE2wN4Lc...2TvUodsE0vaC-NXpB9G39cMXZ9A"
                          }
                        ]}

Whereby the protected header in this case contains the x5c parameter with an associated certificate chain for authenticating the signed request. The wallet processing the request can then decide how from the options provided it would like to authenticate the client, via resolution of its public keys from its metadata document OR via x509 certificate chain validation.

@jogu
Copy link
Collaborator Author

jogu commented Sep 17, 2024

@tplooker Can you explain how your approach covers scenario 2 above please, i.e. "For each client_id_scheme, the verifier may have more than one way to authenticate, e.g. for x509_san_dns they may have a x509 certificate suitable for use in California and a second one suitable for use in the EU."

@tplooker
Copy link

In the event you are signing the request with different keys or associated certificate chains, then you would use the multiple signatures feature, but the request would be from one client_id identified in the payload.

@jogu
Copy link
Collaborator Author

jogu commented Sep 18, 2024

Right, I see, but then how is the client_id allocated to/selected by the wallet?

Are we saying the wallet can say "My client id is www.example.com but actually I'm going to authenticate to you as preregistered client id you allocated 24t3gerge5y346" or "my client id is www.example.com but actually I use did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5 as my identifer to authenticate"?

@Sakurann
Copy link
Collaborator

for vanilla openid4vp, didn't we solve this with request_uri_method=post? where the wallet can tell its trust framework when obtaining the request object? and for openid4vp browser api profile, there is already an array where you can send multiple requests?

@jogu
Copy link
Collaborator Author

jogu commented Sep 19, 2024

for vanilla openid4vp, didn't we solve this with request_uri_method=post? where the wallet can tell its trust framework when obtaining the request object?

Unfortunately not, there's clauses like this:

The Client Identifier value in the client_id Authorization Request parameter and the Request Object client_id claim value MUST be identical. If the Authorization Request contains a client_id_scheme parameter, the client_id_scheme Authorization Request parameter and the Request Object client_id_scheme claim value MUST be identical. If any of these conditions are not met, the Wallet MUST terminate request processing.

which prevent the verifier changing it's client_id or client_id_scheme at the point where it knows what the wallet would support. Even if we fixed that, it doesn't really deal with a wallet that might support multiple trustframeworks and the verifier has no way to know which of the trustframeworks it might need to use to (say) get the user's driving license.

and for openid4vp browser api profile, there is already an array where you can send multiple requests?

I think it was definitely one of the original proposed approaches, but it was discarded and I'm struggling to remember the reason why. I think there was doubt as to whether browsers will actually support having multiple requests with "protocol": "oid4vp", and there was uneasiness with passing the request multiple times.

@Sakurann
Copy link
Collaborator

Unfortunately not, there's clauses like this:

The Client Identifier value in the client_id Authorization Request parameter and the Request Object client_id claim value MUST be identical. If the Authorization Request contains a client_id_scheme parameter, the client_id_scheme Authorization Request parameter and the Request Object client_id_scheme claim value MUST be identical. If any of these conditions are not met, the Wallet MUST terminate request processing.

which prevent the verifier changing it's client_id or client_id_scheme at the point where it knows what the wallet would support. Even if we fixed that, it doesn't really deal with a wallet that might support multiple trustframeworks and the verifier has no way to know which of the trustframeworks it might need to use to (say) get the user's driving license.

then my suggestion would be to consider change the text you quote to say that there is no need to include client_id in the first request from the verifier to the wallet, only request_uri. then when the wallet sends a POST to the request_uri, it can include the trust framework.

@jogu
Copy link
Collaborator Author

jogu commented Sep 21, 2024

then my suggestion would be to consider change the text you quote to say that there is no need to include client_id in the first request from the verifier to the wallet, only request_uri. then when the wallet sends a POST to the request_uri, it can include the trust framework.

We'd need to do this for the suggestion in this issues to work too.

Currently in request_uri_method=post there's not actually a defined way the wallet can communicate which trust framework(s) it supports. Probably we should actually define something if we want this to work. But following that path would mean the browser API and native oid4vp work in quite different ways, which might not be desirable (i.e. it makes the code more complex for implementors who want to support both).

@tplooker
Copy link

Are we saying the wallet can say "My client id is www.example.com but actually I'm going to authenticate to you as preregistered client id you allocated 24t3gerge5y346" or "my client id is www.example.com but actually I use did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5 as my identifer to authenticate"?

What I was meaning to say is "My client id is www.example.com and you have multiple ways you can trust me, heres how, I have one signature over the same request that tree's into this ecosystem and another into this ecosystem, im still the same software regardless".

@tlodderstedt
Copy link
Collaborator

then my suggestion would be to consider change the text you quote to say that there is no need to include client_id in the first request from the verifier to the wallet, only request_uri. then when the wallet sends a POST to the request_uri, it can include the trust framework.

We kept the client id in the initial request to allow the wallet, if possible, to apply a whitelist to the request_uri parameter. As far as I remember, we wanted to prevent attackers to let the wallet send requests to attacker selected locations.

@TomCJones
Copy link

so, what method does the wallet use to decide if the verifier is trusted sufficiently to display a consent message to the user?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants