Short answer: Retry the transport operation, never the OAuth meaning: keep one durable authorization attempt, accept its callback once, and make every downstream effect replayable from recorded state.
For a B2B SaaS account-deletion flow, I would block new sessions before attempting remote cleanup, because deleting data while a surviving session can still act is the more dangerous ordering.
That is the architecture decision.
It treats a timeout as missing knowledge, not proof of failure.
A callback may have committed even when the browser received no response; a token exchange may have reached the other side even when the connection closed; an account deletion may be retried by a worker after its first lease expires.
The recovery design must therefore answer a narrow question at each boundary: do we know the operation did not happen, do we know it happened, or is the result still unknown?
What must remain true during OAuth failure recovery?
The first invariant is that an authorization attempt has one identity independent of any HTTP request.
Store an opaque flow identifier, the expected callback state, the account or tenant context, a creation time, an expiry, and a small state machine such as , , , or .
Don't let a browser refresh create a second logical attempt merely because it creates a second request.
The second invariant is single consumption.
An authorization code and its state belong to one attempt; the callback handler must atomically claim that attempt before triggering side effects.
A duplicate callback should read the previously recorded outcome and return the same application-level destination.
It must not provision the user again, issue another internal session, or append a second audit event that claims a second login.
Exactly once is the goal, but HTTP cannot promise it by itself, so I use an exactly-once mindset at the business boundary: an atomic database transition establishes who owns the work, unique constraints reject duplicate effects, and an outbox carries committed work to asynchronous consumers.
Delivery can still be at least once.
Effects cannot.
For auditability, record transitions rather than raw secrets.
The useful trail says that flow moved from to , that callback attempt observed a transport timeout, and that a later reconciliation read the stored outcome.
Authorization codes, tokens, and session cookies do not belong in logs.
This also keeps a GDPR deletion record useful after the personal fields it once referred to have been erased: retain the minimum compliance evidence your policy permits, under a pseudonymous operation identifier, and separate it from authentication material.
One boundary deserves special treatment.
Account deletion and session revocation are not a single remote call.
In the local transaction, mark the account as deletion-pending, advance or invalidate its session generation, reject future token refreshes, and enqueue cleanup.
Only then should workers revoke remote grants and erase dependent data.
The exact retention period and legal basis vary by jurisdiction and contract; I'm not sure a universal duration exists, so counsel and the data inventory must settle that part rather than the retry loop.
Stop access first.
How should OAuth failure recovery retry authorization and callback steps?
Classify failures by evidence, not by the layer that printed an error.
A connection timeout is ambiguous.
A validation rejection is terminal for that attempt.
A browser abandonment is neither success nor protocol failure; it is an expired attempt that can be closed without inventing an error.
This distinction prevents the common mistake of putting the entire authorization flow inside a generic exponential-retry wrapper.
Step or observation Safe action Why Authorization redirect was not delivered to the browser Re-render a link for the same unexpired attempt, or explicitly start a new attempt after expiring it A redirect is user navigation, not a background command to repeat blindly Callbac