Claim-Based Attestations
Your identity provider already attests who a user is and what they hold: their roles, scopes and mapped claims, signed into their token. A claim-based attestation lets a policy require those values. MACAW verifies the token and accepts its claims as the identity provider issued them. No approver is asked and no tool is involved.
Example
{
"attestations": ["writer", "consented_to_payments", "transfer_approved::{params.amount > 25000}"],
"constraints": {
"attestations": {
"writer": { "claim": { "roles": "Banking.Writer" } },
"consented_to_payments": { "claim": { "scopes": "Banking.Write" } },
"transfer_approved": { "approval_criteria": "role:Banking.Approver", "timeout": 120 }
}
}
}A payment needs a token with the Banking.Writer role and the Banking.Write scope. Above 25,000 it also needs an approver. The first two come from the identity provider; the third from a person.
Writing the Rule
claim maps each claim field to the value it must contain:
| claim | Holds when |
|---|---|
| {"roles": "Banking.Writer"} | roles contains Banking.Writer |
| {"roles": ["Banking.Reader", "Banking.Writer"]} | roles contains either value |
| {"roles": "Banking.Writer", "scopes": "Banking.Write"} | both fields hold |
| [{"roles": "Banking.Writer"}, {"roles": "Banking.Approver"}] | both rules hold: roles contains both values |
A list of values means any one of them. Every field in a rule must hold, and every rule in a list. A field contains a value when it is a list that includes it, a space-separated string that has it as a word (such as scp), or that value itself. A field the token does not carry never holds.
Where the Claims Come From
The fields are those the tenant's identity bridge maps from the identity provider's token: roles, and any other mapped field, such as scopes from Entra's scp. MACAW checks that the token is genuine and current: signed by the tenant's identity provider, issued for this application, not expired. Who holds which role or scope is managed in the identity provider.
Identity provider ──signs──► token { roles, scp, ... }
│
▼
MACAW: signature, audience, expiry ok? ──no──► no claim holds
│ yes
▼
policy: "writer": { "claim": { "roles": "Banking.Writer" } }
│
roles contains Banking.Writer?
│ yes │ no
▼ ▼
attestation holds call deniedHow It Is Decided
- -Whose token: the person or service that started the request. In a chained invocation, that is the first caller.
- -For how long: while that token is valid. Nothing is consumed, so
one_time,time_to_liveandtimeoutdo not apply. - -Only from the token: no approval and no tool can provide it. When the claims do not hold, the call is denied with
Attestation 'writer' is not proven by the requester's token, and no approval is requested. - -Only with a verified identity: a requester registered without a validated token proves no claim.
- -Only a requirement: like any attestation, it can deny a call. It never allows a resource the policies leave out or lifts a denial.
- -When it is required is set by the
::{condition}on the requirement, as for any attestation. It is decided where a policy requires it and is not stored, socontext.has_attestation()does not see it. - -One type per name: definitions merge by name across policies. A name that two policies declare as different types denies the call.
claim and approval_criteria
Both can name a role. "claim": {"roles": "Banking.Writer"} requires the requester's token to carry the role, and is decided on the call. "approval_criteria": "role:Banking.Approver" names who may approve, and waits for that person.