feat: cache the OAuth access token across invocations - #140
Conversation
Every c1i process minted a fresh client_credentials token before its first request, writing one OCSF Authentication event to the tenant's audit log each invocation. Measured live, 12 sequential commands produced 12 events; agent workflows are long sequences of one-shot processes, so this floods customers' system logs. The same 12 commands now produce 1. Cache the minted token in a 0600 file under the user config dir, keyed by host + client id, reused until it nears expiry (60s skew, applied to both the on-disk and in-memory tiers). Only the bearer c1i attaches automatically (the REST client) is cached; auth token and the MCP gateway still mint fresh, since they hand the bearer to the caller. A cached token the server rejects -- clock skew past the skew window, or a server-side revocation -- is dropped and re-minted once via a 401-triggered retry, so a poisoned entry cannot 401 every invocation until local expiry. Opt out with C1I_NO_TOKEN_CACHE=1. The cached token is strictly shorter-lived than the client secret already stored beside it and grants nothing that secret could not re-mint on demand, so it widens no exposure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Review findings — changes required before live acceptance validation:
|
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
What
Cache the OAuth access token across c1i invocations so a run of one-shot
commands mints — and audit-logs — a token once instead of every time.
Why
Every c1i process minted a fresh
client_credentialstoken before its firstrequest, and C1 records each mint as an OCSF Authentication event (class 3002)
in the tenant's system log. Agent workflows are long sequences of one-shot
processes, so c1i was writing one authentication event per command into
customers' audit logs / SIEMs.
Measured live on a lab tenant (audit log = ground truth, filtered to this
CLI's own client id, polled to stabilization to beat ingestion lag):
client_credentialseventsHow
NewCachingTokenSourcewraps the mint with an on-disk cache: a0600fileunder the user config dir, keyed by
sha256(host \0 client-id), writtenatomically (temp-per-PID + rename). Tiers: in-memory → disk → mint.
auth tokenand the MCP gateway keep minting fresh via the uncachedNewTokenSource, since they hand the raw bearer to the caller — soauth token's "not written to disk; minted each invocation" contract holds.skew, or a server-side revocation) is dropped and re-minted once via a
401-only retry (
retryOnTokenReject), so a poisoned entry can't 401 everyinvocation until local expiry.
C1I_NO_TOKEN_CACHE=1.Security
The cached token is strictly shorter-lived than the client secret already
stored beside it and grants nothing that secret couldn't re-mint on demand, so
it widens no exposure. (Owner decision: token-at-rest ≈ the client-id/secret
pair at rest.)
Testing
(11 injected defects, all caught — one surfaced a vacuous test, since fixed
to isolate the in-memory tier).
auth token2 grants(uncached); corrupt cache → mint, expired → re-mint, perms
0600.govulncheck,
go mod tidy.cleared it. Two earlier review findings (a skew mismatch and the
auth tokencontract) were fixed before this PR.
Deferred (non-blocking, LOW)
one, and 401-for-authz is server-dependent).
--url) are never reaped.*.tmp<pid>file (never read).🤖 Generated with Claude Code