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

Updated Age Verification proposal aligned with Spring25 #175

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

fernandopradocabrillo
Copy link
Collaborator

What type of PR is this?

Add one of the following kinds:

  • enhancement/feature

What this PR does / why we need it:

This PR include all the changes and suggestions that haven been made these past months regarding Age Verification in PR #50.
It is a clean version with errors and descriptions aligned with the latest version of Commonalities for Spring25 meta release.

Additionally, it contains a proposal to include two new fields in the response: contentLock and parentalControl. Both fields come from the feedback of our partners in the UK.

This PR is ment to replace PR #50 as it seems easier to work on a updated fresh copy.

Please leave any comments or changes you feel appropriate.

Thanks!

@GillesInnov35
Copy link
Collaborator

@fernandopradocabrillo, thanks for this PR and the update of errors section schema (new Commonalities version). I've started the discussion internally at Orange for the new response attributes.

Gilles

@eric-murray
Copy link

I'm happy with this, so approving. A couple of comments for you to think on:

  • Linting is not enabled for this repository, so you might like to run that separately
  • You might like to add some guidance around providing the birthdate information. If an API consumer really knew the date of birth of the customer, they wouldn't need to use this API! So I assume this information is useful only in that it allows us to verify the customer more quickly in case they API consumer knows the correct date of birth. But if it is incorrect, it will be ignored and other parameters used to verify the identity of the customer.

@rartych
Copy link
Collaborator

rartych commented Dec 17, 2024

@fernandopradocabrillo Thanks for this consolidated PR

The latest API Design guidelines add requirements for api-name and using it in file name and servers url .
I would also take the location-verification API as the reference.
Hence I suggest to set api-name=kyc-age-verification then we get:

  • filename: kyc-age-verification.yaml
  • url: "{apiRoot}/kyc-age-verification/vwip"
  • path: (verb)
paths:
 /verify:
  • security:
   - openId:
          - kyc-age-verification:verify
  • operationId - OperationIds are defined in lowerCamelCase
    verifyAge - in line with location verification

  • tags - Title Case
    Age Verification

@fernandopradocabrillo
Copy link
Collaborator Author

I'm happy with this, so approving. A couple of comments for you to think on:

  • Linting is not enabled for this repository, so you might like to run that separately
  • You might like to add some guidance around providing the birthdate information. If an API consumer really knew the date of birth of the customer, they wouldn't need to use this API! So I assume this information is useful only in that it allows us to verify the customer more quickly in case they API consumer knows the correct date of birth. But if it is incorrect, it will be ignored and other parameters used to verify the identity of the customer.

@eric-murray Thank you for the review! I will pass the linter locally, and I'll raise a PR to include it in the repo later (after Holidays probably haha)

Regarding the birthdate, from TEF we agree... It was in the original proposal and I just left it there, but you are right, it doesn't make much sense.

wdyt about the birthdate @GillesInnov35 @ToshiWakayama-KDDI

@fernandopradocabrillo
Copy link
Collaborator Author

@fernandopradocabrillo Thanks for this consolidated PR

The latest API Design guidelines add requirements for api-name and using it in file name and servers url . I would also take the location-verification API as the reference. Hence I suggest to set api-name=kyc-age-verification then we get:

  • filename: kyc-age-verification.yaml
  • url: "{apiRoot}/kyc-age-verification/vwip"
  • path: (verb)
paths:
 /verify:
  • security:
   - openId:
          - kyc-age-verification:verify
  • operationId - OperationIds are defined in lowerCamelCase
    verifyAge - in line with location verification
  • tags - Title Case
    Age Verification

Thanks @rartych, I have applied the changes to align with commonalities guidelines

@HuubAppelboom
Copy link
Collaborator

I'm happy with this, so approving. A couple of comments for you to think on:

  • Linting is not enabled for this repository, so you might like to run that separately
  • You might like to add some guidance around providing the birthdate information. If an API consumer really knew the date of birth of the customer, they wouldn't need to use this API! So I assume this information is useful only in that it allows us to verify the customer more quickly in case they API consumer knows the correct date of birth. But if it is incorrect, it will be ignored and other parameters used to verify the identity of the customer.

@eric-murray Hi Eric, this birthdate information is what the use claims it to be. If it it does not match, you are sure the end user is not the contract owner, and you can not return a reasonable answer. We propose to reply with "not_available" and not provide an identityMatchScore. See also the proposal I made here: #81 (comment)

@fernandopradocabrillo
Copy link
Collaborator Author

Linter issues fixed:
image

@eric-murray
Copy link

@HuubAppelboom

