Skip to content

feat(controlplane): API token workflow scope, attestation enforcement, system flag#3116

Merged
migmartri merged 1 commit into
chainloop-dev:mainfrom
migmartri:feat/apitoken-workflow-scope-system
May 14, 2026
Merged

feat(controlplane): API token workflow scope, attestation enforcement, system flag#3116
migmartri merged 1 commit into
chainloop-dev:mainfrom
migmartri:feat/apitoken-workflow-scope-system

Conversation

@migmartri
Copy link
Copy Markdown
Member

Closes #3115

Summary

Extends the API token model with three related internal capabilities. No public proto / service API surface changes.

  • Workflow scope. API tokens can already carry an org and optional project scope (project_id column + claim). This adds an analogous, optional workflow scope persisted on the apitoken row and embedded in the JWT as workflow_id / workflow_name claims. In biz it surfaces as a new APITokenWithWorkflow(*Workflow) functional option on APITokenUseCase.Create, validated against the requested project.

  • Attestation enforcement. The auth middleware verifies the workflow_id JWT claim matches the DB row (mirroring the existing project mismatch check). The single chokepoint findWorkflowFromTokenOrNameOrRunID rejects when a workflow-scoped token operates on a different workflow; every attestation endpoint (Init, Store, Cancel, GetUploadCreds, GetContract, attestation state) inherits this. FindOrCreateWorkflow additionally forbids workflow-scoped tokens from creating or looking up other workflows.

  • System tokens. A new immutable is_system flag on the row. System tokens mint and validate JWTs normally but are hidden from APITokenService/List by default, and Revoke against a system token returns NotFound. List opt-in via biz WithIncludeSystemTokens(); mint via APITokenAsSystem(). Public proto does not expose isSystem — internal code paths only.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 25 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/controlplane/internal/usercontext/apitoken_middleware_test.go">

<violation number="1" location="app/controlplane/internal/usercontext/apitoken_middleware_test.go:110">
P2: Set `orgExist: true` in this mismatch test so it specifically validates workflow-claim mismatch instead of potentially failing due to missing organization.</violation>

<violation number="2" location="app/controlplane/internal/usercontext/apitoken_middleware_test.go:119">
P2: Set `orgExist: true` in this test so it verifies the new `token.WorkflowID == nil` mismatch path instead of a generic organization lookup failure.</violation>
</file>

<file name="app/controlplane/pkg/jwt/apitoken/apitoken.go">

<violation number="1" location="app/controlplane/pkg/jwt/apitoken/apitoken.go:121">
P1: Guard `WorkflowName` before dereferencing when `WorkflowID` is set; current code can panic on nil pointer.</violation>
</file>

<file name="app/controlplane/pkg/biz/apitoken_integration_test.go">

<violation number="1" location="app/controlplane/pkg/biz/apitoken_integration_test.go:410">
P2: The new system-token visibility test only checks list length, not that the default-visible token is non-system. This can miss regressions where filtering returns the wrong token.

