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

Proposal for new query language #178

Closed
danielfett opened this issue May 21, 2024 · 11 comments · Fixed by #266
Closed

Proposal for new query language #178

danielfett opened this issue May 21, 2024 · 11 comments · Fixed by #266

Comments

@danielfett
Copy link
Contributor

Related to #160, #161, #162 and #157.

This is a proposal for a new query language fulfilling the requirements discussed in the issues linked above. I can create a PR once we are sure that this is the direction we want to go.

Note: This proposal for now does not cover restricting/querying values of claims. See examples 6 and 7 here for a proposal, but that is out of scope for this issue.

Example 1a - Request one Credential (SD-JWT VC)

Just one credential is requested. It must contain the claims last_name, first_name, and address/street_address.

{
  "credentials": {
    "my_cred_1": {
      "format": "vc+sd-jwt",
      "vc+sd-jwt": {  # format-specific requirements
          "vct": "https://credentials.example.com/identity_credential",
          "alg_values": ["ES256", "ES384"],
          "hash_alg_values": ["SHA-256"]
      },
      "claims":[
        { "path": ["last_name"] },
        {
          "path": ["first_name"],
          "intent_to_retain": false  # might be defined as an add-on, not in this PR
        },
        {  "path": ["address", "street_address"]  }
      ]
    }
  }
}

Note the identifier my_cred_1. It will show up in the VP Token, which is restructured to be an object mapping the identifiers in the request to the presentation (string or object depending on a credential format):

vp_token={
    "my_cred_1": "eY..."
}

Example 1b - Request one Credential (mdoc)

The syntax for the claims is slightly different due to the structure of mdocs:

{
  "credentials": {
  {
     "my_mdoc_credential": {
        "format": "mso_mdoc",
        "mso_mdoc": {  # format-specific requirements
            "doctype": "org.iso.7367.1.mVR",
            "alg_values": [ "EdDSA" ],
            "hash_algorithm_values": [ "SHA-384" ],
        },
        "claims": [
          {
            "namespace": "org.iso.7367.1",
            "claim_name": "vehicle_holder",
            "intent_to_retain": false
          },
          {
            "namespace": "org.iso.18013.5.1",
            "claim_name": "first_name",
            "intent_to_retain": false
          }      
        ]
      }
    }
  }
}

Response:

vp_token={
    "my_mdoc_credential": "<mdoc string>"
}

Example 2 - Request multiple Credentials (all of the requested credentials needs to be returned)

All three credentials have to be delivered:

{
  "credentials": {
    "my_cred_1": {
      "format": "vc+sd-jwt",
      "vc+sd-jwt": {
          "vct": "https://credentials.example.com/identity_credential",
          "alg_values": ["ES256", "ES384"],
          "hash_alg_values": ["SHA-256"]
      },
      "purpose": "give me the credential",  # we might pull this to the top
      "claims":[
        { "path": ["last_name"] }
      ]
    },
    "my_cred_2": {
      "format": "vc+sd-jwt",
      "vc+sd-jwt": {
          "vct": "https://credentials.example.com/identity_credential/2",
          "alg_values": ["ES256", "ES384"],
          "hash_alg_values": ["SHA-256"]
      },
      "claims": [
          { "path": ["addresses"] }
      ]
    },
    "my_cred_3": {
      "format": "vc+sd-jwt",
      "vc+sd-jwt": {
          "vct": "https://credentials.example.com/identity_credential/3",
          "alg_values": ["ES256", "ES384"],
          "hash_alg_values": ["SHA-256"]
      },
      "claims": [
        { "path": ["addresses"] }
      ]
    }
  }
}

All three credentials will show up in the VP Token:

vp_token={
    "my_cred_1": "eY...",
    "my_cred_2": "eY...",
    "my_cred_3": "eY..."
}

Note: If there is a requirement on sending multiple credentials fulfilling one request, this should be turned into an array.

Example 3 - Request alternative claims (one rule)

Require age_over_18 or birth_date or age_birth_year by grouping them in an object with the properties require and from. The order of the claims in the from list implies a preference by the verifier.