If it it does not match, you are sure the end user is not the contract owner, and you can not return a reasonable answer.

OK. So the API also allows the API consumer to confirm whether the DoB they have been given by the customer is correct.

@HuubAppelboom
Copy link
Collaborator

@eric-murray Yes, the DoB is then used as a first tollgate check. If it matches, you continue to processing the other input parameters that have been provided with which you can calculate the aggregate identity match score.

@ToshiWakayama-KDDI
Copy link
Collaborator

@fernandopradocabrillo Thanks for this consolidated PR
The latest API Design guidelines add requirements for api-name and using it in file name and servers url . I would also take the location-verification API as the reference. Hence I suggest to set api-name=kyc-age-verification then we get:

  • filename: kyc-age-verification.yaml
  • url: "{apiRoot}/kyc-age-verification/vwip"
  • path: (verb)
paths:
 /verify:
  • security:
   - openId:
          - kyc-age-verification:verify
  • operationId - OperationIds are defined in lowerCamelCase
    verifyAge - in line with location verification
  • tags - Title Case
    Age Verification

Thanks @rartych, I have applied the changes to align with commonalities guidelines

Hi @fernando, @rafal, all,

Thank you for intensive review of Age Verification API.

But, from KDDI side, I cannot accept all of this kind of last minute fundamental changes. We have already started implementation work, as this API has been discussed since January and the discussion had become stable before.

From KDDI side, I would like to ask to revert url and path to the previous ones, i.e.

  • url: {apiRoot}/kyc-ageverify/vwip
  • path: /ageverify
  • Consecurtly I would propose to change the filename to: kyc-ageverify.yaml.

I do not think the above is not against commonalities guidelines.

Sorry for the late response due to the time difference, but your understanding would be appreciated.

Best reagrds,
Toshi
KDDI

@ToshiWakayama-KDDI
Copy link
Collaborator

Hi all,

I am checking other changes e.g. error responses, made by European colleagues during my night. Please allow me to take some time and comments.

Best regards,
Toshi
KDDI

@eric-murray
Copy link

@ToshiWakayama-KDDI

But, from KDDI side, I cannot accept all of this kind of last minute fundamental changes. We have already started implementation work, as this API has been discussed since January and the discussion had become stable before.

The registered name for this API in the API Backlog WG is, and always has been, "KYC Age Verification". If you want it to be known as "KYC Age Verify", then you should submit a PR to that WG to get it changed. Even then, the name in the URL should be /kyc-age-verify (i.e. url: {apiRoot}/kyc-age-verify/vwip) following Commonalities rules.

url: https://www.apache.org/licenses/LICENSE-2.0.html

servers:
- url: "{apiRoot}/kyc-ageverify/vwip"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- url: "{apiRoot}/kyc-ageverify/vwip"
- url: "{apiRoot}/kyc-age-verify/vwip"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The full request would look like "{apiRoot}/kyc-age-verify/vwip/verify", but we can decide and change the names in separate PR as it is not related to API functionality.

The guidelines were update with: camaraproject/Commonalities#333

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @eric-murray , @rartych ,

Thanks. Then I will submit a PR to API Backlog WG. If the API name is KYC Ageverify, it would be url "{apiRoot}/kyc-ageverify/vwip". Am I correct?

Thanks,
Toshi

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Then I will submit a PR to API Backlog WG. If the API name is KYC Ageverify, it would be url "{apiRoot}/kyc-ageverify/vwip". Am I correct?

Yes, though expect some debate on calling an API "KYC Ageverify".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As commented in the main conversation, url "{apiRoot}/kyc-age-verification/vwip" is now fine with us/KDDI.

Thanks
Toshi

@GillesInnov35
Copy link
Collaborator

hi @claraserranosolsona, I've discussed internally with my team. Regarding parentControl and contentBlock, they submitted those ideas

  • each country should have possibility to decide/manage if those kind of information should be returned
  • the requester should indicate clearly if information are expected (scope or claims).
    I've scheduled a point this afternoon. I guess I'll have a decision from our side
    Thanks a lot
    BR
    Gilles

operationId: verifyAge
security:
- openId:
- kyc-ageverify:ageverify
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep the \verify in paths then the scope should be:

Suggested change
- kyc-ageverify:ageverify
- kyc-age-verify:verify

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rartych ,

Thanks. I have asked to keep the original path 'ageverify', instead of 'verify'. If url becomes either of kyc-ageverify or kyc-age-verify, 'ageverify' can be used, I understand. Correct?

Thanks,
Toshi

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no clear guideline but in section 3.1 there is such a sentence:

