PingAuthorize Talk to us

Reference design · PingAuthorize 11 · AuthZEN Adapter · OpenID Federation 1.0

Purpose-bound delegation

When an AI agent hands a customer's task to another agent, and that one to a third, every hop is a chance for the task to drift from what the customer asked for. Purpose-bound delegation checks every entity in the chain against the purpose of the agent the customer started with, and PingAuthorize decides - before a token is issued, and again when it is used.

The chain

Judged against the origin, at every hop

A customer, Alice, asks Northwind Bank's Bill Pay Assistant to pay her bills. Bill Pay checks her available funds, hands the payments to a routing agent, which may hand payee checks to a third, which calls a directory API. Each hand-off is an RFC 8693 token exchange, and each agent that took part is added to the token's act claim - the newest outermost, the agent Alice started with innermost.

That innermost agent is the origin, and its purpose bounds everything after it. At every hop the whole chain, and the entity being asked for, is judged against the origin's purpose - never against the agent before it. A direct-debit agent may suit a payment router perfectly and still be outside what Alice asked for.

  • Before a token exists. The authorization server asks PingAuthorize before it issues each token, so an off-purpose party never receives the customer's token at all.
  • Again when it is used. The receiving API's enforcement point asks again at the call, resolving every entity in the chain afresh: a member dropped since the token was issued is refused there.
  • Deny, hold or permit. A misfit is denied with a reason the caller can show. Something outside the customer's purpose that serves nothing else is held for the customer to approve. Only the bank's purpose map permits.
  • Agents, APIs and the authorization server are all federation entities, with their keys and their words published and vouched for by a superior, not configured locally.
THE CHAIN THE TOKEN FOR THE HOP · act JUDGED AGAINST BILL PAY Alice customer Bill Pay Assistant origin PayRoute Orchestrator agent Payee Check agent Payee Directory API API delegates hands on hands on calls delegation grant may_act Bill Pay Bill Pay · origin PayRoute Bill Pay · origin Payee Check PayRoute Bill Pay · origin innermost at every hop its own purpose: vouched, specific PayRoute PayRoute, Payee Check PayRoute, Payee Check, the API at the AS, then at the receiver's PEP
The chain grows at the front and the origin never moves. By the last hop the token names three agents, with Bill Pay innermost. The authorization server judges every entity in the chain, and the one it is asked for, before it issues each token; the receiver's enforcement point judges them all again when the token is used.

Three parts

Records in banking terms, a model that reads them, and PingAuthorize deciding

OpenID Federation

Every agent and API is a federation entity

What an entity does is published in its metadata in BIAN v14 terms - the banking industry's service vocabulary. Agents declare it in authorization_details_types (RFC 9396), APIs in authorization_details_types_supported (RFC 9728). Each also states a mission and a purpose statement in clear text, which its superior vouches for by setting them in its Subordinate Statement.

Entity statementsTrust chainsMetadata policy
Small language model

Reads what the words actually describe

A model of a few billion parameters, running inside the bank's network, reads each entity's resolved record - purpose statement first, then mission and description - and reports which BIAN Service Domain the words describe, and whether they show anything the declared codes do not: hidden activity, use for other organisations, or text aimed at the checker. Every adverse claim must quote the record; a quote that is not really there is dropped.

Open weights on Ollamaor a hosted typed decision model
PingAuthorize

Decides on the bank's purpose map

Every decision goes through PingAuthorize 11 in embedded-PDP mode, through the AuthZEN Adapter. Its policy holds the bank's BIAN purpose map - which Service Domains a task may draw on - and the secondary-use list. Inside the map an entity is permitted. Outside it, the model may deny what serves another purpose and can never permit: everything else, a helpful extra included, is held for the customer.

Ten rules, first applicableAuthZEN 1.0

Architecture

Agents act; the authorization server and every API ask PingAuthorize first

Every decision goes through PingAuthorize: the authorization server asks before it issues a token, and each API and agent asks before it acts on one. PingAuthorize gets its evidence from the purpose service, which reads the federation, and asks for the model's judgement only when the federation facts are clean. The authorization server and the enforcement points also take the keys they check - client keys, token-signing keys - from the federation, not from local configuration. In the hosted demo, PingFederate is the authorization server: RFC 8693 token exchange issuing DPoP-bound tokens, each one decided by PingAuthorize before it is issued.

