跳到正文
DEV Community· Jeff·· 3 小時前AI 評分57

智能體永久快取短效 access token,令應用程式在 token 到期後回傳 401

The agent fetched the access token once at boot and cached it forever, so rotation broke the app hours later

AI 導讀

智能體在程序啟動時只取得一次第三方 billing API 的短效 access token,並將其永久快取,令正式環境在 token 約一小時後到期時開始回傳 401。文章建議首次使用時才取得 token、記錄到期時間並在到期前刷新;若收到 401,刷新後只重試一次並重放請求。測試應覆蓋 token 過期及刷新流程,並由單一 provider 集中處理 token 換取和刷新。

正文

Jeff

We asked the agent to wire an outbound call to a third-party billing API. It read the auth guide, obtained a client-credentials token, and added a module-level constant: fetch the token once at startup, store it, reuse it for every request. The tests passed against a stub that always returned 200. The deploy went fine.

Around an hour later — the token's typical lifetime — production calls started returning 401, and restarting the process fixed it until the next rotation. The agent had modeled a short-lived token as a static credential, like an API key that never changes.

Why the happy path wins

The SDK guide shows one token exchange and one request. The agent copies that sequence: exchange credentials, hold the result, attach it to the next call. Nothing in the example mentions an expiry timestamp, a refresh, or a retry on 401, so nothing in the implementation appears.

  • The tests stub the auth layer. A fake provider returns a valid token forever, so expiry never reaches the suite. Green proves the call shape, not the credential's lifetime.
  • A long-running process is foreign to a short session. The agent runs against a script that exits; "fetch once" reads as a tidy optimization rather than a correctness bug.
  • Nothing fails at boot. A clean local run and green CI confirm a design that only breaks after uptime, which the agent never observes.

The result is code that works for the first hour of every process and degrades silently after that. A restart masks it, which makes the failure look flaky rather than structural.

Store the token with a lifetime

A token is a value that expires; the secret used to obtain it is what rotates. Keep that distinction and model the expiry instead of ignoring it.

  • Cache the token, not the exchange. Fetch lazily on first use rather than at import, store the returned expiry, and refresh a short window before it expires instead of waiting for a 401.
  • Refresh on 401 once. When a token expires early or a clock drifts, retry the exchange once and replay the request; do not let a single expired token surface as a hard failure to the caller.
  • Test the expiry path. Force an expired token in the suite and assert the provider refreshes and the request still succeeds. A test that only feeds a fresh token never exercises the branch that matters in production.
  • Keep one token provider. Put exchange, expiry, and refresh behind a single provider instead of re-implementing the fetch at each call site; scattered copies drift the fastest.
  • Rotate the secret, not the token. The client secret is the long-lived credential; the token it buys is short-lived and should be disposable.

Fetched once is not cached forever

Green after a clean deploy proves the request works against a stub, not that the credential survives an hour of uptime. When the agent holds a token for the process lifetime, the first failure lands after rotation, disguised as a flaky 401 that a restart hides. Model the expiry, refresh before it, and test the branch the happy path never reaches.

來源:DEV Community · dev.to