When the POST method is used, the resource in the path must be a verb (e.g. retrieve-location and not location) to differentiate from an actual resource creation.

One can say that the verb is verify, so the kebab case should be used: verify-age or age-verify (but it starts with not a verb)

"{apiRoot}/kyc-ageverify/vwip/ageverify" looks redundant to me, but we can leave it for final review

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rartych , @eric-murray , @fernandopradocabrillo ,

I had one question during my internal discussion regarding this part.

Security:
-openId:
-kyc-ageverify:ageverify

(Currently the scope is kyc-ageverify:ageverify.)

Can we keep the first part 'kyc-ageverify', even though we change the filename to kyc-age-verification.yaml and change the url to {apiRoot}/kyc-age-verification/vwip ? In another wey, should the first part 'kyc-ageverify' be aligned with 'api-name', i.e. kyc-age-verification?

Many thanks,
Toshi

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As commented in the main conversation, "kyc-age-verification:verify" is now fine with us/KDDI.

Thanks,
Toshi

@GillesInnov35
Copy link
Collaborator

@claraserranosolsona, after an discussion at Orange see bellow our quick comments

  • those information are not determinist while the other are determinist (ageCheck, verifiedStatus).
  • if we decide to include those information, they should be returned only if the developer ask for them (request body or claims)
  • some countries will probalbly decide not to return those information. In that case, will the returned value be unavailable ?
    Thanks
    BR
    Gilles

@ToshiWakayama-KDDI
Copy link
Collaborator

Hi @GillesInnov35 , @ToshiWakayama-KDDI , are you happy with the PR? Let us know if you have any additional comment, as I understood the objective would be to close the RC within this week Many thanks in advance Clara

Hi @claraserranosolsona ,

I should comment it is not possible for us to complete checking of these big bunches of changes from the previous version. I am still discussing with my implementation / product teams. Fortunately, I do not think we have to close the RC within this week now, and I think we should extend the target date of the RC.

Regarding the addtional paramters, 'contentLock' and 'parentalControl', as our quick response, we should be fine with them as long as they are optional and they don't add mandatory logic to the API backend. (In addition, I am not sure if they are related to verifying age, but it is fine with me.)

Many thanks,
Toshi

@ToshiWakayama-KDDI
Copy link
Collaborator

Hi @fernandopradocabrillo , @GillesInnov35 , @eric-murray , @rartych , all

I have been working internally on the recently proposed changes to get the Age Verificaiton yaml aligned with the latest Commonalities. We are investigating some work around, in order for us to accept the proposed changes. Now we are on new-year holiday in Japan, so, it is to be confirmed early next week, i.e. on 6th January at the earliest. Please give me some more time.

Let me double-check the proposed changes as below:

  • API-name: KYC Age Verification (decided by API Backlog and GSMA Product WS from the beginning)

-> Based on the API name,

  • filename: kyc-age-verification.yaml

  • url: "{apiRoot}/kyc-age-verification/vwip"

->As 'paths' should be verb,

  • paths:
    /verify:

->based on the above,

  • security:
    -openId:
    -kyc-age-verification:verify

Please let me know if my understanding above is not correct.

Many thanks,
Toshi

@ToshiWakayama-KDDI
Copy link
Collaborator

Hi @fernandopradocabrillo , @GillesInnov35 , @eric-murray , @rartych , all

I am glad to inform you that we/KDDI can accept the proposed changes as written down in my previous comment, above, for Age Verification initial version / Spring25 Meta version.

So, no API name change will be challenged in API Backlog.

I would like to ask Fernando to modify the yaml file accordingly.

Thanks.

@AxelNennker
Copy link
Collaborator

Note: Please remember the OIDF presentation from March 2024 which included Age Verification
https://github.com/camaraproject/KnowYourCustomer/blob/f6c75cf4ed2db6d08287d23dfe468a034f8ebfdd/documentation/MeetingMinutes/MOM-2024-03-19.md#oidf-presentation-re-age-verification

This OIDF specification defines that Advanced Syntax: https://openid.bitbucket.io/ekyc/openid-connect-advanced-syntax-for-claims.html#name-example-age-verification

Interesting side-note: Requests should be signed

Integrity protection and authentication of authentication requests can be achieved in particular by
using Pushed Authorization Requests [RFC9126] to send requests server-to-server with authentication of the RP, or
using JWT-Secured Authorization Requests [RFC9101] to sign the authentication request parameters.

Copy link
Collaborator

@AxelNennker AxelNennker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the additional functions essential for the commercial success? Otherwise they lead to more trouble than they are worth. Keep it simple.

