Refresh-token rotation should be tested as a state transition with concurrent callers, not just as a successful token endpoint request. Separate access-token expiry, refresh-token lifetime, reuse handling and disconnect. Use disposable credentials in an isolated server fixture, record outcomes by synthetic labels and verify the durable token-family state without placing raw credentials in the result report.
Decide the credential-family contract
Write what one successful refresh does: which previous credential becomes unusable, which replacement is issued, and how the server treats reuse. Also record the access token's expiry and any revocation checks. A rotated refresh credential does not necessarily invalidate an already issued access token immediately. The provider's documented behavior determines that boundary, and the product must describe disconnect accurately.
Cloudflare's Workers OAuth provider documents its authorization-server behavior, including credential lifecycle. Use it as a reference for that implementation rather than a universal description of every MCP authorization server. Pin the package version and inspect the configured storage and transaction semantics before asserting concurrency behavior. A helper named rotate does not by itself establish atomic single use under two workers.
Workers OAuth provider authorization-server lifecycle
Race the same disposable credential deliberately
Create a test account and one refresh credential R0. Synchronize two local refresh requests so both attempt R0 before either test client adopts a replacement. Define the allowed outcomes from your provider policy in advance. Some systems permit only one winner; others document a bounded reuse or grace mechanism. The suite must verify the chosen behavior, not label a documented grace policy a defect.
| Case | State to inspect | Recovery contract |
|---|---|---|
| Two requests use R0 | Replacement and family status | Adopt allowed replacement |
| Response lost after rotation | Committed rotation outcome | Provider-defined reconnect |
| Expired refresh credential | No usable replacement | Reauthorize |
| Disconnect | Refresh and access boundaries | Describe exact residual lifetime |

The table is a proposed fixture design and has not been run against a provider here. Inspect database state or the provider's administrative test interface after the race. Two responses alone may miss a replacement that was issued but lost, a family revoked after reuse detection, or an orphaned token still valid in storage. Keep the external response and durable credential state as separate assertions.
Do not make clients amplify a normal race
A host can trigger simultaneous refresh from multiple conversations or background requests. Where appropriate, coordinate refresh through a single in-flight operation per connection and atomically adopt the replacement. If a request receives a reuse refusal, it should not loop with the same credential indefinitely. Distinguish a stale client holding R0 from an account whose family was revoked, then follow the provider's reconnect policy.
Synthetic audit fields
connection_id: fixture-connection
credential_label: R0 (never the token value)
attempts: refresh-A, refresh-B
response_class: accepted / refused / provider-defined grace
family_state_after: inspect in isolated store
next_client_action: adopt replacement or reconnectCancellation deserves its own case. If the host closes a token request after the server commits rotation, it may never receive R1. A retry with R0 can then look like reuse even though the user did nothing suspicious. Your recovery contract should account for that possibility without weakening the documented security policy. Surface a reconnect when necessary and avoid blaming the user for an ambiguous network outcome.
Specify what disconnect invalidates
List the items affected by disconnect: local host storage, server-side refresh credentials, related grants and already issued access tokens. If an access token remains usable until expiry, describe that time boundary and its operational owner. For sensitive administrative actions, application authorization can still check current membership at execution. Removing the local connector alone should not be presented as proof that every credential everywhere disappeared.
Test expiration with a controllable fixture clock and test revocation before and after a successful rotation. Add a request using a replacement from a different connection to verify binding where the implementation promises it. Keep token fingerprints out of public material unless a reviewed diagnostic need justifies them; synthetic labels are normally sufficient. A useful release artifact identifies package version, policy, race sequence and final state, giving future upgrades a repeatable compatibility gate.
MCP server authentication: trace the identity all the way to the record
Evaluate the tool action, not just the answer.
Bring one tool workflow, supported hosts and a sanitized failure trace for a scoped AI integration review.
AptixLabs ai agent development
What should an MCP refresh-token rotation test cover?
Cover concurrent refresh, old-token reuse, expiry, response loss and disconnect, then verify both client recovery and the provider’s durable credential-family state.
Does refresh rotation revoke an existing access token?
Not necessarily. Check the provider’s access-token lifetime and revocation behavior separately.
Should both concurrent refresh attempts always fail except one?
Use the provider’s documented policy; some implementations define a bounded grace or reuse mechanism.
