Guides
How to Handle OAuth Token Refresh and Expiry for SaaS Integrations at Scale
Every SaaS provider handles OAuth refresh differently. Learn how to manage token expiry, rotation, secure storage, and policy changes at scale.

Garrett Scott
,
Head of Marketing
Paragon Auth handles OAuth token refresh and expiry at scale by owning the per-provider refresh contract entirely: checking expiry before every call, refreshing with a safety margin, retrying once on 401, rotating and storing tokens atomically, and monitoring provider policy changes, so a customer’s connection never breaks silently. Building this yourself is possible: treat each provider-tenant connection as its own token lifecycle, check expiry before each call, refresh with a safety margin, retry once on 401, store rotated tokens atomically, secure them at rest, monitor provider changes, and prompt re-authorization when refresh fails. Below is exactly how that loop works, and where it breaks at scale.
The first OAuth integration you build can feel deceptively simple: redirect the user, exchange the code, store the tokens, make the API call.
Where it gets challenging is when that same flow has to work across dozens of providers, thousands of tenants, background syncs, webhooks, retries, and long-lived customer accounts. This is when OAuth stops being a setup step and becomes a reliability issue. Every provider has its own refresh behavior. Every tenant has its own token state. One expired or mishandled refresh token can break a sync, drop records from a workflow, or break an integration the customer relies on every day.
That’s the real problem with OAuth token refresh at scale: not getting a token once, but keeping every customer’s token valid, secure, and recoverable for as long as the integration exists.
This article covers the refresh loop, rotation risks, secure token storage, provider-change monitoring, and re-authorization paths required to keep OAuth-backed integrations reliable at scale.
OAuth is a standard, and every provider implements it differently
OAuth 2.0 is a framework that leaves refresh behavior deliberately open, and every provider implements it a little differently. Some providers support long-lived refresh tokens, some tie refresh behavior to app-level policy, and others require rotation or additional security controls. As a result, you end up maintaining a slightly different refresh flow for each one, sometimes even across products from the same vendor. Most of the refresh behaviors you’ll encounter follow one of four patterns.
Refresh Behavior | What it Means | What You Have to Handle |
|---|---|---|
Access token, short TTL | Access tokens expire in minutes or hours | Refresh proactively on a margin, or reactively on a 401, before the call fails |
Refresh token never expires | A long-lived refresh token mints new access tokens indefinitely | Long-term secure storage; a rotation path if a token is compromised |
Refresh token expires after N days | The refresh token dies if unused within a window | Track last use; refresh before the window closes so the grant doesn’t lapse |
Refresh token rotates on every use | Each refresh returns a new refresh token; the previous one is invalidated | Persist the newest token atomically; prevent concurrent-refresh races |
None of these are edge cases. They’re the default behavior of major providers you’re already connecting to, and the policy can change under you without warning. “Handle token refresh” isn’t one engineering task. It’s four, multiplied by every provider in your catalog.
Validate token state before each provider call
The core loop in the token refresh lifecycle is simple to describe but easy to get wrong. Before each third-party API call, check whether the token is close to expiring. If it is, refresh and store the new token before calling the provider. If the provider still returns 401, refresh once, store the result, and retry the call.
This proactive path handles predictable expiry before the provider rejects the request. But it won’t catch everything. A provider can revoke a token early, shorten a session, or reject a token because of clock drift or user action. That is why you also need the reactive path: if the API call returns 401, refresh once, store the new token, and retry the request.
That’s two separate paths through the same four operations: one triggered by the expiry check, one triggered by a failed call. Both need to land in the same place: a stored token, then a successful response. Here’s how they diverge and reconverge as a flow.