description: |

# Summary
This API provides the customer with the ability to check if the user of the line is older than a provided age, in order to provide API customer's age-restricted services, access to its age-restricted website etc..
Copy link
Collaborator

@AxelNennker AxelNennker Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This API provides the customer with the ability to check if the user of the line is older than a provided age, in order to provide API customer's age-restricted services, access to its age-restricted website etc..
This API provides the customer with the ability to check if the user is older than a provided age, in order to provide API customer's age-restricted services, access to its age-restricted website etc..

Copy link
Collaborator

@HuubAppelboom HuubAppelboom Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the goal is to verify the age of the end user in most use cases, not that of the subscriber (although the subscriber data is used for the verification)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HuubAppelboom I am struggling with the word "line". I amended my suggestion and reintroduced "user" but removed "of the line".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AxelNennker It would be more precise to use the word End-User, as it is defined in the Glossary of Identity and Consent Management

- `POST /ageverify`

Takes the network subscription identifier (e.g. the mobile phone number for a mobile network subscriber) and checks if the age of the subscriber is older than the age threshold in the request.
Additionally, as optional function, provides (1) if age check is done against another form of official identification or not (verifiedStatus), (2) an overall score of how certain is the response using both information provided in the request and information that the Operator holds(identityMatchScore), (3) an indicator on whether the subscription has any kind of content lock (contentLock) and (4) an indicator on whether the subscription has any kind of parental control activated (parentalControl).
Copy link
Collaborator

@AxelNennker AxelNennker Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Additionally, as optional function, provides (1) if age check is done against another form of official identification or not (verifiedStatus), (2) an overall score of how certain is the response using both information provided in the request and information that the Operator holds(identityMatchScore), (3) an indicator on whether the subscription has any kind of content lock (contentLock) and (4) an indicator on whether the subscription has any kind of parental control activated (parentalControl).
Additionally, as optional function, provides (1) if age check is done against another form of official identification or not (verifiedStatus), (2) an overall score of how certain is the response using both information provided in the request and information that the API provider holds(identityMatchScore), (3) an indicator on whether the subscription has any kind of content lock (contentLock) and (4) an indicator on whether the subscription has any kind of parental control activated (parentalControl).

I find these additional functions concerning. Wouldn't it be better to keep age verification simple? parentalControl could be some limit on carrier-billing or whatnot. OfOr the subscriber has a legal custodian who has "parental control". These additional functions will get us into trouble, I am sure.

Also, "official identification" is not well defined. Do you mean government issued? If we go there, we are going to end up defining trust frameworks. Please see the OIDF eKYC-IDA working group documents.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Official Identification can be defined as Identity Document which is legally accepted as a means to verify someone's identity. We don't want to define which trust framework is used for this, but more whether data has been verified or not at all.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AxelNennker ,

I find these additional functions concerning. Wouldn't it be better to keep age verification simple? parentalControl could be some limit on carrier-billing or whatnot. OfOr the subscriber has a legal custodian who has "parental control". These additional functions will get us into trouble, I am sure.

we understand that those 2 information could be very helpful in case of the MNO can't return that the age is greater than 18. But were wonder if those 2 parameters should be requested by th consumer (claims or scope for example).
When you said that it could get us into trouble, what do you mean exactly ?

Thanks
Gilles


If successful, a JSON object is returned containing the following data:
- `ageCheck`: Indicate 'true' when the age of the user is the same age or older than the age threshold (age >= age threshold), and 'false' if not (age < age threshold). Otherwise, 'not_available'.
- `verifiedStatus`: true if the information provided was checked against another form of official identification, otherwise false.
Copy link
Collaborator

@AxelNennker AxelNennker Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- `verifiedStatus`: true if the information provided was checked against another form of official identification, otherwise false.
- `verifiedStatus`: true if the information provided was checked against was collected by the API provider based on a identification document that is legally accepted as an age-verification document, otherwise false.

Please see other comment on "additional functions"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that government issued is not always correct, for example for driver licenses this can be different, like that it is issued by semi-government organisations. Suggest to change it to legally accepted identifcation document. What is legally accepted can also vary per country.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated my suggestion

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can include this, can we rephrase it a bit? I find it a little bit confusing, but I get the idea

Suggested change
- `verifiedStatus`: true if the information provided was checked against another form of official identification, otherwise false.
- `verifiedStatus`: Indicate `true` if the information provided has been compared against information based on an identification document legally accepted as an age verification document, otherwise indicate `false`.

