Repository navigation
feat(delegate): a service can act for a person, and the token says so (AUDIT-6) - #109
Merged
Merged
Conversation
… (AUDIT-6) POST /delegate exchanges a person's authn JWT, with the calling service's own projected ServiceAccount token, for a short-lived JWT that keeps the person as sub, username and groups (minus krateo:loa:) and names the service in RFC 8693's act claim, nested for a chain. Shaped like RFC 8693 token exchange. Snowplow derives user, on_behalf_of and delegation for its AuditEvent from this verified token instead of client-set baggage. - ServiceAccount mappings opt in with spec.delegation.allowed; maxDepth (default 3, 1-8) caps the chain. - The subject token must be authn's own, under its current or a previous key; the issued token lives min(15m, the subject's expiry). - Service authentication is shared with /serviceaccount/login (serviceaccount.Authenticate), so its causes apply unchanged. - plumbing v1.30.0 carries the act claim; it requires Go 1.26, so go.mod and the builder image move to 1.26. Design: docs/design/agent-delegation.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TLFnUeufsniBTiPtg82fDb
/delegate read the subject token's exp with jwt.ParseUnverified after validating it, which CodeQL flags (go/missing-jwt-signature-check). plumbing v1.31.0's ValidateClaimsWithKeySource returns the verified claims, expiry included, in one parse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TLFnUeufsniBTiPtg82fDb
This was referenced Nov 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the accepted AUDIT-6 design (
docs/design/agent-delegation.md, #107; installer#412).What it adds:
POST /delegate, shaped like RFC 8693 token exchange.Request:
Authorization: Bearer <the service's projected SA token, audience authn>, authenticated exactly as on/serviceaccount/login, throughserviceaccount.Authenticate, now shared.grant_type=urn:ietf:params:oauth:grant-type:token-exchange,subject_token_type=urn:ietf:params:oauth:token-type:jwtandsubject_token=<a person's authn JWT>.Opt-in: the service's
ServiceAccountmapping must setspec.delegation.allowed: true.maxDepthdefaults to 3 (range 1–8) and caps the actor chain.Subject: it must be authn's own token, under the current or a previous key, and not expired.
Answer:
{access_token, issued_token_type, token_type: "Bearer", expires_in}, withCache-Control: no-store. The issued JWT:sub,usernameandgroups, withoutkrateo:loa:*act: {sub: <service>}, nested for a chainmin(15m, subject exp)No kubeconfig is minted.
New causes, all documented in
docs/api.md:UnsupportedGrantType,UnsupportedSubjectTokenandMissingSubjectToken(400),SubjectTokenInvalid(401),DelegationNotAllowedandDelegationTooDeep(403), plusIssuanceFailed. Form errors are answered before the apiserver is called.Dependencies: plumbing v1.14.0 → v1.30.0, which adds
jwtutil.Actor(plumbing#98). It requires Go 1.26, sogo.modgoes 1.25.6 → 1.26.0 and the Dockerfile buildergolang:1.25-trixie→golang:1.26-trixie. The rest of the suite passes on v1.30.0 unchanged.CRD:
ServiceAccount.spec.delegation, regenerated, and mirrored into theauthn-crdschart. There's no RBAC change: TokenReview and the mapping list were already granted.Tests:
internal/routes/delegate/delegate_test.gocovers:TestPublicSurfacenow pinsPOST /delegateand probes it.Docs:
api.md(route, CRD and failures table),behavior.md,log.md, and the design doc's status set to implemented with its deltas.Out of scope, per Diego: the apiserver-level audit. Snowplow still dials as the person.
Intended release: 0.37.0. Draft #91 moves to the next minor.
🤖 Generated with Claude Code
https://claude.ai/code/session_01TLFnUeufsniBTiPtg82fDb