WHO ACTS WHO DECIDES WHAT IT DECIDES ON Alice the customer Agents each a federation entity and a PEP Bill Pay · origin PayRoute Payee Check Spend Insights QuickPay Helper BillSaver DebitLink BillBridge Biller Connect Receipt Relay BillNudge Money Mate APIs each behind a PEP Payments API Customer Position API Credit Rating API Payee Directory API Offers API Authorization server RFC 8693 token exchange DPoP · private_key_jwt PingAuthorize AuthZEN servlet embedded PDP Policy rules, first applicable BIAN purpose map secondary-use list decides every checkpoint OpenID Federation Trust anchor Northwind Bank Accredited Fintech Registry mission and purpose, vouched BIAN-coded capabilities resolve endpoint applies the policy Purpose service Federation facts · no model Purpose judge · the model judge only when facts are clean Small language model ministral-3:3b · Ollama runs inside the network delegates token exchange DPoP-bound call AuthZEN AuthZEN AuthZEN facts, judge resolves chains classifies records client keys

One decision

The model is the last thing asked, and only when it is needed

PayRoute, which Bill Pay handed the payment to, asks for a token for the Payments API. Steps 4 to 6 settle what can be settled without a model: every entity still resolves, no text is aimed at the checker, the origin's purpose is vouched for, and nobody declares a secondary use. In step 8 the model reads each entity's mission and purpose statement in BIAN terms. The answers are cached until the federation statements expire, so the model rarely runs twice. The token in step 11 names Alice as sub, the Payments API as its only audience, and the chain PayRoute ← Bill Pay in act. When PayRoute then calls the Payments API, its enforcement point asks PingAuthorize again.

PayRoute Authorization server PingAuthorize Purpose service Federation Model 1 token exchange, DPoP 2 resolve PayRoute's keys 3 AuthZEN + act chain 4 facts 5 resolve every entity 6 clean 7 judge 8 classify records 9 in the purpose map 10 PERMIT + reason 11 token

The policy

Ten rules, first applicable

RuleWhat must holdWho answersIf it fails
1Every entity in the chain, and the one asked for, resolves to the trust anchorthe federation's resolverdeny
2No text in a record is aimed at the checkerfacts, no modeldeny
3The origin's purpose is vouched for by its superior, and specificfacts, no modeldeny
4No entity declares a secondary-use Service Domainfacts, no modeldeny
5What an entity declares matches what its words describethe model's readingdeny
6No entity's words describe a secondary usethe model's readingdeny
7Every entity serves the origin's purposethe model, outside the purpose mapdeny
8Every entity can be placed, and the answer is surethe judgehold
9Every entity is inside the bank's purpose mapthe purpose mappermit
10Otherwisepolicydeny

Rules 1 to 4 need no model: they are settled from signatures and declared codes. The model may deny (5 to 7) or leave a hold (8); only the purpose map permits (9). The reason code of the first failing rule goes back to the caller, so an enforcement point can show why.

Outside the purpose map the model denies only what it finds serves another purpose: marketing, profiling, data for other organisations, the bank's own planning, risk or credit decisions, or a different product. Anything else outside the map - a helpful extra, something too vague to place, or no answer in time - is held.

Holding is a decision. Bill reminders serve nothing but the customer, and are not what she asked for. They are neither waved through nor refused outright: they are held, and the customer decides.

Two places to check

At the PDP, or in the federation's policy

Whether an entity's words fit its declared codes can be checked in two places, and the demo runs every scenario both ways. At the PDP, the bank's purpose service runs the model on each record and applies rule 5. In the federation's policy, each superior writes the check into its Subordinate Statement as a metadata policy on purpose_statement: the operator describes, listed in metadata_policy_crit so that a relier which cannot evaluate it must reject the chain rather than skip the check. The trust anchor's resolver has the model and applies the policy once for everyone; an entity whose words fail it does not resolve, and is refused with invalid_metadata. What stays put is who decides fit to the task: PingAuthorize, on the bank's purpose map, either way.

At the PDPAt the federation's resolver
Who runs the modelEach bank, for its own decisionsThe trust framework's resolver, once for every relier
Where the rule livesPingAuthorize's policy: rule 5, "what an entity declares must match what it describes"The superior's Subordinate Statement: purpose_statement describes the codes, critical
What happens to a misfitDenied by the bank, with the model's quoteDoes not resolve at all: rule 1, for every relier, with the resolver's reason
What the PDP trustsIts own modelThe resolver's signature on the resolve response, with the trust chain it was derived from
Fit to the taskThe bank's purpose map; outside it, the PDP's model may deny another purposeThe same, at the PDP: the resolver never sees the task
SuitsA bank that wants its own model, prompts and audit trailA federation whose superiors vouch for purpose, and want one check for everyone

The token

What the token carries