If successful, a JSON object is returned containing the following data:
- `ageCheck`: Indicate 'true' when the age of the user is the same age or older than the age threshold (age >= age threshold), and 'false' if not (age < age threshold). Otherwise, 'not_available'.
- `verifiedStatus`: true if the information provided was checked against another form of official identification, otherwise false.
- `identityMatchScore`: The overall score of identity information available in the Operator, information either provided in the request body comparing it to the one that the MNO holds or directly using internal MNO's information. It is optional for the Operator to return the Identity match score.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- `identityMatchScore`: The overall score of identity information available in the Operator, information either provided in the request body comparing it to the one that the MNO holds or directly using internal MNO's information. It is optional for the Operator to return the Identity match score.
- `identityMatchScore`: The overall score of identity information available in the API provider, information either provided in the request body comparing it to the one that the API provider holds. It is optional for the API provider to return the Identity match score.

Some jurisdictions might require that the API provider publishes the scoring algorithm. This publishing requirement is done to allow checking the score against discrimination e.g. subscribers with foreign sounding names get a lower score. These additional functions are tricky.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MNO can publish the algorithm used, if need be. Note that algorithm may vary depending on the quality of the data the MNO holds, so it is difficult to come with a algorithm which will work in all cases.


- If the authentication token is not valid, a `401 UNAUTHENTICATED` error is returned
- If the API call contains a formatting or other any other syntactic error, a `400 INVALID_ARGUMENT` error is returned.
- If the network subscription cannot be identified from the provided parameters (e.g. the subscription identifier is not associated with any customer of the CSP), a `404 IDENTIFIER_NOT_FOUND` error is returned.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is an "authentication token"? API access token? Isn't that error then 401 Unauthorized? https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401

IDENTIFIER_NOT_FOUND can be used to implement a Telco Finder. If the API consumer has a valid 3-legged access token then the this error cannot happen, right? Is this error for 2-legged tokens only?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is the access token, I'll update the text to reflect this more clear.

Regarding the IDENTIFIER_NOT_FOUND not found error, it is the error accepted in latest Commonalities release. It's common for all APIs that go to meta Spring25.

- If the authentication token is not valid, a `401 UNAUTHENTICATED` error is returned
- If the API call contains a formatting or other any other syntactic error, a `400 INVALID_ARGUMENT` error is returned.
- If the network subscription cannot be identified from the provided parameters (e.g. the subscription identifier is not associated with any customer of the CSP), a `404 IDENTIFIER_NOT_FOUND` error is returned.
- If the API consumer has a valid access token but is not authorised to request an age verification for the specified network subscription, then a `403 PERMISSION_DENIED` error is returned.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can that be? The API consumer should never get a valid access token in this case, right?
At onboarding time of the API consumer includes the scope "kyc-age-verification:verify" or not. And then the access token is created or not.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably for the 2 legged flow, not?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify: my comment is about this item on the list

  • If the API consumer has a valid access token but is not authorised to request an age verification for the specified network subscription, then a 403 PERMISSION_DENIED error is returned.

With 2-legged flow there is a scope in the client credentials token request, right?
https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2

The 2-legged token request with OAuth2 client_credentials might look like in this example, right?:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Bearer eyJhbGciOiJFUzI1NiJ9.ewogICJqdGkiOiJteUpXVElkMDAxIiwKICAic3ViIjoiMzgxNzQ2MjM3NjIiLAogICJpc3MiOiIzODE3NDYyMzc2MiIsCiAgImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDAwMC9hcGkvYXV0aC90b2tlbi9kaXJlY3QvMjQ1MjMxMzgyMDUiLAogICJleHAiOjE1MzYxNjU1NDAsCiAgImlhdCI6MTUzNjEzMjcwOAp9Cg.YB4gdhWUGRjWEsEbKDs7-G2WFH2oYz7bAEP5AtegHXInkY9ncA2V3IoA6O_HVQuFxyCRIklrxsMk32MfNF_ABA
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope="dpv:FraudPreventionAndDetection kyc-age-verification:verify"

The resulting access token is then valid for the scope kyc-age-verification:verify. So, 403 PERMISSION_DENIED cannot happen, I think.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case the end user is excluded/blocked from API services this may come about with a 2 legged flow. You may want to offer this kind of blocking as a service for example for VIPs or other special interest groups, where privacy concerns may prevalate.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Valid" in this context means "issued by the authorisation server, and not expired", but that does not mean it has the required scope. It may be that the API consumer is calling multiple APIs using the same access token with multiple scopes, but forgot to request kyc-age-verification:verify scope. Hence the 403 error should that token be used to access this API.

