Claims evaluation
Introduction
Claims evaluation is done during the process of determining whether a party is authorized to execute a protocol permission. This also applies to an obligation, which is a specialized permission. In this process, the party claims, which are attributes that identify the party, are compared to the claims bound to the protocol party. The two sets of claims are evaluated to determine authorization.
Evaluation rules
This describes how the claims are evaluated to determine if the calling party is authorized to access the given protocol.
The calling party's claims must be a superset of the entity claims of the bound party, which means it must contain all the key values of the entity claims but could contain more. If the entity key is found in the calling party claims, the values too must be a superset of the entity values.
The rules for the access evaluation of claims are slightly different. Like with entity claims, the calling party's claims keys must be a superset of the bound party's access claim keys. However, for every matched key, the intersection of the two values sets must have at least one overlapping value.
See party methods to see how to access claim elements or to test for claim compatibility.
Note: As documented, it is allowed to have empty entity and access claims. A protocol or permission bound to a party with no claims is publicly accessible because there are no claims to verify. Though this is not the common use of a protocol, it is possible.
A use case for having a protocol bound to a public party could be one of the following scenarios:
- making read access (as observer)
- making action invocation public (as a regular member party), e.g. an action that is possible for any user to access, without defining a kind of explicit "public" claim
Evaluation process
Let's look at the process step by step.
Scenario 1
We have a simple protocol, called externally, that has no references to any other protocol.
Joe has a reference to protocolA
and wants to execute the permission actionA
.
package test;
protocol[partyA] protocolA(var myData: Text) {
@api
permission[partyA] actionA() {
debug("do something with " + myData);
};
}
Claims bound to the protocol party - partyA.
{
"entity": {
"company": ["client-company"]
},
"access": {
"department": ["sales"]
}
}
Since Joe is accessing the system externally, he is first authenticated by the identity management system and a JWT token is returned. Joe's claims are contained in the JWT token.
Joe's raw JWT token
{
"exp": 1673864644,
"iat": 1673864344,
"iss": "http://idm.com/token",
"sub": "54e593fe-a928",
"name": "Joe",
"email": "joe@client-company.com",
"email_verified": false,
"given_name": "",
"family_name": "",
"company": "client-company",
"position": ["ceo", "sales"],
"department": ["executive", "sales"]
}
The JWT token values are stringified (for non-string values) and converted to sets. Several JWT standard reserved claims are removed. As well as, any claims that have empty values.
Joe's converted claims
{
"iss": ["http://idm.com/token"],
"name": ["Joe"],
"email": ["joe@client-company.com"],
"email_verified": ["false"],
"company": ["client-company"],
"position": ["ceo", "sales"],
"department": ["executive", "sales"]
}
These converted claims will now be used to evaluate if Joe is authorized to execute the permission on protocolA
.
Once access is given after a successful authorization check, the party will take on the identity of the bound party.
This means, that the claims will be identical to the claims bound to partyA
, listed above, not Joe's original
claims. This also means that Joe is now identified as partyA
and all actions in this permission will be executed as
partyA
with the associated claims.
In other words, even though Joe can play many roles (as reflected in position and/or department claims) in
client-company
, he will now be limited only to the roles that partyA
has access.
The set of claims are now converted to their NPL representation containing both entity and access claims.
{
"entity": {
"company": ["client-company"],
"position": ["sales"]
},
"access": {
"department": ["sales"]
}
}
Note: It is important to remember, even though claims are represented within NPL as entity and access, the claims are once again merged during the claims evaluation process. This has the following effect:
- Whenever claims evaluation happens, the union of the bound party claims will only be equivalent to or a subset of the union of the calling party claims.
- During evaluation, entity and access claims are not constrained as such. Meaning, that an entity claim on the calling
party could become an access claim on the bound party. Given
partyA
andpartyB
below, consider the following scenario. You have a protocol with the bound party,partyA
, that calls an action on another, with bound partypartyB
. In this case,partyA
will pass the claims evaluation as bothpartyA
andpartyB
because the union set of the claims will match both parties. - As illustrated in the above example, multiple party matches are possible within NPL. In the event that two parties
exist on one protocol where the calling party matches both, the first matching party is arbitrarily chosen, unless the
tie-breaker is specified using the
caller
parameter. See the "exercise action request" engine API endpoint.
PartyA
{
"entity": {
"company": ["client-company"],
"location": ["california"]
},
"access": {
"department": ["security"],
"role": ["guard"]
}
}
PartyB
{
"entity": {
"department": ["security"],
"role": ["guard"]
},
"access": {
"company": ["client-company"],
"location": ["california"]
}
}
Scenario 2
Joe has a reference to protocolB
and wants to execute actionB
. The permission actionB
attempts to access a
permission actionA
on protocolA
.
protocol[partyB] protocolB (var protoRef: protocolA) {
@api
permission[partyB] actionB() {
debug(protoRef.actionA[partyB]()); // references permission on protocolB
};
}
The claims bound to the protocol party (partyB) are
{
"entity": {
"company": ["client-company"],
"position": ["sales"]
},
"access": {
"department": ["sales"]
}
}
The authorization process is the same with a slight difference with regard to the claims.
In the previous scenario, the claims are provided by the JWT token. In this scenario, the invocation is internal so the claims are resolved by the system. The claims returned are those bound to the protocol party.
partyB
claims are matched against partyA
claims to determine compatibility. The claims are not evaluated as entity
and access but are merged into a single map of claims prior to evaluation. Once merged, the matching process is
identical.