claims for age verification

Issue #1172 resolved
Torsten Lodderstedt created an issue

Shall we define claims for privacy preserving age verification (age_over_16 … age_over_21)?

Comments (35)

  1. Daniel Fett

    age_under_X might also be interesting (e.g., for buying products with a discount for young people).

  2. Vladimir Dzhuvinov

    +1 because in the absence of those standard and ready to use claims some ppl may be tempted to choose to release the actual age.

    In a ideal privacy preserving scenario / trust framework the RPs will only be asking for is_eligible_for_X claims, so only metadata about the subject will be released.

    Unfortunately regulations around KYC don’t seem to make use of this possibility. In future this privacy aspect may become a hindrance to applications.

  3. Alberto Pulido Moyano

    Since this could potentially bring a whole new topic, I would like to share our line of thoughts (Santander) around assertions claims of this type. Let us review our documentation and extract the pieces that could be relevant to you for further analysis.

  4. Torsten Lodderstedt reporter

    Since the discussion in the call developed into a „let’s add a query language“ that I support, we need to keep the user experience in mind. For example, expressing an query over a claim is possible, how is the user ask for consent? How is the OP supposed to translate this into human language?

  5. Alberto Pulido Moyano

    Adding on top of my previous comment. In order to preserve the ability to not disclose the users actual data whilst completing the verification process, we have created an assertion language to be used in a special claim in the request object (assertion_claims). This allows the RP to ask for verification of information such as email address/telephone number or verifying that a user is over a certain age.

    This is an example of a request object using assertion_claims:

    {
        "response_type": "code",
        "scope": "openid",
        "nonce": "p4j45uhwx8088",
        "claims": {
          "id_token": {
            "assertion_claims": {
                "email": {
                    "assertion": {
                        "eq": "john.doe@email.com"
                    }
                },
                "birthdate": {
                    "assertion": {
                        "eq": "1998-03-04"
                    }
                },
                "age": {
                    "assertion": {
                        "gt": 18
                    }
                }
            }
          }
        }
    }
    

    The OP will check the claims and return the result without disclosing any personal data in the id_token:

    {
        "sub": "af06cec1daecb383ad2fce124c7c00fdba538f2317920fa1529a4fa034d78d4e",
        "assertion_claims": {
            "email": {
                "result": true
            },
            "birthdate": {
                "result": true
            },
            "age": {
                "result": true
            }
        },
        "nonce": "p4j45uhwx8088",
        "at_hash": "9q9iAIkrWo9zIFfBK4lykw",
        "aud": "TEST-2754efa75e8c4d11a6d7f95b90cd8e40-TEST",
        "exp": 1582215779,
        "iat": 1582215179,
        "iss": "https://op.iamid.io"
    }
    

    In this id_token, after the customer has completed the consenting section, the OP is verifying to the RP that the email and birthdate supplied by the RP, have successfully matched the OP's data, and that the age has been verified to be over 18 years of age.

    If necessary we could also provide details about how the available operations are available via discovery, and what is our approach for complex objects assertions and even the way we propose showing the assertions in the consent screens. Maybe worth considering opening another issue not just related to age claim verification.

  6. Stephane Mouy

    What about expanding the ‘above threshold' concept? I am saying this as I do not see a huge use cases for the ‘above 18’ confirmation, but it would be useful to have a ‘cash or financial assets in excess of…(AMOUNT)’ confirmation.

  7. Torsten Lodderstedt reporter

    We discussed in the call on 26.23, The topic (a query language) is worth a discussion, we will start it after implementers draft 2.

  8. Kosuke Koiwai

    I like the concept of the query language.

    One question, in this scheme, RP will send user's email and birthdate to OP before OP authenticates the user (and get her consent.) Does RP get user consent before making this request?

  9. Alberto Pulido Moyano

    That particular example could perfectly represent an escenario where RP already has email and date of birth as part of their enrolment (using an OP or directly provided to the RP).

    In a subsequent interaction, the user is presented with the option to further validate those details or ask for more details (no need for this assertions). This could be done with a different OP, one that will be presented with a request to assert some of the details the user has provided so far, and ideally with the relevant legal status to assert the details (that’s another topic related with the core of this WG).

    In any case, It makes sense for the RP to have consent from user before starting that second interaction with the OP to assert the details, and of course integrate that as part of the terms and conditions and privacy policy the user must accept.

  10. Daniel Fett

    As I see it, we are deriving new claims from existing claims:

    birthdateage less than x, age greater than y,

    number_of_fingersnumber_of_fingers less than x, (to stay with the example from the call 😉 )

    Therefore, we should introduce a syntax that does just that: Describing how to derive claims depending on other claims.

    This is also interesting because multiple, independent queries can be made on a single claim (e.g., less than x but greater than y).

    We could therefore encode this query in the claim name (similar to language tags) to generate new binary claims.

    "claims": {
      "birthdate < 2000-02-27": null,
      "given_name": null,
      "family_name": null,
    }
    

    … would mean “please return a boolean indicating whether the birthdate is less than 2000-02-27, i.e., whether this person’s age is above 20 or not”.

    User consent screen:

    Data to be provided:
    Your given name (Albert)
    Your family name (Einstein)
    If you were born before 2000-02-27 (yes)

    An answer would be:

    [Response]
    "claims": {
      "birthdate < 2000-02-27": true
      "given_name": "Albert",
      "family_name": "Einstein",
    }
    


    Since we are not introducing a new claim, “age”, but relying on “birthdate”, we can be more precise in the request. For example, for some cases it might be important to say “was born in 1970 or later”. This could be expressed as follows:

    "claims": {
      "birthdate >= 1970-01-01": null,
    }
    

    We could leverage existing OIDC restrictions:

    "claims": {
      "birthdate < 2000-02-27": {
        "value": true
      }
    }
    

    … to express: I’m interested in this value being true.

    This syntax is easily compatible with all existing features of OIDC4IA:

    "claims": {
      "birthdate <= 2002-02-27": {
        "essential": true,
        "purpose": "Verify that you are allowed to drive in this state."
      } 
    }
    


    We can derive multiple claims from the same base claim, for example to express ranges:

    "claims": {
      "birthdate < 2000-02-27": null,
      "birthdate >= 1970-01-01": null
    }
    

    … where the response could be:

    [Response]
    "claims": {
      "birthdate < 2000-02-27": true,
      "birthdate >= 1970-01-01": false
    }
    


    We could also introduce new operators, now or at a later point, which would not necessarily return binary values:

    "claims": {
      "family_name": null,
      "count(nationalities)": null
    }
    

    We would need to be careful with the operators to ensure that a good natural-language representation can be found and presented to the user. I am sure that this can be solved.

    User consent screen:

    Data to be provided:
    The number of your nationalities (2)
    Your family name (Einstein)


    The precise syntax for the operators and values inside the claim names shown here is just a strawman. It could be a strictly limited subset of some other query language, like JSONata, jq, … (strictly limited to facilitate faithful transformation into natural language).

  11. Vladimir Dzhuvinov

    Two notes when we get to this:

    1. Should the query language be mandatory or optional for OPs?
    2. If optional, the OP metadata would need to show it’s supported.

  12. Achim Schlosser

    we would prefer to have

    1. explicit claims for like age_over_21, age_over_18. I think these are global standards that everyone needs.
    2. assess the assertion language seperately with the following caveats noted:

    The assertion language:

    • has the potential to be abused (determining the real birthdate using multiple requests) - I would generally leak information, if you are not very careful

    • Would introduce the need for OPs to implement a dsl parser for the assertion language, which I'm not sure everyone is keen to do. 

  13. Vladimir Dzhuvinov

    Good point Achim. There’s not much use in a DSL if we’ll have to restrain it to what a simple age_over_X claim would do.

  14. Torsten Lodderstedt reporter

    I suggest to retain this ticket for work related to explicit claims.

    I create a new ticket (#1186) to represent the work towards an assertion/expression feature.

  15. Julian White

    Initial proposal for ages:

    13 - lowest legal age of consent in terms of GDPR (also PG-13 film content in USA and other countries)

    15 - Common film, video and game content classification

    16 - General age of consent for many purposes including some aspects of health, driving, film, video and game content classification in a number of jurisdictions

    18 - Common age for being considered an adult (person can act wholly without parental permission)

    21 - Common higher age for access to restricted or licensed goods in some jurisdictions (alcohol, gambling etc)

    25 - An “at least” age used to cover all of the above without needing to be that specific, often used for restricted or licensed goods, e.g. alcohol - they looked at least 25 and therefore unlikely to be caught by one of the other age restrictions

    60 - An age where some people become entitled to various benefits, services or discounts

    65 - Common age where people become entitled to various benefits, services or discounts

    75 - Another age where people become entitled to further benefits, services or discounts

  16. Kai Lehmann

    In Germany, with the age of 17 a person can acquire a drivers license and drive a car when supervised. I propose to have this age added to the list.

  17. Kai Lehmann

    Just a note from my side regarding implementation: Even if the claims will be separate “ager_over_X” claims, I would still implement a parser to extract the number at the end and do a minimum age calculation based on that. I would still have the claims separately approved by the end user of course. So maybe it would not be necessary to actually limit the possible number values for that claim. The flipside of this claim set would be when they are expected to be listed as supported claims in the discovery response.

  18. Alberto Pulido Moyano

    I wonder if an alternative option, instead of having a set of new claims, just create one and let the RP/OP use the core capability to ask for a specific value (unless I am wrong, I believe that’s a possibility I take from here https://openid.net/specs/openid-connect-core-1_0.html#IndividualClaimsRequests ). Here is an example request:

    "claims": {
        "id_token": {
            "age_verification": { "value": "over_18" }
        }
    }
    

    In case the value passed for that claim can be resolved by the OP, it will return the claim with that value. In case it does not resolve the value, the claim could be omitted in the response or maybe even reject the request?? (not sure what the behaviour should be here)

    This is just an option for this particular case (age verification), for other claims where possible values can not be easily established before hand, this does not make sense.

  19. Julian White

    In terms of driving age of 17 we discarded that from our list for two reasons:

    1) you can get the licence at 16, but you are limited to motorcycles under 125cc until you are 17 so for issuance its the over 16 that counts.

    2) we excluded ages where there wasn’t a a number of useful use cases (there aren’t many things in the UK that are over 17 other than the driving a car one) or where the proof of age is redundant because the process would gather the date of birth anyway (e.g. for driving licence application or taking a test they must have your actual date of birth).

  20. Torsten Lodderstedt reporter

    I discussed this ticket with @Brian Campbell (who is also one of the designated experts of the JWT Claims registry). He came up with a proposal similar but a bit different to the proposal @Alberto Pulido Moyano made. I create PR #42 based on that feedback.

  21. Kai Lehmann

    In this case, we will need to add information to the spec on how an OP should handle requests to that same claim but with different values from the same RP. It should not be stored that user gave consent to the at_least_age claim alone, but with the exact value queried for.

  22. Torsten Lodderstedt reporter

    good point - I think the OIDC request syntax would allow two occurrences of the same claim in different containers only, i.e. top level in an ID Token and within one or more verified_claims elements.

    Do you think having multiple occurrences with different values is a real use case? Otherwise, we may decide to prohibit multiple occurrences.

  23. Kai Lehmann

    Actually, this was not my concern. I was referring to the already mentioned privacy concerns that an RP could consecutively query for the same claim with different values to figure out the exact age (year of birth) of an end-user.

  24. Log in to comment