{
  "credentials": {
    "my_cred_1": {
      "format": "vc+sd-jwt",
      "vc+sd-jwt": {
          "vct": "https://credentials.example.com/identity_credential",
          "alg_values": ["ES256", "ES384"],
          "hash_alg_values": ["SHA-256"]
      },
      "purpose": "give me the credential",
      "claims":[
        { "path": ["last_name"] },
        {
          "required": 1,
          "from": [
            { "path": ["age_over_18"] },
            { "path": ["birth_date"]  },
            { "path": ["age_birth_year"] }
          ]
        },
      ]
    }
  }
}

Note: the expectation is for the wallet to check which of the alternative claims are present and render a user screen based on that - not for the wallet to display to the user all alternative claims (age_over_18 or birth_date or age_birth_year in this case).

Example 4 - Request alternative claims (multiple rules)

By nesting groups, complex queries can be expressed; here, either "zip_code" or both "city" and "state" are required.

{
  "credentials": {
    "my_cred_1": {
      "format": "vc+sd-jwt",
      "vc+sd-jwt": {
          "vct": "https://credentials.example.com/identity_credential",
          "alg_values": ["ES256", "ES384"],
          "hash_alg_values": ["SHA-256"]
      },
      "purpose": "give me the credential",
      "claims":[
        { "path": ["last_name"] }, 
        { 
          "required": 1, 
          "from": [ 
            { "path": [ "zip_code" ] }, 
            {
              "required": 2,
              "from": [
                { "path": [ "city" ] },
                { "path": [ "state" ] }
              ]
            }
          ] 
        } 
      ]
    }
  }
}

Example 5 - Credentials Alternatives

The same logic to requesting alternative claims can be applied to requesting alternative credentials as well. The "rules" for credentials are separated from the credentials to make the processing easier and the syntax more clean. The following example requests "mycred_1" and either "my_cred_2" or "my_cred_3":

{
  "rules": {
    "required": 2,
    "from": [
      { "ref": "my_cred_1" },
      {
        "required": 1,
        "from": [
          { "ref": "my_cred_2" },
          { "ref": "my_cred_3" }
        ]
      }
    ]
  },
  "credentials": { # each object follows the syntax described above
    "my_cred_1": {...}, 
    "my_cred_2": {...},
    "my_cred_3": {...}
  }
}

As shown in the examples further up, if rules is omitted, all credentials have to be delivered.

If the verifier just wants the user's claim (like street address/age), it would express it as "give me one of these credentials and outline any number of credentials whose schema includes a desired claim" - the verifier still needs to specify the format/type of the credential to indicate what it can understand.

Only the available/selected credential will show up in the VP Token:

vp_token={
    "my_cred_1": "eY..."
    "my_cred_2": "eY..."
}
@tplooker
Copy link
Contributor

tplooker commented May 28, 2024

Appreciate you putting this proposal together @danielfett, as I shared in the hackmd version that preceded this, I had the following feedback.

Supporting algorithm negotiation per credential request I'm still not in favour of, as I have outlined here I'm yet to understand a usecase where this is required rather then handling algorithm negotiation at a wallet metadata level.

The syntax around alternative claims here is quite difficult to understand, for the following reasons

  1. IIUC each claim object becomes polymorphic? e.g describing this as a type in a language like typescript would look like
type ClaimRequest = {
   path: string;
} | {
  required: number;
  from: ClaimRequest[]
}

Which could be difficult to parse in other programming languages that don't support union types.

It also creates situations where the syntax becomes ambiguous to a parser, for example how would I parse the following and what would it mean? Am I asking for last_name OR zip_code OR city and state?

{
  "credentials": {
    "my_cred_1": {
      "format": "vc+sd-jwt",
      "vc+sd-jwt": {
          "vct": "https://credentials.example.com/identity_credential",
          "alg_values": ["ES256", "ES384"],
          "hash_alg_values": ["SHA-256"]
      },
      "purpose": "give me the credential",
      "claims": [
        { 
          "path": ["last_name"] 
          "required": 1, 
          "from": [ 
            { "path": [ "zip_code" ] }, 
            {
              "required": 2,
              "from": [
                { "path": [ "city" ] },
                { "path": [ "state" ] }
              ]
            }
          ] 
        }
      ]
    }
  }
}
  1. I would perhaps question how many usecases exist where the value of required > 1. I understand the example you have provided but how many usecases do we expect that require this feature, because if it is very small, then simpler syntaxes, like what were discussed at IIW can be used which can reduce implementation complexity.

