Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 3 min read

Mocking Token Expiry Based on Clock Time, Not Request Count

Quick answer: An access token expires at a specific moment, not after a set number of calls, so mock it by comparing the current time against the token's expiry rather than counting requests. Issue a short-lived token, l

Quick answer: An access token expires at a specific moment, not after a set number of calls, so mock it by comparing the current time against the token's expiry rather than counting requests. Issue a short-lived token, let the mock return a 401 once the clock passes that point, and then exercise the refresh-and-retry flow. This keeps the test faithful to how real expiry works while removing the need to wait hours for a live token to age out.

Why request count is the wrong trigger

A tempting shortcut is to make the mock fail the third request and call that a token-expiry test. It is not. A real token carries an expiry timestamp, and the server rejects it based on the wall clock, regardless of how many requests you made. A count-based mock passes even if the client never reads the expiry, sends the wrong refresh grant, or fails to retry, because the trigger has nothing to do with time. It also cannot test the boundary where a token is valid one second and rejected the next. Tie the mock's decision to time so the behavior under test matches the mechanism in production.

Mock expiry against the clock

Issue a token with issued-at and expiry values, and have the mock compare the current time to that expiry on every authenticated call. Use a short lifetime or a clock you can advance so you do not sit through a real wait, and cover the points below.

Clock point Token state Mock response Client action
Before expiry Valid 200 Send the request normally
At or after expiry Expired 401 Call the refresh endpoint
After a successful refresh New token 200 Retry the original request
Refresh token also expired Invalid 401 on refresh Re-authenticate or sign out

The last row is worth keeping: a client that assumes refresh always succeeds is left in a loop when the refresh grant itself is rejected.

The refresh-and-retry flow to assert

The full sequence is a normal request, a 401 after expiry, a refresh call that returns a new access token, and a retry of the original request with the new token. Assert that the retry carries the new token rather than the old one and that the original response is returned to the caller. If several requests are in flight when the token expires, confirm the client refreshes once and queues the rest rather than firing many refresh calls. Inspect the recorded requests to verify the order and the headers each step used. A desktop API workspace lets you set these time-based overrides, advance the scenario, and assert on the responses in the same place. Powerduck works this way, so the expiry path is deterministic instead of a waiting game.

Honest limits

A local mock runs in the desktop main process and does not replace the real authorization server, so the token and its expiry rule are ones you define. The mock does not prove how a real identity provider handles clock skew, token signing, refresh-token rotation, or revocation, and it does not validate a real cryptographic signature unless you wire that in. Keep a small set of requests against the live auth service to confirm the actual 401 behavior and refresh contract. Importing a schema does not deploy a backend, and a passing mock run is not evidence about the real issuer.

Frequently asked questions

Why not just fail the third request to simulate expiry? Because real tokens expire on a timestamp, not a call count. A count-based mock misses boundary timing and can pass even when the client never handles the expiry correctly.

How do I avoid waiting for a token to expire? Use a short token lifetime or a clock the mock can advance, so the moment of expiry arrives on demand without real waiting.

What should happen after the 401? The client calls the refresh endpoint, receives a new access token, and retries the original request with that new token. If the refresh grant is rejected, it should re-authenticate rather than loop.

Does the mock verify a real token signature? No, unless you explicitly configure it. It reproduces the time-based decision, so confirm signature and issuer behavior separately against the live service.

The bottom line

Judge token expiry on the clock, not on a request counter, because that is the rule the real server applies. Mock the valid, expired, refreshed, and refresh-rejected cases, assert the full 401, refresh, and retry sequence, and verify the live identity provider separately. For more on this approach, see the API mocking guide hub.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.