A useful audit trail must reconstruct whose authority applied, which software acted, what was approved, and what actually changed. A single username field cannot represent that model.
Create a verified request-scoped identity
At authentication, construct an immutable context from validated claims or trusted introspection: subject with issuer, current actor, prior actor history, OAuth client, transport peer, delegation reference, token identifier, granted constraints, and policy version. Propagate it through authorization and business-event logging. Do not use a mutable global user context across concurrent requests or workers.
For delegated tokens, the outermost act identifies the current actor; nested actors represent earlier actors. Prior actors are informational rather than an additional permission source. Preserve history for traceability without authorizing from a union of actor privileges.
A proposed structured business event
{
"event": "case.update",
"identity_mode": "user_delegation",
"effective_subject": {
"issuer": "https://identity.example.org",
"id": "user:lisa"
},
"current_actor": {
"id": "spiffe://agents.example.org/agent/xyz"
},
"transport_peer": "spiffe://services.example.org/tool/case-editor",
"oauth_client_id": "agent-xyz-client",
"delegation_id": "approval-81a6",
"token_id": "token-4b781",
"request_id": "req-a740",
"agent_run_id": "run-93a2",
"resource": "case/4821",
"authorization": {
"decision": "allow",
"policy_version": "case-policy-17"
},
"outcome": "committed"
}
These field names are recommendations, not a standards-defined event schema. A run ID helps correlation but does not prove identity. A token ID helps join events but does not make a token single-use. One-time semantics require protected state and atomic consumption at the appropriate transaction boundary.
One approval, five API calls
| Call | Observed behavior | Expected investigation conclusion |
| 1 | Read case/4821 under approved read authority | Authorized read, if included in approval |
| 2 | Update status on case/4821 | Authorized mutation and committed result |
| 3 | Update another field on case/4821 | Denied field-level violation |
| 4 | Update case/9920 | Denied resource mismatch |
| 5 | Repeat one-time status update | Denied replay or recorded idempotent retry under policy |
Join the approval event, STS decision, API decision, and business commit. A 200 response in an agent trace does not independently prove a database commit. An authorization allow does not prove the operation succeeded. Conversely, a failed response after a commit can cause a retry; the audit model must distinguish this from deliberate duplicate execution.
Background work and provenance
Queue messages should reference a protected grant and carry authenticated provenance. Re-evaluate permission when the worker executes. Record the originating agent and current worker separately, because the worker may become the current actor in a new token exchange. Preserve request and job correlation without trusting identity fields supplied by arbitrary producers.
Mark log fields by provenance: token-verified, authorization-service-issued, infrastructure-derived, or application-supplied. This prevents an agent-provided display name from being mistaken for the verified SPIFFE actor. Use stable identifiers for joins and display names only for presentation.
Logging controls
- Never log bearer tokens, private keys, or full authorization headers.
- Redact business payloads and approval details according to sensitivity.
- Record denied attempts as well as successful operations.
- Protect log transport, administrative access, and retention configuration.
- Synchronize clocks and retain event time separately from ingestion time.
- Version audit schemas and ensure downstream parsers preserve nested identity context.
- Record configuration/policy changes so investigators can reconstruct the decision in force.
Takeaway: attribution becomes useful only when it survives all the way to the committed business event.