Salesforce RTR Requires Concurrency-Safe Token Storage for Multi-Worker ISVs

Salesforce's refresh token rotation invalidates every active token if two workers refresh concurrently. The single-writer pattern with shared storage prevents this multi-worker failure mode.
Salesforce's May 11, 2026 OAuth security mandate added Refresh Token Rotation alongside the Refresh Token IP Allowlist requirement. Both controls together create a problem most AppExchange ISVs haven't faced before: a distributed-systems concurrency problem at the token storage layer. The static IP requirement is the easy half. The concurrency control is the half that catches people.
This post is for ISVs running OAuth Authorization Code flows from server-side infrastructure with more than one worker process. If your integration is purely JWT Bearer flow (no refresh tokens at all), the concurrency problem doesn't apply to you. If you're a user-facing AppExchange ISV with customer OAuth grants, keep reading.
Concurrent Refresh Attempts Trigger Salesforce's Stolen-Token Response
Refresh Token Rotation invalidates the old refresh token the moment a new one is issued. That's the documented behavior. The undocumented part is what happens when two workers present the same refresh token within milliseconds of each other.
The Salesforce-focused engineering blog Beyond The Cloud has documented this failure mode in detail. Once a refresh token has been rotated, presenting the old one is treated by Salesforce as evidence of a stolen-token incident. The response isn't a simple token invalidation. Salesforce can invalidate every active refresh token for that user-app pair as a precaution. See Beyond The Cloud's analysis of the May 11 deadline for the specific quote.
The error response on a stale refresh token is HTTP 400 with error: "invalid_grant". That's documented in Salesforce's OAuth Refresh Token Flow documentation. What's not documented is whether the cascade behavior is universal or situational. Treat it as the failure mode you need to design against.
Unlike Auth0 or Okta, Salesforce does not provide a grace period or overlap window for concurrent refresh attempts. There's no "we'll accept the previous token for 30 seconds after rotation" buffer. The first concurrent request succeeds. The second is treated as suspicious. The distributed-systems problem is yours to solve.
The Single-Writer Pattern Eliminates Concurrent Redemption
The most robust solution is to ensure only one worker process ever attempts to redeem a given customer's refresh token. Other workers read the current access token from shared storage but never touch the refresh endpoint themselves.
The pattern works like this:
Worker A (designated token-keeper for customer X):
- Periodically checks token expiration
- Calls /services/oauth2/token when refresh is needed
- Atomically writes (access_token, refresh_token, expires_at) to Redis
Worker B, C, D:
- Read access_token from Redis
- Use it for API calls to Salesforce
- On 401 response, signal Worker A to refresh (or wait briefly and re-read)
- Never call /services/oauth2/token directly
Implementing the designation layer is the harder part. Two common approaches:
Consistent hashing or service discovery. Each customer's token is assigned to a specific worker based on a hash of the customer ID. Workers know which customers they own. Other workers route refresh signals through a queue.
Leader election via Redis or Consul. One worker holds the refresh lock for all customers. If it dies, another worker takes over. Simpler to implement but creates a single point of failure under load.
The single-writer pattern is the most resilient against Salesforce's RTR cascade because it makes concurrent redemption structurally impossible.
Database Row Locks Are the Pragmatic Alternative for Existing Stacks
If your integration already uses a relational database for customer state, row-level locks are easier to bolt on than the single-writer pattern.
The pattern uses SELECT ... FOR UPDATE on the customer's token row before any refresh attempt:
BEGIN;
SELECT access_token, refresh_token, expires_at
FROM customer_tokens
WHERE customer_id = :customer_id
FOR UPDATE;
-- If token is expired, call /services/oauth2/token here
-- Other workers attempting SELECT ... FOR UPDATE on the same row will block
UPDATE customer_tokens
SET access_token = :new_access,
refresh_token = :new_refresh,
expires_at = :new_expires
WHERE customer_id = :customer_id;
COMMIT;
Two workers attempting refresh on the same customer will serialize through the row lock. The second worker waits for the first to commit, then reads the new token and uses it instead of attempting its own refresh.
This pattern survives process restarts (the lock is in the database, not in worker memory), works across multiple servers without coordination, and is well-understood by every engineer who's written transactional code. The trade-off is slightly higher latency on first request after token expiration, since the holding worker has to make a network call to Salesforce inside the transaction.
Library Support: jsforce, simple_salesforce, restforce, and Managed Platforms
None of the major Salesforce client libraries handle multi-worker concurrency out of the box. They expose refresh hooks that you have to wrap with your own locking.
| Library | Language | Refresh Hook | Multi-Worker Ready? |
|---|---|---|---|
| jsforce | Node.js | Built-in refresh event listener |
No. Wrap the event handler in a distributed mutex. |
| simple_salesforce | Python | Manual via SalesforceLogin(refresh_token=...) |
No. Implement your own lock and retry around the call. |
| restforce | Ruby | Auto refresh via authentication_callback |
No. Use the callback to acquire a lock before persisting. |
| Nango | External platform | Automatic, handles concurrency internally | Yes, but adds an external service dependency. |
If you're starting fresh and don't want to build the concurrency layer yourself, a managed OAuth platform like Nango handles it for you in exchange for the dependency. If you're working in an existing codebase, the library-plus-lock pattern is the pragmatic path.
Static IPs Don't Solve Salesforce's Behavior Detection at Scale
The Refresh Token IP Allowlist is one of two network-layer controls in the May 11 mandate. The other is IP-based monitoring. Salesforce's security infrastructure observes patterns, not just allowlisted IPs.
For ISVs at scale, this matters. An AppExchange integration that routes many customer org tokens through one outbound IP creates a network pattern that resembles a stolen-token attack in shape: one source IP, many customer tokens, many target orgs. Salesforce's security monitoring may flag this pattern even when the IP is allowlisted and the implementation is technically correct.
This isn't a hypothetical concern. ISVs running large customer bases through QuotaGuard should expect to engage Salesforce support proactively about their integration architecture. The recommended posture is to surface the pattern to Salesforce in advance rather than wait for monitoring to flag it.
QuotaGuard tip: Dedicated IPs on QuotaGuard Shield Enterprise improve reputation issues that come from sharing an outbound IP with other customers. Shared IPs can carry traffic patterns from unrelated workloads that affect how Salesforce's monitoring scores your traffic. A dedicated pair eliminates that concern. What dedicated IPs do not solve is the underlying behavior pattern (one IP, many tokens, many orgs) that Salesforce's security infrastructure can detect regardless of IP exclusivity. Treat dedicated IPs as a reputation improvement, not a guarantee that Salesforce won't flag the pattern.
Anti-Patterns to Avoid
Four implementation patterns reliably cause RTR cascades or related token management failures.
Naive Redis caching without locks. Multiple workers read a stale or expired token from shared cache simultaneously. Each attempts an independent refresh. The first succeeds. The rest present the now-rotated old token and trigger Salesforce's stolen-token response. This is the most common cause of cascade failure in distributed ISV codebases. Add a lock around the refresh operation, not just the cache read.
In-process memory locks across multiple servers. Python threading.Lock or Node.js mutex libraries protect against threading races inside a single process. They do nothing across multiple processes or servers. Multi-worker ISVs need distributed locks (Redis, ZooKeeper, database) not in-process locks.
Blind retry on failed refresh. When a refresh fails, the temptation is to retry. If the failure was an HTTP 400 invalid_grant because another worker already rotated the token, retrying with the same stale token will trigger more cascade signals. Don't retry refresh failures without first re-reading the token from shared storage.
Refresh on every API call. Some implementations refresh the access token before every Salesforce API call rather than checking expiration first. This multiplies refresh volume unnecessarily and increases the surface area for concurrent redemption collisions. Check the cached access token's expiration and only refresh when it's actually expired or close to it.
QuotaGuard Static Pricing Starts at $19/Month
Bandwidth is bundled. No per-GB overage fees. Salesforce OAuth refresh traffic is typically low bandwidth (small JSON payloads on /services/oauth2/token), so most ISVs fit comfortably in the Starter or Production tiers. Dedicated IPs are available on Enterprise and above. On lower tiers, your two assigned IPs are still static, but shared with other customers.
QuotaGuard Shield Pricing Starts at $29/Month
Shield costs slightly more than Static at each tier because SSL passthrough adds routing overhead. For Salesforce ISVs specifically, the compliance coverage Shield provides matters most when your integration handles regulated customer data inside Salesforce (PHI in healthcare-focused listings, payment data, regulated PII). If your integration is purely OAuth metadata, Static is sufficient.
All plans include a 3-day trial. Enterprise plans include a 7-day trial. Credit card required.
See the full pricing table at quotaguard.com/products/pricing.
Get a Static IP Plus a Concurrency-Safe Token Pattern
The May 11 mandate makes the static IP a hard requirement. The concurrency control is a hard requirement too, just one Salesforce doesn't enforce directly. The ISVs that survive RTR without cascading token revocations are the ones that solve both problems together.
QuotaGuard handles the IP layer. The single-writer pattern or database row lock handles the concurrency layer. Both need to be in place before you scale customer onboarding through your AppExchange listing.
For the IP layer, see the Salesforce Connected App static IP setup guide for the External Client Apps Manager configuration. For dedicated IPs at scale, see QuotaGuard Shield Enterprise tier. If your integration handles regulated data, Shield is the right product regardless of plan tier.
If you're scoping this work for a production AppExchange listing and want to talk through your specific architecture, contact us and an engineer will respond directly.
QuotaGuard Static IP Blog
Practical notes on routing cloud and AI traffic through Static IPs.





