Skip to content

feat(broker): per-user credential-brokering audit records (spec 074 T10)#694

Open
Dumbris wants to merge 1 commit into
mainfrom
074-t10-broker-audit
Open

feat(broker): per-user credential-brokering audit records (spec 074 T10)#694
Dumbris wants to merge 1 commit into
mainfrom
074-t10-broker-audit

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 16, 2026

Copy link
Copy Markdown
Member

Spec 074 T10 — Audit integration: per-user brokering activity records

Closes the per-user attribution payoff (User Story 4). Emits a secret-free activity-log record on every per-user credential operation — acquisition, refresh, injection, connect — carrying attribution (user id, server, request id), the acquisition method (token_exchange / entra_obo / connect), and the outcome (success/failure + a coarse reason). No token/refresh/secret value is ever recorded (FR-028, FR-029, SC-006).

Design

  • broker.AuditEvent / broker.AuditSink (internal/serveredition/broker/audit.go): a structurally secret-free port — the event struct has no field able to hold a credential, so auditing cannot leak a secret. Default is a no-op sink.
  • CredentialResolver.Resolve emits exactly one classified event per resolution. The action (acquire / refresh / inject cache-hit / connect not-connected) is threaded out of the single-flight acquire so success and failure are attributed correctly. Exchanger errors are already sanitized upstream; NotConnectedError/PolicyDeniedError/sentinels are coarse and secret-free.
  • OAuthConnector.Complete / Deny emit the connect-flow event (the connect completion path is REST-driven and never flows through Resolve). Failure reasons are fixed coarse labels, never the raw upstream token-endpoint body. Refresh stays unaudited at the connector — the resolver owns refresh auditing (no double-count).
  • storage.ActivityTypeCredentialBroker + an activity-log-backed adapter (api.NewActivityAuditSink) using SaveActivityAsync (non-blocking on the request path). This reuses the existing activity log. Wired into the live connect flow via NewCredentialHandlersconnectorProviderNewOAuthConnector.

Note: the broker's injection path (ResolveInjectFor) is not yet wired into production (a later task). The audit port is in place on the resolver so injection/acquire/refresh auditing flows automatically once that wiring lands; it is unit-tested now with a fake sink. The connect flow is live today and audited end-to-end.

Tests (TDD)

  • audit_resolver_test.go — cache-hit→inject, fresh→acquire, near-expiry→refresh, entra_obo method mapping, exchange-failure (reason, no secret), policy-denied→inject failure, not-connected→connect failure, store-disabled, anonymous (no event).
  • audit_connector_test.go — connect Complete success/failure (coarse reason, no upstream body), invalid-state, Deny; asserts no upstream token / client secret in any event.
  • broker_audit_test.go — adapter persists a real credential_broker record with attribution + request_id and no secret; nil-storage → nil sink.

Verification

  • go build ./cmd/mcpproxy (personal) + go build -tags server ./cmd/mcpproxy
  • go test -tags server ./internal/serveredition/... -race
  • go test ./internal/storage/... ./internal/runtime/... -race -short
  • golangci-lint v2.5.0 --config .github/.golangci.yml --new-from-rev=origin/main0 issues

Docs

  • docs/features/activity-log.md and docs/cli/activity-commands.md updated with the new credential_broker activity type.

Related spec 074 (blockers MCP-1039/T6, MCP-1040/T7 merged in #691).

Emit a secret-free activity-log record on every per-user credential
operation — acquisition, refresh, injection, and connect — carrying
attribution (user id, server, request id), the acquisition method, and the
outcome (success/failure + coarse reason). No token/refresh/secret value is
ever recorded (FR-028, FR-029, SC-006; User Story 4 payoff).

- broker.AuditEvent/AuditSink: a structurally secret-free audit port (no
  field can hold a credential) with a default no-op sink.
- CredentialResolver.Resolve emits one classified event per resolution
  (acquire / refresh / inject / connect), threading the action out of the
  single-flight acquire so success and failure are attributed correctly.
- OAuthConnector.Complete/Deny emit the connect-flow event; failure reasons
  are fixed coarse labels, never the raw upstream token-endpoint body.
- storage.ActivityTypeCredentialBroker + an activity-log-backed AuditSink
  adapter (SaveActivityAsync, non-blocking) that reuses the existing
  activity log; wired into the live connect flow via NewCredentialHandlers.

TDD: resolver/connector emit correct attribution on success and failure and
never leak token material; the adapter persists records with no secret.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 10eec4d
Status: ✅  Deploy successful!
Preview URL: https://ba48e9ee.mcpproxy-docs.pages.dev
Branch Preview URL: https://074-t10-broker-audit.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: 074-t10-broker-audit

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (25 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 27603208239 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

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.

2 participants