So certainly this error is possible for API consumers who do not manage their access tokens correctly. It may be that "valid" needs to be clarified in this statement.

description: |
Verify that the age of the subscriber associated with a phone number is equal to or greater than the specified age threshold value.

As it is possible that the person holding the contract and the person using the line may not be the same, the endpoint also admits a list of optional properties to be included in the request to improve the identification. The response may optionally include the `identityMatchScore` property with a value that indicates how certain it is that the information returned relates to the person that the API Client is requesting. To increase the reliability of the information returned, the operator may include in the response the `verifiedStatus` property, indicating whether the identity information in its possession has been verified against an official source.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does including additional data improve the "identification"? What is the threat model?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What needs to be verified is whether the end user is the same person as the subscriber (user) to a certain level of degree of certainty. That is what calculating the identity match score is for

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this scenario: A minor is using their parent's mobile phone to access age-restricted content on the API consumer's website.

The minor enters all the parents attributes into the API consumer webpage.
Variant 1) CIBA
The API consumer sends a signed CIBA request to the API provider. The API Provider sends a message to the subscriber's mobile phone that is in the hands of the minor. The minor enters e.g. a received OTP code into the API consumers webpage and the API consumer retrieves the access token.
Then the API consumer requests age-verification with those parameter:

POST /ageverify
Host: camaraapi.example.com
Authorization: Bearer SplxlOBeZQQYbYS6WxSbIA
Content-Type: application/json

{
  "ageThreshold": 18,
  "idDocument": "66666666q",
  "name": "Federica Sanchez Arjona",
  "givenName": "Federica",
  "familyName": "Sanchez Arjona",
  "middleNames": "Sanchez",
  "familyNameAtBirth": "YYYY",
  "birthdate": "1978-08-22",
  "email": "[email protected]"
}

Sending the attributes in the age-verification process does help in verifying whether the end user (minor) is the same person as the subscriber (user)(parent).

Variant 2) OIDC authorization code flow
The API consumer sends a signed OIDC code request with max_age=0.
Note max_age is mandatory to implement for all OIDC server.
The minor does not know the subscriber credentials and the flow stops. Access to the age-protected contents is prevented.

Security considerations:

  • all requests for code and tokens for age-verification MUST be signed by the API consumer. The API provider MUST validate the signatur.
  • all access tokens SHOULD be sender-constrained e.g. by using DPoP.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AxelNennker Regarding variant 1. If the end user (a minor) is entering all the details of the susbcriber (a parent) with the API Consumer, the Age Verification will of course not work. We assume here that the minor is entering his/her own data, and email address etc. In case the minor is clever and knows about this and enters all the data, the API Consumer can always send a confirmation mail that he is granted access to the service, see what happens....

If you are looking for a age verifcation which is done at a higher level of assurance, you are probably better of with a different product, like for instance an eIDAS wallet.

$ref: '#/components/schemas/VerifyRequestBody'
examples:
Two-Legged Access Token Example:
value:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The content has the content-type application/json but the example value is no JSON.

code/API_definitions/kyc-age-verification.yaml Outdated Show resolved Hide resolved
middleNames: Sanchez
familyNameAtBirth: YYYY
birthdate: '1978-08-22'
email: "[email protected]"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
email: "federicaSanchez.Arjona@gmail.com"
email: "federicaSanchez.Arjona@example.com"

@HuubAppelboom
Copy link
Collaborator

HuubAppelboom commented Jan 7, 2025

Note: Please remember the OIDF presentation from March 2024 which included Age Verification https://github.com/camaraproject/KnowYourCustomer/blob/f6c75cf4ed2db6d08287d23dfe468a034f8ebfdd/documentation/MeetingMinutes/MOM-2024-03-19.md#oidf-presentation-re-age-verification

This OIDF specification defines that Advanced Syntax: https://openid.bitbucket.io/ekyc/openid-connect-advanced-syntax-for-claims.html#name-example-age-verification

Interesting side-note: Requests should be signed

Integrity protection and authentication of authentication requests can be achieved in particular by
using Pushed Authorization Requests [RFC9126] to send requests server-to-server with authentication of the RP, or
using JWT-Secured Authorization Requests [RFC9101] to sign the authentication request parameters.

@AxelNennker If you look at the Advanced Syntax specification, I think this specification is much to broad on the one hand for age verification (you can define a lot more complex use cases) which also has a strong risk in it that it will be used for cases which are not intended. I think we are much better of with a well defined API, including wel defined options, than giving relying parties a toolbox by which can do a lot of things. Furthermore, if you look into the specs, I doubt you can for example also calculate a Jaro-Winkler score with it, so on the other hand the toolbox is also too limited.

