Skip to content

fix(ui): guard SSO redirect to stop 401 retry loop - #28807

Open
geojaz wants to merge 2 commits into
argoproj:masterfrom
geojaz:fix/sso-redirect-loop-24807
Open

fix(ui): guard SSO redirect to stop 401 retry loop#28807
geojaz wants to merge 2 commits into
argoproj:masterfrom
geojaz:fix/sso-redirect-loop-24807

Conversation

@geojaz

@geojaz geojaz commented Jul 20, 2026

Copy link
Copy Markdown

Fixes #24807

When an OIDC session expires, each open watch stream emits a 401 into the shared requests.onError subject. subscribeUnauthorized reassigns window.location.href on every one of them, so each 401 cancels the previous in-flight navigation to the identity provider. The browser never finishes leaving the page, and the streams keep reconnecting and re-emitting 401s.

This adds a module-level flag so only the first 401 triggers the redirect.

The check and the assignment are deliberately adjacent, immediately before window.location.href. await isExpiredSSO() earlier in the handler yields the event loop, so a guard placed at the top of the function would let several handlers past before any of them set the flag, which would leave the race intact.

No explicit reset is needed. The redirect is a full page load, so the module state is discarded on the way back in.

Only the SSO branch is guarded. The local-account branch uses history.push and is already covered by the existing pathname checks.

What I saw

I hit this myself on v3.4.4. A tab I had left open on an application view lost its session at 00:43 UTC. At 01:16 the streams started retrying and did not stop until I completed a fresh login at 01:30, with the last failure logged at 01:36. Over that roughly 20 minute window argocd-server logged 26,522 Failed to verify session token warnings and the load balancer in front of it logged 38,649 requests to the argocd-server backend. That works out to somewhere between 1,300 and 1,900 requests per minute from a single browser tab.

Every one of those warnings carried the same token expiry timestamp, which is what pointed at the redirect race rather than at ordinary session churn.

My setup is dex with the Google connector, behind GCP IAP and GKE ingress. The earlier reports on the issue were Okta, so this looks identity-provider agnostic, which matches the code path.

Not included

watch() in applications-service.ts still uses argument-less .pipe(repeat()).pipe(retry()), so the streams keep reconnecting without backoff until login completes. That is a separate concern and #24807 discusses it as a follow-up.

Checklist

  • Commit message follows the conventional format and is signed off per the DCO.
  • pnpm lint passes (tsc --noEmit --project ./src/app && eslint), 0 errors.
  • No test added. There is no existing test for app.tsx which would require setting up mocks for a root React component and an RxJs subject... but if you really want a test just LMK.

Signed-off-by: Eric Hole <eric.hole@gmail.com>
@geojaz
geojaz requested review from a team as code owners July 20, 2026 02:28
@bunnyshell

bunnyshell Bot commented Jul 20, 2026

Copy link
Copy Markdown

❗ Preview Environment deployment failed on Bunnyshell

See: Environment Details | Pipeline Logs

Available commands (reply to this comment):

  • 🚀 /bns:deploy to redeploy the environment
  • /bns:delete to remove the environment

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 132.44kB (1.19%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
argo-cd-ui-array-push 11.25MB 132.44kB (1.19%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: argo-cd-ui-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
main.*.js 139.28kB 3.71MB 3.9%
4952.*.chunk.js -2 bytes 3.0MB -0.0%
3494.*.chunk.js (New) 6.33kB 6.33kB 100.0% 🚀
2315.*.chunk.js (Deleted) -13.17kB 0 bytes -100.0% 🗑️

@dudinea dudinea left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geojaz Thank you for your PR,

After some hard trying I've succeeded to reproduce the issue,
like @trv-bmpage in #28649 I used OIDC against Okta with custom auth server, enablePKCEAuthentication: true, offline_access + refreshTokenThreshold.

Your PR seems to fix the problem, with it I still see bunch of 401 errors from argocd-server when the token expires, but this no more causes the "Too many requests" error from Okta.

I still see two problems with the handling of timeout:

  1. There is a chance that the flag might get stuck: is for any reason the redirect fails (some network failure or timeout) the flag will stay true and the page won't do redirect until user reloads the page.
  2. While reload happens (and that may take some time), the API server continues to be severly hammered by UI requests which continuously get error 401 and then immediately retry the same operation. Probably once the redirect started all the other requests should be cancelled and only redirect should be retried. Or generic backoff mechanism should be implemented.

I see the former as a bigger, user-visible problem that can be easily fixed in this PR, something like setTimeout(()-> {ssoRedirectInProgress = false}, 5000) when setting the ssoRedirectInProgress to true.

The second one probably is a generic problem, not specifically related to SSO and should be dealt with as a separate issue.

geojaz added a commit to geojaz/argo-cd that referenced this pull request Aug 13, 2026
Addresses review feedback on argoproj#28807: if the redirect navigation fails,
the guard flag would stay true and block any future SSO redirect until
a manual page reload. Reset it after 5s so a later 401 can retry.
Addresses review feedback on argoproj#28807: if the redirect navigation fails,
the guard flag would stay true and block any future SSO redirect until
a manual page reload. Reset it after 5s so a later 401 can retry.

Signed-off-by: Eric Hole <ehole@onixnet.com>
@geojaz
geojaz force-pushed the fix/sso-redirect-loop-24807 branch from 0d26484 to fddea9f Compare August 13, 2026 17:48
@geojaz

geojaz commented Aug 13, 2026

Copy link
Copy Markdown
Author

Thanks for taking the time to reproduce this.

  1. Ack. When the flag is set, a setTimeout now resets ssoRedirectInProgress back to false after 5 seconds. If the redirect fails to navigate away for any reason, a later 401 can retry the redirect instead of being blocked until a manual page reload.

  2. ack 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Excessive never-ending retry loop of /api/v1/stream/applications and /api/v1/stream/applications/<app>/resource-tree when OIDC token has expired

2 participants