Before each third-party API call, check whether the access token is close to expiring. Use a proactive refresh (TTL margin check): refresh before the exact expiry time, leaving a buffer for clock drift, latency, retries, or long-running jobs.
That proactive refresh avoids most predictable failures, but it won’t catch everything. A provider can revoke a token early, shorten a session, or reject a token after the end user disconnects the app. That’s why you also need a reactive refresh (a 401 fallback) after the API call: if the provider returns 401, refresh once, store the new token, and retry the call.
In pseudocode, the loop looks like this:
Both paths should end the same way: the newest token is stored, and the API call succeeds. If the provider rotates refresh tokens, that storage step has to be atomic. Otherwise, concurrent refreshes can overwrite each other and leave your system with a refresh token the provider has already invalidated. That concurrent-refresh race is the failure mode to solve next.
Concurrency is the real risk in refresh-token rotation
Refresh-token rotation is safe when one worker refreshes one connection at a time. When a provider rotates refresh tokens on every use, each successful refresh invalidates the token you just used and returns a new one. That is a good security property, but under concurrency, it can leave your system storing a refresh token the provider no longer accepts. If two workers refresh the same connection simultaneously, one can overwrite the other and leave your system storing a refresh token the provider no longer accepts.
This bug usually doesn’t show up in staging. It shows up at scale, under real concurrency, when cron syncs, retry logic, and a user clicking “sync now” all fire against the same tenant. The fix is a single-flight refresh per tenant-provider pair: only one refresh runs for that connection, and the new token is persisted atomically before another worker can use stale credentials. If refresh-token rotation is possible, single-flight refresh and atomic persistence should be treated as correctness requirements, not optimizations.
This is not a niche precaution. Auth0 and Okta both document refresh-token rotation as a recommended pattern for reducing refresh-token risk, and the OAuth 2.1 draft moves public clients toward sender-constrained or one-time-use refresh tokens. If you have not built for rotation yet, plan to: more providers are likely to require it over time.
Token storage is where security breaches actually happen
A stored OAuth token is a live credential into a customer’s business system:
Their CRM
Their support desk
Their HR data
And that’s how you should be treating it: encrypt the token even when it’s at rest, run a real secrets management layer, lock down access so a compromised service can’t read every tenant’s tokens, and pen-test the storage layer regularly. Build a real revocation path too, so a compromised token can be killed on the spot instead of waiting out its TTL.
This is not a place to vibe-code a solution and revisit it later. A compromised token doesn’t just break your integration. It hands an attacker a working session into a customer’s business-critical data, under your name.
A token store breach doesn’t stay contained to one integration. It exposes every tenant whose tokens live in that store, so the risk scales directly with your customer count.
You don’t get a vote when providers change OAuth
OAuth policies are not static, and providers change them on their own schedule. Here are some recent examples, as of this publication, that illustrate how often operational contracts can move:
Zendesk announced default expiration for OAuth access and refresh tokens for global clients beginning February 2, 2026, with local clients scheduled to adopt refresh-token flows by April 1, 2027.
BambooHR deprecated its legacy OpenID Connect login flow for new integrations on April 14, 2025, moving new marketplace apps to OAuth 2.0.
Salesforce announced enforcement changes for connected apps and external client apps, including PKCE and refresh-token rotation requirements, with a May 11, 2026 deadline for affected apps. The update followed a period of heightened concern around malicious connected-app authorization.
Linear migrated OAuth2 applications to a new refresh-token system on April 1, 2026, changing how apps handle access-token expiry and refresh.
None of these were breaking changes to the OAuth spec. They were policy changes inside providers’ own implementations, the kind you only catch by watching changelogs across every provider in your catalog, on their schedule, not yours. Miss one, and that provider’s integrations start failing in customer accounts with no code change on your side to point to.
OAuth maintenance multiplies across integrations and tenants
Every pattern we’ve covered here has to work per tenant, per provider, every time: proactive and reactive refresh, rotation handling, secure storage, and provider-change monitoring. Even a modest provider catalog becomes a matrix of token state: each customer connection has its own expiry behavior, re-authorization path, failure signature, and vendor changelog to track.
At that point, building it yourself means owning the operating model around it. It’s your team that’s responsible for provider monitoring, token failure alerts, re-auth recovery paths, incident response, and concurrency bugs that only appear under production load. Someone owns the on-call burden. Someone reads every provider’s changelog. And someone gets paged when a rotation race takes down a tenant’s connection at 2 a.m.
Offloading token management centralizes refresh, expiry, and storage
The alternative is to stop owning the per-provider refresh contract entirely. Paragon Auth is a managed authentication layer that handles refresh and expiry across supported SaaS integrations and custom integrations, behind a single integration point. This means proactive refresh, reactive fallback, rotation handling, and secure storage are solved once, not once for every provider.
The product ships with an embeddable auth experience, Connect Portal, so your users authorize integrations without you building and maintaining that UI yourself. It also supports on-premise deployment for teams with stricter security requirements around where tokens live.
The point is not to remove OAuth from your architecture. It is to stop rebuilding provider-specific refresh, rotation, storage, re-authorization, and policy-change handling for every integration.
For more on the build-versus-buy tradeoff behind this decision, see our guide to multi-tenant auth for customer-facing integrations and keeping integrations working when third-party APIs change.
FAQ
How do I handle OAuth token refresh and expiry for SaaS integrations at scale?
At scale, OAuth refresh gets hard because every SaaS provider-tenant connection has its own token lifecycle that’s outside of your control. You can handle this by offloading the per-provider refresh contract to a managed auth layer like Paragon Auth. Alternatively, you can manage it yourself by:
Refreshing before expiry
Retrying once on 401
Storing rotated tokens atomically
Securing tokens at rest
Monitoring provider changes
Prompting re-authorization when refresh fails
What happens if an OAuth token expires and isn’t refreshed?
You’ll usually see it in the customer’s account before you see it in your monitoring. The integration just errors out, often without throwing anything you’d catch, unless you’re actively checking for it.
How is OAuth token handling different across SaaS providers?
Because the OAuth spec leaves expiry and rotation open, you’ll find providers use that freedom differently: some refresh tokens never expire, some expire if you don’t use them, and some rotate every time you do. Paragon Auth normalizes these differences behind one integration point, so your app doesn’t maintain four different refresh flows.
What is refresh-token rotation?
Every time you refresh, you get a new refresh token and the old one stops working. Store the newest one atomically, or a second request refreshing at the same time will quietly break the connection.
How do you store OAuth tokens securely at scale?
Encrypt tokens at rest, run a real secrets management layer, lock down access with least privilege, and pen-test the storage layer regularly. Paragon Auth handles this encryption and secrets management as part of its managed layer.
How do you handle a provider changing its OAuth requirements?
Monitor provider changelogs continuously, since policy changes ship without warning. Paragon Auth absorbs these changes centrally, so you’re not re-engineering your integration every time a provider updates its policy.
Should you build OAuth refresh yourself, or hand it off?
Hand it off once you’re past a couple of providers. Paragon Auth handles refresh and expiry across its integration catalog and for custom integrations, so your team isn’t building and maintaining a per-provider refresh contract in-house.