@AxelNennker
Copy link
Collaborator

Note: Please remember the OIDF presentation from March 2024 which included Age Verification https://github.com/camaraproject/KnowYourCustomer/blob/f6c75cf4ed2db6d08287d23dfe468a034f8ebfdd/documentation/MeetingMinutes/MOM-2024-03-19.md#oidf-presentation-re-age-verification
This OIDF specification defines that Advanced Syntax: https://openid.bitbucket.io/ekyc/openid-connect-advanced-syntax-for-claims.html#name-example-age-verification
Interesting side-note: Requests should be signed

Integrity protection and authentication of authentication requests can be achieved in particular by
using Pushed Authorization Requests [RFC9126] to send requests server-to-server with authentication of the RP, or
using JWT-Secured Authorization Requests [RFC9101] to sign the authentication request parameters.

@AxelNennker If you look at the Advanced Syntax specification, I think this specification is much to broad on the one hand for age verification (you can define a lot more complex use cases) which also has a strong risk in it that it will be used for cases which are not intended. I think we are much better of with a well defined API, including wel defined options, than giving relying parties a toolbox by which can do a lot of things. Furthermore, if you look into the specs, I doubt you can for example also calculate a Jaro-Winkler score with it, so on the other hand the toolbox is also too limited.

@HuubAppelboom I agree, I just wanted to remind the WG that OIDF is discussing age-verfication for ages. Age verification was already discussed in OpenId 1.0 and early IIWs. Currently most work is done in the field of Verifiable Credentials/Presentations, I think.

@AxelNennker
Copy link
Collaborator

There are examples of law that requires that e.g. in a web forum participants MUST be children, that is UNDER a certain age.

E.g.: COPPA and CIPA

Maybe change ageThreshHold to ageLowerBound and introduce ageUpperBound, or something?

@AxelNennker
Copy link
Collaborator

Should the API provider prevent the API consumer from testing all values from 0 to 100 to find the birth-year of a user?

The API consumer can send many requests for one user and if we are not careful the API provider can find the exact birth-year.

@AxelNennker
Copy link
Collaborator

Is max_age=0 mandatory for age-verification?
I think, that IF we want the age of the End-User to be verified then the End-User MUST authenticate.
Otherwise, all we verify is the age of the subscriber, I think.

@AxelNennker
Copy link
Collaborator

In the past some telcos used an Adult-PIN for End-User authentication. End-Users view the Adult-PIN as anonymous, at least more anonymous than username and password.

As the End-User authentication method in OIDC is decided by the OpenId Provider, the API provider could use an Adult-PIN if the scope is "kyc-age-verification:verify", just a thought.

@AxelNennker
Copy link
Collaborator

What would be the purpose usually associated with this API?
https://w3c.github.io/dpv/2.0/dpv/modules/purposes.html#AgeVerification seems very generic.

If the API consumer is a bank and the legal age for a minor having a bank account is 14, what purpose would the API consumer then send? This is not a problem of this subproject, but somewhere this has to be defined, I think.

@HuubAppelboom
Copy link
Collaborator

There are examples of law that requires that e.g. in a web forum participants MUST be children, that is UNDER a certain age.

E.g.: COPPA and CIPA

Maybe change ageThreshHold to ageLowerBound and introduce ageUpperBound, or something?

I would leave it for now as is. I have not seen any internet service yet where adults are excluded. Usually it is something like an internet filter which excludes content for adults, and you can only be excluded from the filter if you prove you are an adult.

@HuubAppelboom
Copy link
Collaborator

What would be the purpose usually associated with this API? https://w3c.github.io/dpv/2.0/dpv/modules/purposes.html#AgeVerification seems very generic.

If the API consumer is a bank and the legal age for a minor having a bank account is 14, what purpose would the API consumer then send? This is not a problem of this subproject, but somewhere this has to be defined, I think.

@AxelNennker I think you can still ctegorize this under fraud prevention (the use is actually not allowed access to a service, and is committing fraude to ge the access by claiming he is older than 18)

@HuubAppelboom
Copy link
Collaborator

Is max_age=0 mandatory for age-verification? I think, that IF we want the age of the End-User to be verified then the End-User MUST authenticate. Otherwise, all we verify is the age of the subscriber, I think.

