Skip to main content

Api

Test mid-flow token expiry without rotating real API keys

Happy-path mocks never expire a token between step 1 and step 3. Your production client does. Here's how to force a mid-flow 401 on purpose — without burning partner credentials.

FetchSandbox EngineeringFetchSandbox Engineering

Most auth tests prove one thing: you can get a token and call an endpoint. That is the easy half.

The hard half is what happens when the token dies between steps. Create a customer with a valid session. Pause. Resume. Attach a payment method. The second call returns 401. Your retry logic either refreshes cleanly, or it reuses a dead bearer and loops until the partner rate-limits you for looking like an attack.

Happy-path mocks never expire anything mid-flow. They return whatever status you hardcoded. So the refresh path ships untested — and first runs in production when a long checkout or a multi-step agent workflow spans a short-lived JWT.

Why this bug is invisible in CI

A typical integration test:

  1. Mint (or stub) an access token
  2. Call POST /customers
  3. Call POST /payment_methods with the same token
  4. Assert 201

Every step uses the same immortal stub. Nothing in that suite exercises:

  • 401 on step 3 after a successful step 1
  • refresh-token rotation that invalidates the previous access token
  • a clock-skew window where the partner thinks the JWT is expired and you don't

Code review cannot see this. The types are fine. The mock is green. The agent that wrote the client followed the "get token, then call API" docs.

Force the failure on purpose

You need a sandbox that can inject auth failure at a named step — not "always 401" and not "never 401."

With the FetchSandbox MCP server in Cursor or Claude Code, the ask looks like:

./fetchsandbox run my Clerk-authenticated create-then-update flow.
Succeed on the create. Inject auth_failure / expired token on the update.
Assert we refresh once, retry the update, and do not hammer the API with the dead bearer.

The run receipt shows the first call succeeding, the injected 401, the refresh, and whether the retry used a new token. If your client retries the original bearer three times before refreshing, that shows up in the timestamps — in a URL you can paste into the PR.

Provider sandboxes that model session lifecycle (Clerk and others) matter here because the failure shape is provider-specific: some return 401 with a JSON code, some return 403, some want you to hit a refresh endpoint before any retry. A generic mock that always returns { error: "unauthorized" } trains the wrong handler.

The rule

Auth that only works for a single request is not integrated. If your product does anything that takes more than one authenticated call — checkout, invite accept, webhook reconcile with a follow-up read — expire the token between calls once, on purpose, before production does it for you at 2am.