Claims evaluation
Introduction
Claims evaluation occurs during protocol access authorization. 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
Here we will describe the rules that govern whether the calling party, defined below, is authorized to access the given protocol. A request to access a protocol can come either via an API request or internally from other NPL code. Claims evaluation occurs both for internal and external requests. These two method of interacting with protocols are largely identical but there are some minor differences -- see API Access Evaluation (external) and NPL Access Evaluation (internal). Common rules to both will be discussed below.
Terms
Calling party: The party given when executing a protocol action or accessing one of its properties. In this example,
ISSUER
would be the calling party when executing the pay
action.
var iou = Iou[ISSUER, PAYEE](100);
iou.pay[ISSUER](50);
Bound party: A bound party is the party that is specified during creation of the protocol or permission. In this
example, the bound parties are issuer
and payee
.
protocol[issuer, payee] Iou(var forAmount: Number) {
permission[issuer] pay(amount: Number) {
// payment logic
};
};
Bound claims: Bound claims are the claims of the bound party specified at instantiation of the protocol.
If a party has access to execute protocol actions, they will also have access to fields of that protocol.
How claims are evaluated
The calling party's claims keys must be a superset of the bound party's claim keys. For every matched key, the intersection of the two values' sets must also have at least one overlapping value.
See party methods to see how to access claim elements or to test for claim compatibility.
Note: In some cases, it may be desired that a protocol's members be publicly accessible. If declared, observers with no claims can be used for this purpose.
Evaluation process
Let's look at the process step by step.
Evaluating a simple protocol
We have a simple protocol, called externally, that has no references to any other protocol.
Joe has a reference to Iou
and wants to execute the protocol action, pay
:
protocol[issuer, payee] Iou(var forAmount: Number) {
permission[issuer] pay(amount: Number) {
// payment logic
};
};
Claims bound to the issuer
protocol party:
{
"claims": {
"company": ["client-company"],
"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.
Joe's raw JWT:
{
"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 values are stringified (for non-string values) and converted to sets. Several JWT standard reserved claims are removed, and claims with empty values are omitted.
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 Iou
.
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 issuer
, listed above, not Joe's original
claims. This also means that Joe is now identified as issuer
and all actions in this permission will be executed as
issuer
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 issuer
has access.
The set of claims are now converted to their NPL representation.
Converted claims:
{
"claims": {
"company": ["client-company"],
"position": ["sales"],
"department": ["sales"]
}
}
Matching a claim with multiple keys
In the earlier example, Joe's claims included a department key with multiple values. These claims were compared against the bound party's corresponding claims. Since Joe's claims keys formed a superset of the bound claims keys and at least one value for each key matched, he was authorized to perform the action.
In a similar fashion, if Joe's claims included only a single department value—such as "department": ["sales"]
—
he would still be authorized, even if the issuer protocol party’s bound claims listed multiple department values,
as long as one matched Joe’s claim:
{
"claims": {
"company": ["client-company"],
"department": ["sales", "marketing", "IT"]
}
}
Evaluating a protocol referencing another protocol
Joe has a reference to ProtocolB
and wants to execute actionB
. The permission actionB
attempts to access a
permission actionA
on ProtocolA
.
Example of ProtocolB
referencing actionA
on ProtocolA
:
protocol[partyA] ProtocolA() {
@api
permission[partyA] actionA() {
// do something
};
}
protocol[partyB] ProtocolB(var protoRef: ProtocolA) {
@api
permission[partyB] actionB() {
protoRef.actionA[partyB](); // references action on protocolA
};
}
The claims bound to the protocol party (partyB
) are:
{
"claims": {
"company": ["client-company"],
"position": ["sales"]
"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. 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.