Skip to content

Capture RFC 8693 act claim in audit logs - #6046

Merged
JAORMX merged 2 commits into
mainfrom
audit-act-claim-6035
Jul 28, 2026
Merged

Capture RFC 8693 act claim in audit logs#6046
JAORMX merged 2 commits into
mainfrom
audit-act-claim-6035

Conversation

@JAORMX

@JAORMX JAORMX commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

The embedded authorization server already mints delegated JWTs with the RFC 8693 act claim and supports nested act for multi-level delegation (pkg/authserver/server/tokenexchange/handler.go). However, when those tokens are presented to the proxy / vMCP request-serving path, audit events only recorded the terminal sub (the original user). It was impossible to distinguish "Alice did X" from "Alice's coding agent did X on Alice's behalf" — the exact forensic gap RFC 8693 is designed to close.

This change captures the full delegation chain in every audit event for requests authorized by a delegated token:

  • Adds auth.DelegationChain / auth.ParseDelegationChain to parse nested act claims with a bounded depth (default 10, aligned with the token-exchange issuance cap) and explicit truncation reporting (dropped_count).
  • Promotes the parsed chain into auth.Identity / auth.PrincipalInfo during claimsToIdentity so downstream consumers see it as a structured field rather than an opaque JWT claim.
  • Extends audit.AuditEvent with a delegationChain member and teaches both the HTTP Auditor and WorkflowAuditor to populate it from the request identity, respecting the configured maxDelegationDepth.
  • Covers issuance → validation → audit extraction with unit tests, middleware-level tests (including SSE stream opens), and an end-to-end integration test that mints a delegated token via the real token-exchange handler and asserts the audit event.

Closes #6035

Type of change

  • New feature

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)

API Compatibility

  • This PR does not break the v1beta1 API, OR the api-break-allowed label is applied and the migration guidance is described above.

Changes