# the token PayRoute receives for the Payments API
{
  "iss": "…/e/as",
  "sub": "alice@northwind.example",
  "aud": "…/e/payments-api",
  "client_id": "…/e/payroute",
  "scope": "bill_pay",
  "act": {
    "sub": "…/e/payroute",     ← current actor
    "act": {
      "sub": "…/e/bill-pay"   ← origin
    }
  },
  "cnf": { "jkt": "…" }        ← DPoP key
}
  • Delegation, not impersonation. Alice stays the subject. Each agent that took part is added to act, the newest outermost.
  • The origin bounds the chain. The innermost actor is the agent Alice started with. Its purpose, as its federation record declares it, is what every later entity is checked against.
  • Read on purpose. RFC 8693 §4.1 treats earlier actors as informational. For agents, where a request came from is the point, so this design reads the whole chain - and only PingAuthorize's rules read it.
  • Every name resolves. Each act.sub is a federation entity identifier, so its record, and who vouches for it, can be fetched and checked.
  • Onward only when approved. When the audience is itself an agent, the token also carries may_act naming it. Only that agent can exchange it onward, and only because PingAuthorize permitted it as the audience.

The record

What an entity statement carries

Every agent and API states its mission and purpose in clear text in its federation metadata, and its superior vouches for them. This is the registry's Subordinate Statement about PayRoute, with purpose checked in the federation's policy and the keys left out.

{
  "iss": "…/e/registry",               ← its superior signs
  "sub": "…/e/payroute",
  "metadata": {
    "oauth_client": {
      "display_name": "PayRoute Orchestrator",
      "mission": "Get each bill payment to the
        biller by the fastest safe route.",
      "purpose_statement": "Routes customers' bill
        payments to the fastest available payment
        rail and confirms settlement.",   ← what the model reads
      "description": "Fast, reliable routing
        for bill payments."
    }
  },
  "metadata_policy": {
    "oauth_client": {
      "authorization_details_types": {
        "subset_of": [
          "…/initiate_payment_order_initiation",
          "…/execute_payment_rail" ]     ← codes capped
      },
      "purpose_statement": {
        "describes": "authorization_details_types"
      }                                ← the purpose policy
    }
  },
  "metadata_policy_crit": ["describes"]  ← must be understood
}
  • Mission and purpose, in clear text. The mission says why the entity exists; the purpose statement says what it does and for whom, in words a person and a small model can both read. The description is for people.
  • Vouched, not self-asserted. The registry sets these in its statement's metadata, which replaces whatever PayRoute wrote about itself (OpenID Federation §6.1.4.2). An entity whose superior does not vouch for its words is marked self-asserted.
  • Codes capped. The registry's metadata_policy limits the BIAN capabilities PayRoute may declare, whatever it claims.
  • Purpose as policy. The describes operator says the purpose statement must describe the declared codes and nothing else. Listed in metadata_policy_crit, it cannot be skipped: a relier either evaluates it or does not resolve the entity.
  • What the model makes of it. Payment Rail, Execute, serving the customer: the policy holds, and PayRoute resolves. The model reads all three fields, so words that give an entity away count wherever they are.
  • This design's convention. mission, purpose_statement and the describes operator are not defined by OpenID Federation. The standard has description (§5.2.2) and allows federations to add policy operators (§6.1.3.2); the rest is proposed here.

Scenarios

Fifteen scenarios, each run for real

Each is one button in the demo. Alice delegates bill payments to Northwind's Bill Pay Assistant, and every token exchange and every call is decided by PingAuthorize, which asks the model only when it has to. Three of them end differently depending on where purpose is checked: at the PDP the model reads the words and denies; in the federation's policy the entity does not resolve at all, and the model is never asked.

ScenarioWhat it showsDecided
Happy pathBill Pay → PayRoute → Payments API, every entity within the purpose mappermit
Purpose driftBill Pay names Spend Insights, which declares a secondary-use Service Domaindeny no model
Codes pass, words do notQuickPay's codes fit; its purpose statement sells payee datadeny model · in the federation's policy it does not resolve
Injection neutralised by the federationThe registry's vouched text replaces BillSaver's injectionpermit
Wrong resourcePayRoute asks for the Credit Rating APIdeny model
Revoked memberPayRoute dropped while its token is in flightdeny no model
Three agents deepBill Pay → PayRoute → Payee Check → Payee Directory API: three agents in actpermit
Revoked in the middlePayRoute dropped while Payee Check, the current actor, holds a valid tokendeny no model
Fits the delegator, not the originDebitLink (direct debit mandates) suits PayRoute's purpose, not Bill Pay'sheld model
The bank's own marketingBill Pay asks Northwind's Offers API, which declares Product Matchingdeny no model
Drift three hops downPayee Check asks for Biller Connect (Customer Relationship Management)deny no model
Borrowing to payPayRoute asks for BillBridge, which lends the shortfall (Consumer Loan): another productdeny model
Passed on to othersReceipt Relay's words say it passes payment history to credit reporting agenciesdeny on the model's quote · in the federation's policy it does not resolve
A helpful extraBillNudge sends bill reminders: not needed, but serving nothing elseheld model
An agent for everythingMoney Mate helps with anything to do with money: too broad to placeheld on the model's reading · in the federation's policy it does not resolve

Against the immediate delegator alone. The last scenario also puts the same question to PingAuthorize with the chain cut at PayRoute, which is what a check against the immediate delegator would ask. That answer is PERMIT, and the demo shows it next to the real decision. It is for comparison only; nothing is issued on it.

Recorded runs

Who took part, what was decided at each checkpoint, and where the deny landed

Each timeline is a recorded run of the demo, not a drawing of what should happen. Left to right are the checkpoints the run passed through: the customer's consent, each token exchange at the authorization server, each call at the receiver's enforcement point, and any change the registry made in between. Each box says what was decided, by which rule and by whom; the first deny stops the chain.

All fifteen, both ways, are on the demo's use cases page. The first decision that needs the model to read a record it has not seen takes a few seconds; later ones are answered from the cache.

What drift looks like

Every entity resolves and is in good standing; what is wrong is the purpose

KindExampleHow it is caught
A secondary use, declaredSpend Insights scores customers for partner offers. Northwind's own Offers API matches customers to products.Rule 4, from the declared codes. No model.
Drift deep in the chainThree agents from the origin, Payee Check asks for Biller Connect, which shares customers' details with billers' relationship teams.Rule 4, still judged against Bill Pay. No model.
A secondary use in the wordsQuickPay's codes say payments; its description says it sells payee data.Rule 5 at the PDP: the model reads the words and quotes them. In the federation's policy, rule 1: QuickPay does not resolve.
Use for other organisationsReceipt Relay sends receipts, and passes payment history on to credit reporting agencies.Rule 7 at the PDP: the quoted words show data going to other organisations. In the federation's policy, rule 1: it does not resolve.
A different taskBillBridge lends the shortfall. The Credit Rating API.Rule 7: outside the purpose map, and the model finds it serves another purpose - a different product, or the bank's own credit decisions.
A helpful extraBillNudge sends bill reminders: not needed, but serving nothing else.Rule 8: held for the customer to approve.
Fits the delegator, not the originDebitLink registers direct-debit mandates: within a payment router's purpose, outside what Alice asked for.Rule 8: held. Only the origin decides.
Too broad to placeMoney Mate helps with anything to do with money.Rule 8: held at the PDP. In the federation's policy it does not resolve.
RevokedThe registry drops PayRoute while its token is in flight, or while a later agent holds a valid one.Rule 1 at the receiver's enforcement point, which resolves every entity afresh. A check of the current actor alone would let the call through.

Built on

Standards where they exist, and named conventions where they do not

Standard or productUsed for
OAuth 2.0 Token Exchange, RFC 8693Each hand-off; act and may_act
Rich Authorization Requests, RFC 9396 §10.2authorization_details_types: what an agent may ask for, in BIAN terms
Protected Resource Metadata, RFC 9728 §2authorization_details_types_supported: what an API serves
OpenID Federation 1.0Entity statements, trust chains, metadata policy, the resolve endpoint (§8.3), metadata_policy_crit (§3.1)
BIAN Service Landscape v14The vocabulary: Service Domains, checked against BIAN's semantic APIs
DPoP, RFC 9449; private_key_jwtSender-constrained tokens; client authentication with keys from the federation
AuthZEN 1.0How the authorization server and every enforcement point ask PingAuthorize: subject = current actor, resource = audience, context = the act chain
PingAuthorize 11.1 · AuthZEN AdapterEmbedded PDP; the purpose map, secondary-use list and ten rules as policy
PingFederateThe authorization server in the hosted demo: token exchange that asks PingAuthorize before it issues

Not to claim

What this design does not do

  • mission, purpose_statement and the describes operator are this design's conventions. OpenID Federation defines the resolve endpoint, description, additional policy operators and metadata_policy_crit, not these. The BIAN-coded metadata, the purpose map and the rules are a design, not a standard.
  • The chain is read whole by design, contrary to RFC 8693 §4.1. It is safe here because only an authorization server in the federation builds a chain, every act.sub must resolve to the trust anchor, and only PingAuthorize's rules read it.
  • The model catches misled or careless delegation, and entities whose words contradict their codes. It does not stop a determined liar: with open weights, an attacker can search offline for a description that passes. That is why the words it reads should be vouched for by a superior.
  • A resolver that applies a model-backed policy becomes something every relier trusts for that check, as it already is for chain resolution. Its signed response proves what resolved; it does not make the model's reading right.
  • Revocation is as fast as reliers re-resolve. The purpose service resolves fresh each time; a relier that caches statements keeps trusting a dropped member until they expire.
  • Error rates need an independent, blind-labelled test set. The figures so far are a feasibility check, and where the hold line falls between a helpful extra and a different task is still the model's call.