@AxelNennker We see a lot of use cases where the API consumer wants to have a bit more assurance than asking whether you are older than 18. And in many cases, the authentication is not necessary. For example for a subscrption on a monthly lottery ticket you need to be older than 18. What happens in that minors do register the tickets on their own name (otherwise you run the risk of not getting paid in case of a big prize), but lie about their age. Authentication is in this case not necessary, just confirming that the name is associated with a phone number and a date of birth which is 18+ is sufficient. The API consumer is always looking for as low as possible friction, and it is best to leave the decision on what authentication is need to the API Consumer.

@HuubAppelboom
Copy link
Collaborator

Should the API provider prevent the API consumer from testing all values from 0 to 100 to find the birth-year of a user?

The API consumer can send many requests for one user and if we are not careful the API provider can find the exact birth-year.

@AxelNennker The same applies to other attributes, like for example housenumber, or first name. Best is probably to monitor for this kind of behaviour, and lock accounts that show abuse or search behaviour.

@fernandopradocabrillo
Copy link
Collaborator Author

fernandopradocabrillo commented Jan 9, 2025

Hi, @GillesInnov35, @ToshiWakayama-KDDI, @HuubAppelboom
As discussed in the last workgroup meeting with @claraserranosolsona, I have updated the proposal including the properties contentLockand parentalControl as optional input flags in the request body to indicate if the corresponding response properties should be returned.
Thanks!

Copy link
Collaborator

@GillesInnov35 GillesInnov35 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @fernandopradocabrillo , concerning contentLock an parentControl, name of flag in the request should not be the same as it is 2 different parameters (a boolean and a string). WDYT
Gilles

- `POST /ageverify`

Takes the network subscription identifier (e.g. the mobile phone number for a mobile network subscriber) and checks if the age of the subscriber is older than the age threshold in the request.
Additionally, as optional function, provides (1) if age check is done against another form of official identification or not (verifiedStatus), (2) an overall score of how certain is the response using both information provided in the request and information that the Operator holds(identityMatchScore), (3) an indicator on whether the subscription has any kind of content lock (contentLock) and (4) an indicator on whether the subscription has any kind of parental control activated (parentalControl).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AxelNennker ,

I find these additional functions concerning. Wouldn't it be better to keep age verification simple? parentalControl could be some limit on carrier-billing or whatnot. OfOr the subscriber has a legal custodian who has "parental control". These additional functions will get us into trouble, I am sure.

we understand that those 2 information could be very helpful in case of the MNO can't return that the age is greater than 18. But were wonder if those 2 parameters should be requested by th consumer (claims or scope for example).
When you said that it could get us into trouble, what do you mean exactly ?

Thanks
Gilles

@HuubAppelboom
Copy link
Collaborator

Is max_age=0 mandatory for age-verification? I think, that IF we want the age of the End-User to be verified then the End-User MUST authenticate. Otherwise, all we verify is the age of the subscriber, I think.

For many cases, legitimate interests can be used, and in this case you don't need to authenticate at all, as long as you keep it a verification product, and the level of assurance that can ben obtained (including the optional identityMatchScore is sufficient for the API consumer.

In case a higher level of assurance regarding age is required or legitimate interest can not be used (depending on the use case), you may need another product, which is more similar to a eIDAS wallet (or at least something of a similar level of assurance).
In that case, and alternative approach to max_age=0 could be to solve it in the consent acquistion process of CAMARA, and make sure that next to acquiring the explict end user consent (with a tick box) you also let the subcriber provide the credentials required. This is however a different product in my opnion, similar to teh difference between KYC Match and KYC Fill-in.

@fernandopradocabrillo
Copy link
Collaborator Author

hi @fernandopradocabrillo , concerning contentLock an parentControl, name of flag in the request should not be the same as it is 2 different parameters (a boolean and a string). WDYT Gilles

I also thought about it yerterday while doing it, but I ended up going with the same names. We can go for something like: includeParentalControl and includeContentLock. Or in the response change to parentalControlOn or parentalControlActive.

I don't have any preference

@GillesInnov35
Copy link
Collaborator

hi @fernandopradocabrillo , thanks a lot for your proposal (includeParentalControl and includeContentLock), it suits me well. The objective is to allow consumer to request those 2 added parameters or not.

All, WDYT ?

Gilles

@fernandopradocabrillo
Copy link
Collaborator Author

Hi @fernandopradocabrillo , @GillesInnov35 , @eric-murray , @rartych , all

I am glad to inform you that we/KDDI can accept the proposed changes as written down in my previous comment, above, for Age Verification initial version / Spring25 Meta version.

So, no API name change will be challenged in API Backlog.

I would like to ask Fernando to modify the yaml file accordingly.

Thanks.

Hi @ToshiWakayama-KDDI, It has been modified accordingly! Thank you very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Spring25 To be included in meta release Spring25
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants