-
assigned issue to
[SAO] Output claim set varies depending on evaluation order
The current specification of “Selective Abort/Omit” (SAO) does not prevent the output claim set from varying depending on evaluation order.
Example request and data:
{
"id_token": {
"verified_claims": {
"verification": null,
"claims": {
"given_name": {
"if_different": "omit_verified_claims",
"value": "John"
},
"family_name": {
"if_different": "omit_set",
"value": "Smith"
}
}
},
"email": {
"if_unavailable": "omit_set"
}
}
}
claim | actual value |
---|---|
verified_claims / given_name | Max |
verified_claims / family_name | Meier |
max@example.com |
Case 1:
If "if_different"
of "given_name"
is evaluated before "if_different"
of "family_name"
,
- The whole
"verified_claims"
is omitted. - The value of
"family_name"
becomes unavailable. - The action for
"if_different"
of"family_name"
,"omit_set"
, is not triggered.
As a result, "email":"max@example.com"
is included in the output claim set.
Case 2:
If "if_different"
of "family_name"
is evaluated before "if_different"
of "given_name"
,
- The
"omit_set"
action is triggered. - The value of
"email"
becomes unavailable.
As a result, the "email"
claim is not included in the output claim.
Comment from an implementer
I spent some weeks considering how to implement SAO, but I’ve not reached a conclusion yet. The difficult part of the specification is in that "if_different"
may trigger omission of other nodes and even itself (not only by "omit_set"
and "omit_verified_claims"
but also by "omit"
which may recursively omit upper nodes) and the omission may invalidate "if_different"
of other nodes and even itself. Therefore, the evaluation order of "if_different"
matters and may generate different claim sets.
I’m afraid that the current specification of SAO cannot be implemented in a deterministic way. Different implementations would generate different claim sets.
Comments (25)
-
-
Would this evalutation order fix things?
-
evaluate all
if_unavailable
items, within those- evaluate all
omit
- evaluate all
omit_set
- evaluate all
omit_verified_claims
- evaluate all
-
evaluate all
if_different
items, within those same order as above - finally, evaluate all items that became unavailable and are marked with
if_unavailable
.
-
-
reporter I will investigate whether the evaluation order works, but it will take time due to other higher-priority tasks. Sorry.
-
reporter I’ve started implementing SAO. It will take a couple of weeks for me to be able to confirm that the evaluation order works (or at least it can be implemented). If other problems are found while implementing the specification, I’ll report them.
-
reporter Example verified_claims request:
{ "id_token": { "verified_claims": { "verification": { "trust_framework": null, "assurance_level": { "if_unavailable":"omit_verified_claims", "if_different":"omit_set", "value":"example_assurance_level" } }, "claims": { "family_name": { "if_unavailable":"omit_set", "if_different":"omit", "value":"nonexistent_family_name" }, "given_name": { "if_unavailable":"abort" } } } } }
Example data:
property value assurance_level
"example_assurance_level"
family_name
"example_family_name"
given_name
"example_given_name"
Steps:
1 “if_unavailable":"omit”
Nothing is done 2 “if_unavailable":"omit_set”
Nothing is done 3 "if_unavailable":"omit_verified_claims"
Nothing is done 4 "if_different":"omit"
“if_different":"omit"
offamily_name
is triggered. As a result,family_name
is omitted. This will later trigger"if_unavailable":"omit_set”
offamily_name
.5 "if_different":"omit_set"
Nothing is done 6 "if_different":"omit_verified_claims"
Nothing is done 7 Evaluate all items that became unavailable and are marked with "if_unavailable"
."if_unavailable":"omit_set"
offamily_name
is executed. As a result,"if_different":"omit_set"
ofassurance_level
is triggered and executed. As a result,assurance_level
is omitted. This will later (not at this timing) trigger"if_unavailable":"omit_verified_claims"
ofassurance_level
if the same processing flow (“evaluateif_different
then laterif_unavailable
”) should apply.8 "if_unavailable":"omit"
(2nd)Nothing is done. 9 "if_unavailable":"omit_set"
(2nd)Nothing is done. 10 "if_unavailable":"omit_verified_claims”
(2nd)"if_unavailable":"omit_verified_claims"
ofassurance_level
is executed. As a result, the wholeverified_claims
is omitted. This will later trigger"if_unavailable":"abort"
ofgiven_name
.11 "if_unavailable":"abort"
"if_unavailable":"abort"
ofgiven_name
is executed. As a result, the whole request is aborted. (Here it is assumed that"abort"
actions should be evaluated at the latest step.)
In the example above,
"if_unavalable"
actions need to be rechecked after the step 7 again. This implies that the evaluation rule needs to be defined in a recursive manner, for example by using “until …” and “mark ‘processed’ and continue” (like garbage collection algorithms). Or a different syntax for SAO may have to be devised.In addition, although the example above does not include the following pattern, it should be noted that it is theoretically possible for mandatory claims/nodes to trigger
"if_unavailable"
actions of upper nodes recursively.{ "optional_node": { "mandatory_node": { "mandatory_claim": { "if_different":"omit", "value":"impossible_value" }, "if_unavailable":"omit_set" } }, "optional_claim": { "if_unavailable":"abort", "if_different":"omit_set", "value":"possible_value" } }
I now feel that it might be better to devise another different syntax for SAO. In particular, it might be better to let RPs specify evaluation order like below. (The concern described in the Privacy Considerations section is a different matter from SAO syntax.)
{ "sao": { "omit_set": [ "/verified_claims/verification/assurance_level", "/verified_claims/claims/family_name" ], "evaluation_order": [ [ "omit", "if_different", "/verified_claims/claims/family_name" ], [ "omit_set", "if_unavailable", "/verified_claims/claims/family_name" ], [ "omit_verified_claims", "if_unavailable", "/verified_claims/verification/assurance_level" ], [ "abort", "if_unavailable", "/verified_claims/claims/given_name" ] ] } }
The above syntax is not necessarily my recommendation. It is just an example of other syntax for brainstorming.
-
Mark and I discussed this, and we thought to make the following changes to SAO:
omit_verified_claims
is removed. In many cases,omit_set
can be used instead.omit
andomit_set
does not result inif_unavailable
any longer. Instead, the respective claim(s) is just omitted from the response. This should break the cycle and avoid the need for recursive execution.-
The execution order should be changed to the following:
-
check all claims with an
abort
action- for the claim, first check
if_unavailable
, thenif_different
. - as in the current spec, if any abort is triggered, further processing is not required
- for the claim, first check
-
check all claims with an
omit
action- as above
-
check all claims with an
omit_set
action- as above
-
What do you think, @Takahiko Kawasaki ?
-
reporter Well, I started to doubt that
if_different
is worthless, especially when its action isomit
oromit_set
…if_different
assumes that at least one ofvalue
,values
andmax_age
is specified. AS implementations usevalue
,values
andmax_age
to find a dataset that matches the requirements. That the condition ofif_different
becomes true implies that the dataset is not selected. If so, what does it mean toomit
some parts of the unselected dataset?Suppose that an AS receives the following request,
{ "userinfo": { "verified_claims": { "verification": { "trust_framework": null, "evidence": [ { "type": { "value": "electronic_signature" }, "issuer": { "value": "ABC", "if_different": "omit" }, "serial_number": { "value": "123", "if_different": "omit" }, "created_at": null } ] }, "claims": { "family_name": null } } } }
and the AS holds the following evidence:
type issuer serial_number electronic_signature ABC 456 electronic_signature DEF 123 In this case, what should the
evidence
array in the response hold? Possible answers may be the following.{"type":"electronic_signature", "issuer":"ABC", "created_at":"..."}
{"type":"electronic_signature", "serial_number":"123", "created_at":"..."}
- No evidence available that matches the requirements
I guess that AS implementers will choose the last answer. If so,
if_different
's in the request are meaningless.I’ve not dug into this deeply yet, but my intuition at the time of this writing is that it would be difficult to find use cases where
if_different
meaningfully functions. If there are any, please let me know. -
reporter In my opinion, Joseph’s answer in the AB/Connect mailing list does not conflict with the OIDC4IDA specification.
The Section 7.7.2. Data not Matching Requirements of OIDC4IDA says as follows:
When the available data does not fulfill the requirements of the RP expressed through
value
,values
, ormax_age
, the following logic applies:-
If the respective requirement was expressed for a Claim within
verified_claims/verification
, the wholeverified_claims
element MUST be omitted. -
Otherwise, the respective Claim MUST be omitted from the response.
The
value
,values
andmax_age
constraints can work as filters underverified_claims/verification
. At the same time, the constraints underverified_claims/claims
have a different role and they can be used to omit unmatched claims. The behavior of the constraints underverified_claims/claims
is the same as the one described by Joseph’s answer (i.e. the behavior of OIDC Core). -
-
reporter DataExtractor.java in the authlete-java-common library is an open-source implementation of “Data Minimization” and “Filtering” as I mentioned in my blog “OpenID Connect for Identity Assurance, explained by an implementer“. DataExtractorTest.java contains tests for the implementation.
If you change the value of
LOGGING_ENABLED
in DataExtractorTest.java totrue
/** * Flag for logging of {@link DatasetExtractor}. Set {@code true} if you * want to view details logs. */ private static final boolean LOGGING_ENABLED = true;
and run the tests, you can see how the logic of Data Minimization and Filtering visits data elements in
verified_claims
.git clone https://github.com/authlete/authlete-java-common cd authlete-java-common vi src/test/java/com/authlete/common/ida/DatasetExtractorTest.java mvn -Dtest=com.authlete.common.ida.DatasetExtractorTest test
The following is a sample output of the tests.
[INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.authlete:authlete-java-common:jar:3.36-SNAPSHOT [WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-site-plugin is missing. @ line 120, column 29 [WARNING] Reporting configuration should be done in <reporting> section, not in maven-site-plugin <configuration> as reportPlugins parameter. @ line 123, column 40 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] -----------------< com.authlete:authlete-java-common >------------------ [INFO] Building com.authlete:authlete-java-common 3.36-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ authlete-java-common --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 1 resource [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ authlete-java-common --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 215 source files to /Users/taka/GitHub/authlete-java-common/target/classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ authlete-java-common --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 1 resource [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ authlete-java-common --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ authlete-java-common --- [INFO] Surefire report directory: /Users/taka/GitHub/authlete-java-common/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.authlete.common.ida.DatasetExtractorTest [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name={values=["Unknown0""Unknown1"]}, original:/claims/given_name="Sarah"> [DE07] The property does not satisfy the constraint, and therefore the property is omitted. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/family_name=null, original:/claims/family_name="Meredyth"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="my_framework"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/datetime_0={max_age=2000000000}, original:/claims/datetime_0="2022-04-22T12:34:56"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/datetime_1={max_age=2000000000}, original:/claims/datetime_1="2022-04-22T12:34:56+0900"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/datetime_2={max_age=2000000000}, original:/claims/datetime_2="2022-04-22T12:34:56Z"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/datetime_3={max_age=2000000000}, original:/claims/datetime_3="2022-04-22T12:34"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/datetime_4={max_age=2000000000}, original:/claims/datetime_4="2022-04-22T12:34+0900"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/datetime_5={max_age=2000000000}, original:/claims/datetime_5="2022-04-22T12:34Z"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name={value="Unknown"}, original:/claims/given_name="Sarah"> [DE07] The property does not satisfy the constraint, and therefore the property is omitted. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/family_name=null, original:/claims/family_name="Meredyth"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/address/locality=null, original:/claims/address/locality="Edinburgh"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/:tc=null, original:/claims/:tc=null> [DE02] The property is unavailable, and therefore omitted. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/::predefined_tc={object}, original:/claims/::predefined_tc=null> [DE02] The property is unavailable, and therefore omitted. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework={value="unknown"}, original:/verification/trust_framework="uk_tfida"> [DE06] The property does not satisfy the constraint, so matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name={values=["Unknown0""Sarah"]}, original:/claims/given_name="Sarah"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/family_name=null, original:/claims/family_name="Meredyth"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/assurance_process/assurance_details=null, original:/verification/assurance_process/assurance_details=[array]> [DE23] All available sub-elements under 'assurance_details' are unconditionally returned based on the special rule for the property. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/birthdate={max_age=2000000000}, original:/claims/birthdate="1976-03-11"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/family_name=null, original:/claims/family_name="Meredyth"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/0/type="document"> [DE06] The property does not satisfy the constraint, so matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/1/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/1/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/1/check_details/0/organization="TheCreditBureau"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/txn=null, original:/verification/evidence/1/check_details/0/txn="kbv1-hf934hn09234ng03jj3"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0={object}, original:/verification/evidence/1/check_details/0={object}> [DE20] The element in the array in the original dataset satisfies conditions of the element in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/1/check_details=[array]> [DE22] Some elements in the array in the original dataset satisfy any of the elements in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0={object}, original:/verification/evidence/1={object}> [DE20] The element in the array in the original dataset satisfies conditions of the element in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/2/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/2/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/2/check_details/0/organization="OpenBankingTPP"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/2/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/3/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/3/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/3/check_details/0/organization="GSMA"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/3/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/4/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/4/check_details/0/check_method="data"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/4/check_details/0/organization="GRO"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/4/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/5/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/5/check_details/0/check_method="data"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/5/check_details/0/organization="NextLex"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/5/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence=[array], original:/verification/evidence=[array]> [DE22] Some elements in the array in the original dataset satisfy any of the elements in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/birthdate={max_age=100}, original:/claims/birthdate="1976-03-11"> [DE07] The property does not satisfy the constraint, and therefore the property is omitted. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/family_name=null, original:/claims/family_name="Meredyth"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/0/type="document"> [DE06] The property does not satisfy the constraint, so matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/1/type={value="document"}, original:/verification/evidence/0/type="document"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/1={object}, original:/verification/evidence/0={object}> [DE20] The element in the array in the original dataset satisfies conditions of the element in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/1/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/1/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/1/check_details/0/organization="TheCreditBureau"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/txn=null, original:/verification/evidence/1/check_details/0/txn="kbv1-hf934hn09234ng03jj3"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0={object}, original:/verification/evidence/1/check_details/0={object}> [DE20] The element in the array in the original dataset satisfies conditions of the element in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/1/check_details=[array]> [DE22] Some elements in the array in the original dataset satisfy any of the elements in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0={object}, original:/verification/evidence/1={object}> [DE20] The element in the array in the original dataset satisfies conditions of the element in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/2/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/2/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/2/check_details/0/organization="OpenBankingTPP"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/2/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/1/type={value="document"}, original:/verification/evidence/2/type="electronic_record"> [DE06] The property does not satisfy the constraint, so matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/3/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/3/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/3/check_details/0/organization="GSMA"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/3/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/1/type={value="document"}, original:/verification/evidence/3/type="electronic_record"> [DE06] The property does not satisfy the constraint, so matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/4/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/4/check_details/0/check_method="data"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/4/check_details/0/organization="GRO"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/4/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/1/type={value="document"}, original:/verification/evidence/4/type="electronic_record"> [DE06] The property does not satisfy the constraint, so matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/5/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/5/check_details/0/check_method="data"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="TheCreditBureau"}, original:/verification/evidence/5/check_details/0/organization="NextLex"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/5/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/1/type={value="document"}, original:/verification/evidence/5/type="electronic_record"> [DE06] The property does not satisfy the constraint, so matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence=[array], original:/verification/evidence=[array]> [DE22] Some elements in the array in the original dataset satisfy any of the elements in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/:tc=null, original:/claims/:tc=null> [DE01] A transformed claim was found. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/::predefined_tc={object}, original:/claims/::predefined_tc=null> [DE01] A transformed claim was found. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework={object}, original:/verification/trust_framework="uk_tfida"> [DE09] The request has no constraint for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/trust_framework=null, original:/verification/trust_framework="uk_tfida"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/0/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/0/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="OpenBankingTPP"}, original:/verification/evidence/0/check_details/0/organization="TheCreditBureau"> [DE06] The property does not satisfy the constraint, so matching fails. [main] DEBUG com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/0/check_details=[array]> [DE21] None of the elements in the array in the original dataset satisfy any of the elements in the array in the request. Therefore, matching fails. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/type={value="electronic_record"}, original:/verification/evidence/1/type="electronic_record"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/check_method=null, original:/verification/evidence/1/check_details/0/check_method="kbv"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/organization={value="OpenBankingTPP"}, original:/verification/evidence/1/check_details/0/organization="OpenBankingTPP"> [DE08] The property satisfies the constraint, and therefore the property is put in the copy. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0/txn=null, original:/verification/evidence/1/check_details/0/txn="kbv2-nm0f23u9459fj38u5j6"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details/0={object}, original:/verification/evidence/1/check_details/0={object}> [DE20] The element in the array in the original dataset satisfies conditions of the element in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0/check_details=[array], original:/verification/evidence/1/check_details=[array]> [DE22] Some elements in the array in the original dataset satisfy any of the elements in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence/0={object}, original:/verification/evidence/1={object}> [DE20] The element in the array in the original dataset satisfies conditions of the element in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/verification/evidence=[array], original:/verification/evidence=[array]> [DE22] Some elements in the array in the original dataset satisfy any of the elements in the array in the request. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/given_name=null, original:/claims/given_name="Sarah"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/family_name={value="Unknown"}, original:/claims/family_name="Meredyth"> [DE07] The property does not satisfy the constraint, and therefore the property is omitted. [main] TRACE com.authlete.common.ida.DatasetExtractorTest - <request:/claims/address/locality=null, original:/claims/address/locality="Edinburgh"> [DE04] The request does not have constraints for the property, and therefore the property is put in the copy unconditionally. Tests run: 16, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.49 sec Results : Tests run: 16, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 8.994 s [INFO] Finished at: 2022-09-08T00:58:17+09:00 [INFO] ------------------------------------------------------------------------
A point worth learning from the log is that element-level
if_unavailable
andif_different
conditions will not work well due to the nature of “Logical OR by Array”.For example, in the OR operation,
[false, true]
should be regarded astrue
although the first element isfalse
. Ifif_unavailable
is a per-element operator and is attached to the first element, the action specified by theif_unavailable
has to be executed although the final evaluation of the upper array (i.e.[false, true]
) should becometrue
.This is the reason I have reached a conclusion that per-element
if_unavailable
andif_different
won’t work and another approach has to be devised. See also my comments in Issue 1301. -
- changed milestone to ASC ID1
-
I thought about this problem a little more. One problem identified in this discussion is that anything we put into the claims' dictionaries in the structure lacks an explicit evaluation order. If we want to make this order explicit, we need to have an array with the rules that are to be applied. If we have such an array, it would need to live outside of particular claims and it would need to reference the claims. For the reference, I see no good way other than using established syntax, like JsonPath. If we start with JsonPath, we can use it to refer to both the claims to which a rule applies as well as the claims which shall be omitted.
How about this structure?
{ "id_token": { "verified_claims": { "verification": { "trust_framework": null, "assurance_level": { "value":"example_assurance_level" } }, "claims": { "family_name": { "value":"nonexistent_family_name" }, "given_name": null, "birthdate": { "value":"1980-01-01" } } }, "asc/sao-processing-rules": [ // when assurance level is unavailable, omit verified_claims { "claim": ["$.verified_claims.verification.assurance_level"], "if": "unavailable", "then": "omit", "what": ["$.verified_claims"] }, // when assurance level is different, omit verified_claims { "claim": ["$.verified_claims.verification.assurance_level"], "if": "different", "then": "omit", "what": ["$.verified_claims"] }, // when either name component is unavailable, omit both components { "claim": ["$.verified_claims.claims.family_name", "$.verified_claims.claims.given_name"], "if": "unavailable", "then": "omit", "what": ["$.verified_claims.claims.family_name", "$.verified_claims.claims.given_name"] }, // when birthdate is unavailable, abort transaction { "claim": ["$.verified_claims.claims.birthdate"], "if": "different", "then": "abort" } ] } }
Advantages:
- The order of the rules is explicit: Just follow from top to bottom.
- No backtracking needed: Each rules is evaluated based on the previous rule’s output.
- Easy to read.
- Easy to process - for the JSONPaths, we would only allow dot-separated components, not other elements of JSONPath. (I.e., only
$.a.b.c
, not, for example$.a.*
or$.a..c
as the meaning might not be clearly defined.) Implementers can use a JSONPath library, but don’t need to. - The rules in
asc/sao-processing-rules
can be evaluated after retrieving the data - there is no need to mix logic for retrieving the data and processing the ASC/SAO rules.
This is similar to Taka’s approach proposed above, but does not need explicit sets (for the price of some repetition, but I guess that will be fine).
-
reporter "if":"different"
needs an argument for comparison.- A point to consider is that the value of
verified_claims
can be a JSON array. So, the new syntax has to be able to accommodate both a JSON object and a JSON array. (e.g.$.verified_claims.xyz
matches"verified_claims":{”xyz”:123}
but does not match"verified_claims":[{"xyz": 123}]
)
-
Regarding the first point, the value to compare with would be given in usual OIDC syntax as the “value” element in the original claim.
For the second point: Indeed, we need to find a good way to handle that. Not sure what that could be yet.
-
reporter Ah, thanks. Yes, we have the
value
property. -
I have been thinking this through again and it strikes me that we are writing a domain specific language for expressing some policy that the RP would like applied to the returned claims. If that is the case then I think there is some merit in looking at existing policy description languages. There are two that I know of that are ‘standardised’ in some way… XACML and Rego.
I’m pretty sure we are not interested in an XML based approach so I had a look at Rego.
While I think that Rego is probably far too rich for our needs I think there may be some significant merit in copying what we need from it. There is also a possibility (Daniel’s idea not mine) that defining a DSL that is a close derivative of Rego might allow for easy transformation of a SAO policy into compliant Rego which could then in turn be executed using existing OPA software implementations potentially reducing the complexity for implementers.
-
Here’s an example Rego policy that implements quite a bit of the use case Daniel’s example shows. The input object is the claims request parameter without SAO extensions and the data object is the data held at the IDP about the end-user.
The current output is a list of actions either abort or omit (with list of objects to omit)
package app.sao import future.keywords.if import future.keywords.contains omit contains omitlistentry if { # family name different then omit family_name only unless aborted data.verified_claims.claims.family_name != input.id_token.verified_claims.claims.family_name.value not abort # don't list omit if abort is true omitlistentry := "data.verified_claims.claims.family_name" } omit contains omitlistentry if { # assurance level different then omit verified_claims data.verified_claims.verification.assurance_level != input.id_token.verified_claims.verification.assurance_level.value not abort # don't list omit if abort is true omitlistentry := "data.verified_claims" } omit contains omitlistentry if { # assurance level unavailable then omit verified_claims not data.verified_claims.verification.assurance_level not abort # don't list omit if abort is true omitlistentry := "data.verified_claims" } abort := true if { # claims request birthdate value different from data birthdate then abort data.verified_claims.claims.birthdate != input.id_token.verified_claims.claims.birthdate.value }
-
reporter As we talked in the last WG call, if we adopt a rich language like Rego for SAO (Selective Abort/Omit), the language would be able to be used for TC (Transformed Claims), too. And the language may be able to replace the current “verified_claims” request sytax completely.
Daniel pointed out that SAO/TC capabilities should not enable clients to retrieve unintended information of the user. Other points to consider are how easy to implement, how easy to use, whether Rego implementations are available in popular computer languages developers use.
A small language is interesting to support and implement. However, a good point of the current specification of TC is “all are expressed in JSON”. Because JSON parsers available widely can be used, lexer & tokenizer and parser don’t have to be written from scratch.
-
I revisited the latest proposal and took another look at DIF Presentation Exchange. It seems that the current proposal has at least the following problems:
- The problem with arrays identified by Taka above
- The fact that if_different does not translate well into database queries as pointed out also by Taka
- We cannot currently express things like “I need an evidence document with these two properties or one with these two other properties” (i.e., we cannot express related properties over more than one claim at a time)
I’m comparing again to PE and looking at other existing JSON query approaches (like jq) to figure out the best way forward.
-
My slides from today’s meeting: https://docs.google.com/presentation/d/1Wq7AkpDN25fmXP0xZkVrKYDOunpGMtgE3jkkr8ck304/edit?usp=sharing
-
Some of the following are covered by current propsal but we should be clear about our scope and avoid complexity if possible. I suspect there may be more use cases that we might consider. It would be good to find consensus on what is in scope what is out of scope.
Some are about conditions and some are about actions, some may be combinations…
Single claim conditions:
- return a sub-claim e.g. address.country
-
perform data minimisation on a single claim e.g.
- 01/01/2010 → over-18;
- “James” → “J”;
- jim@example.com → example.com
-
match presented value e.g.
- email = “is e-mail jim@example.com → true. (special case of data minimisation?)
-
Combination of sub claim and data minimisation e.g.
- “is address.country = US?”
- “is jim@example.com on example.com?”
Combination of claim conditions across multiple ase claims:
- (address.country = GB) AND (age over 18)
Actions:
- Abort whole request
- omit the single claim subject to the condition
- omit a single set of claims
- omit verified claims regardless of base claim of condition
- omit more than one set of claims
-
Together with Transformed Claims, the approaches in the slide deck can all cover all of these cases. The one case where they differ is
- ((address.country = GB) AND (age over 18)) OR ((address.country = US) AND (age over 21))
This can only be covered by 3, 4, and 5.
-
Here is a table for comparison of the various solutions: https://docs.google.com/spreadsheets/d/18VsXcXN13TbSn6Ehd2hw0utz4i7dANiY7Hkeo7rZAwo/edit?usp=sharing
-
During the WG call today there was a decision to move forward with Proposal 5 in Daniel’s spreadsheet “Partial JSON Schema + filter“
Next steps are that DF will update the ASC draft, the WG may choose to proceed to ASC Implementers' draft 1 and Authlete have kindly said that they will find time to do a POC implementation.
-
This has been solved with https://bitbucket.org/openid/ekyc-ida/pull-requests/172
-
- changed status to resolved
- Log in to comment