@danielfett
Copy link
Contributor Author

  1. IIUC each claim object becomes polymorphic? e.g describing this as a type in a language like typescript would look like
type ClaimRequest = {
   path: string;
} | {
  required: number;
  from: ClaimRequest[]
}

These would be two different types of objects. If that doesn't map well in the language of choice, it could be one object with three properties that are mutually exclusive.

It also creates situations where the syntax becomes ambiguous to a parser, for example how would I parse the following and what would it mean? Am I asking for last_name OR zip_code OR city and state?

The shown example would be illegal. Either path is provided or (required and from).

2. I would perhaps question how many usecases exist where the value of required > 1. I understand the example you have provided but how many usecases do we expect that require this feature, because if it is very small, then simpler syntaxes, like what were discussed at IIW can be used which can reduce implementation complexity.

The point is that "required > 1" comes essentially for free. It's a generalization of "and" and "or" operators which you would need otherwise. After having iterated through a couple of options, I doubt that the syntax becomes simpler when omitting this feature. It might become simpler when removing the functionality of being able to express "and" and "or" with arbitrary nesting.

@samuelgoto
Copy link
Contributor

I got this answered out of band, so I figured others would have this question too:

In case you are wondering, the list of requirements that this proposal is constrained by is here: #157 (comment)

@samuelgoto
Copy link
Contributor

In case you are wondering too, here is a list of use cases that this proposal is constrained by.

@danielfett
Copy link
Contributor Author

danielfett commented Jun 14, 2024

Note to myself: To cover #193, the vct in the format-specific parameters for SD-JWT should not mean a perfect match, but that a credential of this type or a type inheriting from this should be deliviered. If required, both exact matching and inheritance matching could be supported.

@Sakurann
Copy link
Collaborator

I think what has been coming up as a feedback is whether it should be a different syntax between credential formats depending on how they handle types, which is a current design where mdoc have "doctype" and "namespace" claim, while other credential formats only have "type". I think suggestion has been, not to have something specific to mdoc and try have the same syntax.

@TomCJones
Copy link

I am opposed to any request to the user that does not clearly explain the who the verifier is and what data is required in terms that can be displayed to the user. Here is an alternate proposal. https://docs.google.com/document/d/1keHesvQHrCdbNPavI3imleBm4i9ymXM_/edit?usp=sharing&ouid=109794657323597753486&rtpof=true&sd=true
it includes this link to the format
https://docs.google.com/document/d/1n7HobJ6QTsNld5rn1uuIiNw0A__L44ug/edit?usp=sharing&ouid=109794657323597753486&rtpof=true&sd=true

@jogu
Copy link
Collaborator

jogu commented Jul 9, 2024

Discussed on today's WG call; we seemed to have consensus around moving forwards with a PR that adds text that covers examples 1-5 into the VP spec, that would not be merged until after the ID3 vote is underway for the version with the browser API changes incorporated (although some members did express a desire to move quicker).

@jogu
Copy link
Collaborator

jogu commented Jul 11, 2024

One outstanding question we should get consensus on: what parameter name should we use for passing the new query language to the wallet? Kristina opened this issue for that: #211

@Sakurann
Copy link
Collaborator

Sakurann commented Jul 16, 2024

@marcoscaceres @samuelgoto please put your comments here, if any, otherwise @danielfett will proceed with a PR as-is and we can bikeshed in the PR.

@samuelgoto
Copy link
Contributor

@marcoscaceres @samuelgoto please put your comments here, if any, otherwise @danielfett will proceed with a PR as-is and we can bikeshed in the PR.

The main feedback I've given in the past has been to optimize the syntax for the most common cases, rather than the exceptions. Specifically, I think that it is going to be far more common to request a single credential rather than multiple, so I'd make that case as concise as we can possibly make.

I'll comment directly in the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment