Code examples: verifying an agent

Illustrative, runnable examples of the verification flow: a principal delegates scoped authority, the agent presents a proof-of-possession credential, the service verifies it, logs the action, and revocation is checked. Code is illustrative, not production-ready.

The examples follow the seven steps in How to verify an agent. They use a running example: an agent (`travelbot`) acting for a principal (`alice@example.com`) to make a booking on `travel.example.com`. Replace hosts, credentials and library calls with your own stack; the point is the shape of the checks, not the specific SDK.

Not production-ready. These snippets omit error handling, key management, retries and hardening. Do not copy them into production unchanged.

What each example shows

CapabilityWhat the code demonstratesStandards
DelegationExchanging the principal’s token for a scoped, agent-specific token that records both actor and subjectRFC 8693
Proof of possessionBinding a token to the agent’s key so a stolen token cannot be replayedRFC 9449, RFC 8705
Audit loggingAppending a tamper-evident, hash-chained record of who acted, for whom and whenAppend-only logging practice
Revocation checkAsking the authorization server whether a token is still active before actingRFC 7662, RFC 7009

curl

Delegation

“`bash

Exchange the principal’s token for a scoped, agent-specific token (RFC 8693).

curl -s https://auth.example.com/token \ -d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \ -d subject_token=”$USER_ACCESS_TOKEN” \ -d subject_token_type=urn:ietf:params:oauth:token-type:access_token \ -d actor_token=”$AGENT_ASSERTION” \ -d actor_token_type=urn:ietf:params:oauth:token-type:jwt \ -d requested_token_type=urn:ietf:params:oauth:token-type:access_token \ -d scope=”bookings:write” “`

Proof of possession

“`bash

Present a DPoP-bound token to the service (RFC 9449).

curl -s https://travel.example.com/bookings \ -H “Authorization: DPoP $ACCESS_TOKEN” \ -H “DPoP: $DPOP_PROOF” \ -d ‘{“trip”:”HEL-BCN”,”max_eur”:1200}’ “`

Audit logging

“`bash

Record the action with actor, principal, decision and credential reference.

curl -s https://audit.example.com/events \ -H “Authorization: Bearer $AUDIT_TOKEN” \ -d ‘{“actor”:”spiffe://kya.fi/agents/travelbot”, “principal”:”alice@example.com”, “action”:”bookings:write”, “decision”:”allow”, “ts”:”2026-10-03T20:00:00Z”}’ “`

Revocation check

“`bash

Ask the authorization server whether the token is still active (RFC 7662).

curl -s https://auth.example.com/introspect \ -u “$CLIENT_ID:$CLIENT_SECRET” \ -d token=”$ACCESS_TOKEN” “`

Python

Delegation

“`python

Delegation: request a scoped, on-behalf-of token for the agent (RFC 8693).

import requests

resp = requests.post( “https://auth.example.com/token”, data={ “grant_type”: “urn:ietf:params:oauth:grant-type:token-exchange”, “subject_token”: user_token, # the principal “subject_token_type”: “urn:ietf:params:oauth:token-type:access_token”, “actor_token”: agent_assertion, # the agent “actor_token_type”: “urn:ietf:params:oauth:token-type:jwt”, “requested_token_type”: “urn:ietf:params:oauth:token-type:access_token”, “scope”: “bookings:write”, }, ) scoped_token = resp.json()[“access_token”] “`

Proof-of-possession verification

“`python

verify signature, audience, actor and scope before allowing the action

def verify(token, proof, request): assert token.aud == “travel.example.com” assert token.act == “spiffe://kya.fi/agents/travelbot” # the agent assert token.sub == “alice@example.com” # the principal assert request[“max_eur”] <= token.scope["max_eur"] return True ```

“`python

The DPoP proof must be signed by the key the token is bound to (RFC 9449).

def verify_dpop(token, proof, request): jwk = verify_jws(proof, typ=”dpop+jwt”) assert jwk[“thumbprint”] == token.cnf[“jkt”] # token bound to this key assert proof[“htm”] == request.method assert proof[“htu”] == request.url assert proof[“jti”] not in seen_jti # replay check “`

Audit logging

“`python

append-only, hash-chained audit entry

import hashlib, json, time

entry = { “actor”: “spiffe://kya.fi/agents/travelbot”, “principal”: “alice@example.com”, “action”: “bookings:write”, “at”: time.time(), “prev”: head, } entry[“hash”] = hashlib.sha256(json.dumps(entry, sort_keys=True).encode()).hexdigest() audit.append(entry) head = entry[“hash”] “`

Revocation check

“`python

Check the token is still active before acting (RFC 7662 introspection).

r = requests.post( “https://auth.example.com/introspect”, auth=(client_id, client_secret), data={“token”: token}, ) if not r.json().get(“active”, False): raise PermissionError(“token revoked or expired”) “`

JavaScript / TypeScript

Delegation

“`ts // Delegation: exchange for a scoped, on-behalf-of token (RFC 8693). const body = new URLSearchParams({ grant_type: “urn:ietf:params:oauth:grant-type:token-exchange”, subject_token: userToken, subject_token_type: “urn:ietf:params:oauth:token-type:access_token”, actor_token: agentAssertion, actor_token_type: “urn:ietf:params:oauth:token-type:jwt”, requested_token_type: “urn:ietf:params:oauth:token-type:access_token”, scope: “bookings:write”, }); const { access_token } = await fetch(“https://auth.example.com/token”, { method: “POST”, body, }).then((r) => r.json()); “`

Proof of possession

“`ts // Sign the request with the agent’s key so the token cannot be replayed (RFC 9449). const proof = await new SignJWT({ htm: “POST”, htu: url, jti: crypto.randomUUID() }) .setProtectedHeader({ alg: “ES256”, typ: “dpop+jwt”, jwk: publicJwk }) .setIssuedAt() .sign(privateKey); await fetch(url, { method: “POST”, headers: { Authorization: `DPoP ${accessToken}`, DPoP: proof }, }); “`

Audit logging

“`ts // append-only, hash-chained audit entry const entry = { actor, principal, action, at: Date.now(), prev: head }; entry.hash = sha256(JSON.stringify(entry)); audit.append(entry); “`

Revocation check

“`ts // Revocation check before acting (RFC 7662). const { active } = await fetch(“https://auth.example.com/introspect”, { method: “POST”, headers: { Authorization: `Basic ${basic}` }, body: new URLSearchParams({ token }), }).then((r) => r.json()); if (!active) throw new Error(“token revoked or expired”); “`

Go

Delegation

“`go // Delegation: token exchange (RFC 8693). form := url.Values{ “grant_type”: {“urn:ietf:params:oauth:grant-type:token-exchange”}, “subject_token”: {userToken}, “subject_token_type”: {“urn:ietf:params:oauth:token-type:access_token”}, “actor_token”: {agentAssertion}, “actor_token_type”: {“urn:ietf:params:oauth:token-type:jwt”}, “scope”: {“bookings:write”}, } resp, err := http.PostForm(“https://auth.example.com/token”, form) “`

Proof-of-possession verification

“`go // Verify the DPoP proof is bound to the token’s key (RFC 9449); illustrative only. thumb, err := jose.Thumbprint(proofJwk) if err != nil || thumb != token.ConfirmationKey { return ErrProofMismatch } “`

Audit logging

“`go // Audit trail: append-only, hash-chained entry. entry := AuditEntry{Actor: actor, Principal: principal, Action: action, Prev: head} sum := sha256.Sum256(mustJSON(entry)) entry.Hash = hex.EncodeToString(sum[:]) if err := audit.Append(ctx, entry); err != nil { return err } head = entry.Hash “`

Revocation check

“`go // revocation check before acting active, err := introspect(ctx, token) // RFC 7662 if err != nil || !active { return ErrRevoked } “`

Full reference implementations (Python, JavaScript/TypeScript, curl, Go) and an interactive walkthrough live in the project repository under `examples/`.

FAQ

Are these examples production-ready?

No. They are deliberately short to show the shape of each check. They omit error handling, key management, retry/backoff, clock-skew tolerance and hardening. Treat them as a starting sketch, not a library.

Which standard covers delegation versus impersonation?

OAuth 2.0 Token Exchange (RFC 8693) defines the request and token semantics, including the distinction between impersonation and delegation. It is designed so a downstream service can tell who is acting and on whose behalf.

How is proof of possession enforced?

For sender-constrained OAuth tokens, DPoP (RFC 9449) binds the token to a key the client holds and the server verifies per request; mutual-TLS (RFC 8705) binds the token to a client certificate instead. Either way, a stolen bearer token alone is not enough to act.

How do I check revocation?

Introspection (RFC 7662) lets a resource server ask the authorization server whether a token is still active; the revocation endpoint (RFC 7009) lets a client invalidate a token and, where applicable, other tokens from the same grant. Short-lived credentials reduce reliance on revocation lists because they expire on their own. See scoped credentials and the audit trail in the glossary, and why agent identity is new for the reasoning behind these controls.

Sources


Know Your Agent (KYA) explains agent identity, verification and accountability. This is an explainer, not legal or compliance advice — see our Sources & methodology. New to KYA? Start with What is Know Your Agent? and the glossary.