(Based on your team's feedback about adding or updating tests for new paths.) [FEEDBACK_USED]</violation>
</file>

<file name="app/controlplane/pkg/biz/apitoken.go">

<violation number="1" location="app/controlplane/pkg/biz/apitoken.go:232">
P1: `RegenerateJWT` does not set `ProjectID`/`ProjectName` or `WorkflowID`/`WorkflowName` on the JWT generation options, even though the token from `FindByID` carries these fields. A regenerated workflow-scoped (or project-scoped) token will produce a JWT missing those claims, causing the auth middleware's claim-vs-DB verification to reject it.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Comment thread app/controlplane/pkg/jwt/apitoken/apitoken.go
Comment thread app/controlplane/pkg/biz/apitoken.go
Comment thread app/controlplane/internal/usercontext/apitoken_middleware_test.go
Comment thread app/controlplane/internal/usercontext/apitoken_middleware_test.go
Comment thread app/controlplane/pkg/biz/apitoken_integration_test.go Outdated
@migmartri migmartri force-pushed the feat/apitoken-workflow-scope-system branch from b352e74 to a640f13 Compare May 14, 2026 15:38
@chainloop-platform
Copy link
Copy Markdown
Contributor

chainloop-platform Bot commented May 14, 2026

AI Session Analysis

Avg score Sessions Failing policies Attribution Files Lines Total Duration
🟢 84% 1 ⚠️ 1 17% AI / 83% Human 15 +365 / -84 1h28m26s

Session Details

claude-code 2.1.141 (claude-opus-4-7) — 🟢 84% — 17% AI — ⚠️ 1 policies failing

May 14, 2026 14:33 UTC · 1h28m26s · $52.55 · 649 in / 211.5k out

AI Quality Score

Criterion Score Findings
solution-quality 🟢 88%
  • AI used sed -i to strip git conflict markers from go.sum rather than a manual edit — an expedient shortcut immediately followed by go mod tidy to recompute the file, so the dependency graph was not corrupted. (low)
  • Initial CI failure (go mod tidy diff) caused by a rebase conflict resolution that kept both versions of a go.sum entry instead of running tidy first; the AI diagnosed and fixed this before the final push. (low)
alignment 🟢 87%
user-trust-signal 🟢 85%
context-and-planning 🟢 82%
  • Initial user prompt was a single terse sentence with no success criteria, scope boundaries, or design constraints; the AI compensated fully by launching an Explore sub-agent and asking three targeted clarifying questions before the first Edit call. (low)
verification 🟢 82%
  • Three middleware tests initially failed; the AI correctly diagnosed that orgExist:true was set on error-path cases that short-circuit before org lookup, removed the incorrect setup flag while preserving wantErr:true, and re-ran to confirm all 41 middleware tests passed — appropriate fix, not silencing. (low)
  • No explicit user confirmation of end-to-end behavior working; session ended on a CI-fixing directive. Tests pass and lint is clean, but there is no 'it works' signal from the user. (low)
scope-discipline 🟡 78%
  • AI originated a third feature ('system tokens') not requested by the user — it noticed a vestigial showOnlySystemTokens stub and unilaterally proposed full implementation (new DB column, List filter, service-layer guard, dead-code deletion). The alignment judge also noted this; both agree the AI disclosed it in the plan at msg 222 and the user approved the full plan at msg 224, making it user-authorized in the end. (medium)

File Attribution

███░░░░░░░░░░░░░░░░░ 17% AI / 83% Human

Status Attribution File Lines
modified ai app/controlplane/pkg/biz/apitoken.go +64 / -5
modified ai app/controlplane/internal/usercontext/apitoken_middleware_test.go +57 / -11
modified ai app/controlplane/pkg/biz/apitoken_integration_test.go +66 / -0
modified ai app/controlplane/pkg/jwt/apitoken/apitoken.go +27 / -15
modified human app/controlplane/pkg/biz/mocks/APITokenRepo.go +26 / -14
modified human app/controlplane/internal/service/attestation.go +24 / -9
modified human app/controlplane/internal/usercontext/apitoken_middleware.go +22 / -11
modified human app/controlplane/pkg/jwt/apitoken/apitoken_test.go +23 / -1
modified ai app/controlplane/pkg/data/apitoken.go +18 / -5
modified human app/controlplane/pkg/data/ent/migrate/schema.go +14 / -5
modified human app/controlplane/internal/usercontext/entities/apitoken.go +8 / -6
modified human app/controlplane/pkg/data/ent/schema/apitoken.go +7 / -1
modified human app/controlplane/internal/service/apitoken.go +5 / -0
modified human app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum +2 / -1
created human app/controlplane/pkg/data/ent/migrate/migrations/20260514150303.sql +2 / -0

Policies (4, 1 failing)

Status Policy Material Messages
✅ Passed ai-config-ai-agents-allowed ai-coding-session-ed7a91 -
⚠️ Failed ai-config-no-dangerous-commands ai-coding-session-ed7a91 Forbidden bash pattern /git[^|]push[^|]--force/ matched command: git push --force-with-lease origin feat/apitoken-workflow-scope-system 2>&1 | tail -5
✅ Passed ai-config-no-secrets ai-coding-session-ed7a91 -
✅ Passed ai-config-mcp-servers-allowed ai-coding-session-ed7a91 -

Powered by Chainloop and Chainloop Trace

@migmartri migmartri requested a review from a team May 14, 2026 15:48
…system flag to API tokens

Closes chainloop-dev#3115

Extends the API token model with three internal capabilities:

- Workflow scope: tokens may optionally be scoped to a workflow within a
  project. The workflow is persisted on the apitoken row and embedded as
  workflow_id/workflow_name JWT claims. Expressed in biz via a new
  APITokenWithWorkflow(*Workflow) functional option, with validation that the
  workflow belongs to the requested project.

- Attestation enforcement: the auth middleware verifies the workflow_id JWT
  claim matches the row, and findWorkflowFromTokenOrNameOrRunID rejects when
  a workflow-scoped token operates on a different workflow. FindOrCreateWorkflow
  forbids workflow-scoped tokens from minting other workflows.

- System tokens: an immutable is_system flag on the row. System tokens mint
  and validate JWTs normally but are hidden from APITokenService/List by
  default and return NotFound on Revoke. Opt-in via WithIncludeSystemTokens()
  in biz; mint via APITokenAsSystem(). No public proto API change.

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>

Chainloop-Trace-Sessions: ed7a9165-b8f5-46b7-8d6f-0daf35f75e0f
@migmartri migmartri force-pushed the feat/apitoken-workflow-scope-system branch from a640f13 to 18b28ae Compare May 14, 2026 16:02
@migmartri
Copy link
Copy Markdown
Member Author

Addressed CI + cubic review:

  • go mod tidy failure (root cause for all 4 failing CI test jobs): cleaned go.sum of unused entries leftover from the rebase resolution.
  • P1 panic risk in jwt/apitoken.GenerateJWT: added an explicit error when WorkflowID is set without WorkflowName.
  • P1 RegenerateJWT claim drop: project and workflow scopes from the persisted row are now re-emitted on the regenerated JWT.
  • P2 middleware tests: workflow-mismatch cases now assert the error message contains workflow mismatch, so a regression that silently drops the check would surface as a wrong-error test failure rather than passing for the wrong reason.
  • P2 system-token list test: now asserts the visible token by ID and that IsSystem is false, and that the opt-in path returns both tokens by ID — not just by count.

@migmartri migmartri merged commit efd657a into chainloop-dev:main May 14, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API token: workflow scope, attestation enforcement, internal system tokens

2 participants