File Change
pkg/auth/delegation.go New DelegationChain, DelegatedActor, ParseDelegationChain, DefaultMaxDelegationDepth
pkg/auth/identity.go Add DelegationChain *DelegationChain to PrincipalInfo and MarshalJSON
pkg/auth/context.go Parse act claim into identity during claimsToIdentity
pkg/audit/event.go Add DelegationChain field and WithDelegationChain builder; emit in LogTo
pkg/audit/config.go Add MaxDelegationDepth *int and MaxDelegationDepthOrDefault()
pkg/audit/auditor.go Extract and attach delegation chain in logAuditEvent and logSSEConnectionEvent
pkg/audit/workflow_auditor.go Attach delegation chain in all workflow/step audit methods
pkg/authserver/integration_test.go End-to-end test: mint delegated token, run through audit+auth middleware, assert chain
pkg/audit/auditor_test.go Unit tests for extraction, depth cap, SSE path
pkg/audit/workflow_auditor_test.go Unit tests for workflow delegation chain
pkg/auth/*_test.go Tests for parsing, identity population, JSON marshaling
docs/middleware.md Document delegation_chain audit field
docs/arch/02-core-concepts.md Add delegation chain to audit event field list
docs/server/* Regenerated swagger/CRD docs

Does this introduce a user-facing change?

Yes. Audit events for requests authorized by delegated tokens now include a delegation_chain field that lists the acting parties (outermost first) and a truncated / dropped_count indicator when the chain exceeds the configured depth cap. The maxDelegationDepth audit config knob (default 10) is now available.

Implementation plan

Approved implementation plan
  1. Add shared DelegationChain / ParseDelegationChain type in pkg/auth.
  2. Promote act into Identity / PrincipalInfo during claimsToIdentity.
  3. Extend AuditEvent with delegationChain while keeping Subjects backward compatible.
  4. Populate in both HTTP and workflow auditors with configurable depth cap.
  5. Add unit, middleware, SSE, and integration tests.
  6. Update operator-facing docs and regenerate swagger/CRD docs.

Special notes for reviewers

  • The depth cap (10) is intentionally aligned with maxDelegationDepth in pkg/authserver/server/tokenexchange/handler.go:31 and maxClaimNestingDepth in pkg/authz/authorizers/cedar/entity.go:18.
  • The integration test proves the middleware chain alone publishes the identity to the audit holder; the stub handler does not re-publish.
  • SetLogWriterForTest is marked deprecated in favor of a future NewAuditorWithWriter constructor.

@github-actions github-actions Bot added the size/XL Extra large PR: 1000+ lines changed label Jul 27, 2026
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.32%. Comparing base (33023da) to head (7d0d474).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
pkg/audit/auditor.go 94.44% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6046      +/-   ##
==========================================
+ Coverage   72.26%   72.32%   +0.06%     
==========================================
  Files         722      725       +3     
  Lines       75138    75443     +305     
==========================================
+ Hits        54296    54567     +271     
  Misses      16980    16980              
- Partials     3862     3896      +34     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JAORMX
JAORMX requested review from blkt and jerm-dro as code owners July 27, 2026 19:06
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Jul 27, 2026
JAORMX added 2 commits July 28, 2026 10:16
Add first-class support for the RFC 8693 actor claim in ToolHive's
audit pipeline so delegated requests record the full chain of acting
parties, not just the terminal subject.

- Introduce auth.DelegationChain and auth.ParseDelegationChain to
  parse nested act claims with a bounded depth (default 10, aligned
  with the token-exchange issuance cap) and explicit truncation
  reporting (dropped_count).
- Promote the parsed chain into auth.Identity / auth.PrincipalInfo
  during claimsToIdentity so downstream consumers see it as a
  structured field rather than an opaque JWT claim.
- Extend audit.AuditEvent with a delegationChain member and teach
  both the HTTP Auditor and WorkflowAuditor to populate it from the
  request identity, respecting the configured maxDelegationDepth.
- Cover issuance → validation → audit extraction with unit tests,
  middleware-level tests (including SSE stream opens), and an
  end-to-end integration test that mints a delegated token via the
  real token-exchange handler and asserts the audit event.
- Document the new audit field and maxDelegationDepth config knob;
  regenerate operator CRD/swagger docs.

Closes #6035
Run task operator-manifests and task crdref-gen to pick up the new
maxDelegationDepth audit config field introduced in #6035.
@JAORMX
JAORMX force-pushed the audit-act-claim-6035 branch from 4dc3335 to 7d0d474 Compare July 28, 2026 07:17
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Jul 28, 2026
@JAORMX
JAORMX merged commit e79d623 into main Jul 28, 2026
82 of 83 checks passed
@JAORMX
JAORMX deleted the audit-act-claim-6035 branch July 28, 2026 10:33
JAORMX added a commit that referenced this pull request Jul 28, 2026
Main gained CRD-affecting changes (#6092, #6046, #6086) after the
previous merge, so the Generate CRD Docs check fails against the
merge result until docs/operator/crd-api.md is regenerated on top.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y5WXPQr5Da9Cox2nZWhg6n
JAORMX added a commit that referenced this pull request Jul 28, 2026
All eight WorkflowAuditor.Log* methods hand-copied the same
four-step prologue (extract source, extract subjects, build the
event, attach the delegation chain). PR #6046 had to patch all
eight copies by hand and six of them were not covered by tests,
which is how call sites drift.

Extract a private newEvent(ctx, eventType, outcome) helper and
route every Log* method through it, so attaching the delegation
chain is guaranteed by construction rather than by repetition.
No behavior change.

Shrink the eight-case delegation test table to a single test on
the helper; the no-identity and max-depth subtests keep pinning
the emitted log shape end-to-end.

Closes #6095


Claude-Session: https://claude.ai/code/session_01W1kKpsGZTfSgKBi8GdTZXR

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Extra large PR: 1000+ lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit logs must capture RFC 8693 act (actor) claim and full delegation chain

2 participants