Skip to content

fix: use OIDC refresh tokens to renew expired sessions (#27041, #12189)#27777

Open
ris-tlp wants to merge 3 commits intoargoproj:masterfrom
ris-tlp:fix/oidc-refresh-tokens
Open

fix: use OIDC refresh tokens to renew expired sessions (#27041, #12189)#27777
ris-tlp wants to merge 3 commits intoargoproj:masterfrom
ris-tlp:fix/oidc-refresh-tokens

Conversation

@ris-tlp
Copy link
Copy Markdown

@ris-tlp ris-tlp commented May 10, 2026

Issues Addressed

Bug Summary

Currently, the OIDC refresh flow in ArgoCD is not able to refresh sessions due to two separate bugs:

  • The OAuth2 token cache TTL in util/oidc/oidc.go is tied to the ID token's lifetime. When the ID token has expired, the refresh token is evicted at the exact moment the refresh flow would need it. The refresh token was not being given a chance to be used. This scenario happens even if the user is active in the UI.
  • When a token has already expired, the verification flow in server/server.go getClaims returns Unauthenticated and exits before the OIDC refresh logic can run. This scenario happens when a user has successfully logged in but has not carried out any actions in the UI until after the token has expired. The session doesn't get refreshed because the refresh is on demand, i.e., whenever a user does something on the UI.

Fix Summary

  • Extending Token Cache TTL to UserSessionDuration: The OAuth2 token wrapper containing the refresh token is now cached for the UserSessionDuration rather than GetTokenExpiration(claims)/time.Until(token.Expiry) in util/oidc/oidc.go.
  • Additional flow to attempt refresh grant on expired token: If the token has already expired, an attempt to renew the session is made through CheckAndRefreshToken before returning Unauthenticated and exiting out early in server/server.go.

Reproducing the bugs with Keycloak

  1. Keycloak realm configuration in ~/keycloak-realm.json
    {
      "realm": "argocd",
      "enabled": true,
      "accessTokenLifespan": 120,
      "ssoSessionIdleTimeout": 86400,
      "ssoSessionMaxLifespan": 86400,
      "clients": [{
        "clientId": "argocd",
        "enabled": true,
        "publicClient": false,
        "secret": "argocd-client-secret",
        "redirectUris": ["http://localhost:8080/auth/callback",
  "http://localhost:4000/auth/callback"],
        "webOrigins": ["+"],
        "standardFlowEnabled": true,
        "directAccessGrantsEnabled": true,
        "protocol": "openid-connect"
      }],
      "users": [{
        "username": "test",
        "enabled": true,
        "email": "test@example.com",
        "emailVerified": true,
        "credentials": [{"type": "password", "value": "password", "temporary":
  false}]
      }]
    }
  1. Start Keycloak with the realm configuration and additional log levels to view refresh_grant events in docker logs
docker run -d --name keycloak \
      -p 8090:8080 \
      -v ~/keycloak-realm.json:/opt/keycloak/data/import/realm.json \
      -e KEYCLOAK_ADMIN=admin \
      -e KEYCLOAK_ADMIN_PASSWORD=admin \
      quay.io/keycloak/keycloak:25.0 start-dev --import-realm \
      --log-level=info,org.keycloak.events:debug
  1. Patch argocd-cm
    data:
      url: http://localhost:8080
      oidc.config: |
        name: Keycloak
        issuer: http://localhost:8090/realms/argocd
        clientID: argocd
        clientSecret: argocd-client-secret
        requestedScopes: ["openid", "profile", "email"]
        refreshTokenThreshold: 30s
  1. Run ArgoCD locally with make start-local ARGOCD_GPG_ENABLED=false ARGOCD_E2E_DISABLE_AUTH=false

  2. Reproduce bugs

  • Log in to ArgoCD via Keycloak with test / password
  • Stay idle in the UI for 3 minutes (longer than the 2-minute access-token lifespan, so the proactive refresh window passes without an inbound request) to reproduce bug in server/server.go
  • Keep using the UI (just switching between graph views is enough) for the duration of the access token lifetime
  • In both cases, the authorization login is carried out again. Relevant logs: api-server | {"level":"info","msg":"Performing authorization_code flow login:... in ArgoCD and DEBUG [org.keycloak.events] ... type="LOGIN" in Keycloak

Once the fix has been made, refresh logs can been seen in both ArgoCD api-server | {"level":"info","msg":"refreshed token for subject: ... and Keycloak DEBUG [org.keycloak.events] ... type="REFRESH_TOKEN"

Checklist:

  • Either (a) I've created an enhancement proposal and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
  • The title of the PR states what changed and the related issues number (used for the release note).
  • The title of the PR conforms to the Title of the PR
  • I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
  • I have signed off all my commits as required by DCO
  • I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
  • My build is green (troubleshooting builds).
  • I have added a brief description of why this PR is necessary and/or what this PR solves.

@ris-tlp ris-tlp requested a review from a team as a code owner May 10, 2026 10:00
@bunnyshell
Copy link
Copy Markdown

bunnyshell Bot commented May 10, 2026

✅ Preview Environment deployed on Bunnyshell

Component Endpoints
argocd https://argocd-4bnpls.bunnyenv.com/
argocd-ttyd https://argocd-web-cli-4bnpls.bunnyenv.com/

See: Environment Details | Pipeline Logs

Available commands (reply to this comment):

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

@codecov
Copy link
Copy Markdown

codecov Bot commented May 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.98%. Comparing base (5b87aa8) to head (631bae6).
⚠️ Report is 58 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #27777      +/-   ##
==========================================
+ Coverage   63.61%   63.98%   +0.36%     
==========================================
  Files         417      421       +4     
  Lines       57125    57782     +657     
==========================================
+ Hits        36338    36969     +631     
+ Misses      17394    17333      -61     
- Partials     3393     3480      +87     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@ris-tlp ris-tlp force-pushed the fix/oidc-refresh-tokens branch from 65c0602 to 9bffa5f Compare May 10, 2026 20:26
ris-tlp added 2 commits May 10, 2026 16:26
…ration

Signed-off-by: ris-tlp <omarkhantlp@gmail.com>
…ired

Signed-off-by: ris-tlp <omarkhantlp@gmail.com>
@ris-tlp ris-tlp force-pushed the fix/oidc-refresh-tokens branch from 9bffa5f to ffeae1b Compare May 10, 2026 20:26
Signed-off-by: ris-tlp <omarkhantlp@gmail.com>
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.

OIDC refresh tokens not used in Web UI (re-login triggered instead)

1 participant