Skip to content

Conversation

@TheRealJon
Copy link
Member

Problem

When users are load-balanced across multiple console pods, each pod creates a session cookie with a unique name based on POD_NAME: openshift-session-token-<POD_NAME>.

With a 1-month cookie expiration, users accumulate cookies from different pods without old ones being removed. This is especially problematic with Azure AD SSO where new authentication tokens are issued on each login, causing OpenShift to create new session cookies instead of reusing old ones.

Over time, the cookie header can exceed 4096 bytes, causing errors.

Solution

This fix cleans up session cookies from other pods when creating a new session, ensuring only one active session cookie exists at a time.

Changes

  • Modified AddSession() to expire old pod cookies before creating new session in pkg/auth/sessions/combined_sessions.go:46-73
  • Updated DeleteSession() to use modern cookie expiration pattern (removed outdated Go loop variable capture)
  • Added test TestCombinedSessionStore_AddSession_CleansUpOldPodCookies to verify old pod cookies are properly expired

Testing

  • All existing session tests pass ✓
  • New test verifies old pod cookies are expired on new session creation ✓

Impact

  • Backward compatible: Existing sessions continue to work
  • Low risk: Only affects cookie cleanup during new session creation
  • Performance: Minimal overhead - iterates through cookies once per login

Fixes: OCPBUGS-65967

🤖 Generated with Claude Code

When users are load-balanced across multiple console pods, each pod
creates a session cookie with a unique name based on POD_NAME:
openshift-session-token-<POD_NAME>. With a 1-month cookie expiration,
users accumulate cookies from different pods without old ones being
removed, eventually causing the cookie header to exceed 4096 bytes.

This fix cleans up session cookies from other pods when creating a
new session, ensuring only one active session cookie exists at a time.

Changes:
- Modified AddSession() to expire old pod cookies before creating new session
- Updated DeleteSession() to use modern cookie expiration pattern
- Added test to verify old pod cookies are properly expired

Fixes: OCPBUGS-65967

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-ci-robot openshift-ci-robot added jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Dec 12, 2025
@openshift-ci-robot
Copy link
Contributor

@TheRealJon: This pull request references Jira Issue OCPBUGS-65967, which is invalid:

  • expected the bug to target the "4.22.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Problem

When users are load-balanced across multiple console pods, each pod creates a session cookie with a unique name based on POD_NAME: openshift-session-token-<POD_NAME>.

With a 1-month cookie expiration, users accumulate cookies from different pods without old ones being removed. This is especially problematic with Azure AD SSO where new authentication tokens are issued on each login, causing OpenShift to create new session cookies instead of reusing old ones.

Over time, the cookie header can exceed 4096 bytes, causing errors.

Solution

This fix cleans up session cookies from other pods when creating a new session, ensuring only one active session cookie exists at a time.

Changes

  • Modified AddSession() to expire old pod cookies before creating new session in pkg/auth/sessions/combined_sessions.go:46-73
  • Updated DeleteSession() to use modern cookie expiration pattern (removed outdated Go loop variable capture)
  • Added test TestCombinedSessionStore_AddSession_CleansUpOldPodCookies to verify old pod cookies are properly expired

Testing

  • All existing session tests pass ✓
  • New test verifies old pod cookies are expired on new session creation ✓

Impact

  • Backward compatible: Existing sessions continue to work
  • Low risk: Only affects cookie cleanup during new session creation
  • Performance: Minimal overhead - iterates through cookies once per login

Fixes: OCPBUGS-65967

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

Walkthrough

Expire old pod-specific session cookies during AddSession, GetSession, and UpdateTokens; DeleteSession now creates new expired cookies instead of mutating existing ones. Tests added to verify cleanup behavior and some test helpers switched to type-inferred utilptr.To usage.

Changes

Cohort / File(s) Summary
Cookie cleanup & invalidation
pkg/auth/sessions/combined_sessions.go
Added expireOldPodCookies(cs *CombinedSessionStore) and invoke it from AddSession, GetSession, and UpdateTokens to expire cookies whose names start with OpenshiftAccessTokenCookieName but do not match the current pod's SessionCookieName(). DeleteSession no longer mutates existing cookie objects; it constructs new http.Cookie instances with Value="" and MaxAge=-1 (preserving Path, Secure, HttpOnly, SameSite) and calls http.SetCookie to invalidate them.
Tests — cleanup verification & minor test changes
pkg/auth/sessions/combined_sessions_test.go
Added three tests: TestCombinedSessionStore_AddSession_CleansUpOldPodCookies, TestCombinedSessionStore_GetSession_CleansUpOldPodCookies, and TestCombinedSessionStore_UpdateTokens_CleansUpOldPodCookies to assert old pod cookies are expired (MaxAge=-1) and new session cookies still created. Replaced utilptr.To[string]("...") with type-inferred utilptr.To("...") in three test literals.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci bot requested review from Leo6Leo and jhadvig December 12, 2025 14:36
@openshift-ci openshift-ci bot added component/backend Related to backend approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Dec 12, 2025
@TheRealJon
Copy link
Member Author

/hold

Needs testing

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 12, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 335ed77 and 96646c9.

📒 Files selected for processing (2)
  • pkg/auth/sessions/combined_sessions.go (2 hunks)
  • pkg/auth/sessions/combined_sessions_test.go (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • pkg/auth/sessions/combined_sessions_test.go
  • pkg/auth/sessions/combined_sessions.go
🧬 Code graph analysis (2)
pkg/auth/sessions/combined_sessions_test.go (2)
pkg/auth/sessions/combined_sessions.go (2)
  • NewSessionStore (31-44)
  • SessionCookieName (26-29)
pkg/auth/sessions/server_session.go (1)
  • OpenshiftAccessTokenCookieName (17-17)
pkg/auth/sessions/combined_sessions.go (1)
pkg/auth/sessions/server_session.go (1)
  • OpenshiftAccessTokenCookieName (17-17)
🔇 Additional comments (2)
pkg/auth/sessions/combined_sessions_test.go (2)

55-55: LGTM: Type inference improves readability.

The changes from utilptr.To[string] to utilptr.To leverage Go's type inference, making the code cleaner while maintaining equivalent functionality.

Also applies to: 67-67, 96-96


699-745: Verify Path attribute in expired cookies.

The test correctly verifies that MaxAge is set to -1 for old pod cookies, but it doesn't check whether the Path attribute is set correctly. Cookies must have matching Name, Domain, and Path to be deleted by the browser. If Path is not set correctly in the expired cookies, the browser won't delete them, which would defeat the purpose of this cleanup logic.

Consider adding an assertion like:

 		if c.Name == OpenshiftAccessTokenCookieName+"-console-old-pod-1" ||
 			c.Name == OpenshiftAccessTokenCookieName+"-console-old-pod-2" {
 			require.Equal(t, -1, c.MaxAge, "Old pod cookie %s should be expired", c.Name)
+			require.Equal(t, "/", c.Path, "Old pod cookie %s should have correct Path for deletion", c.Name)
 			expiredCookies++
 		}

When deleting cookies via HTTP response headers, browsers need the
deletion cookie to match the Name, Path, and Domain of the original
cookie. The previous implementation only set MaxAge=-1 on the existing
cookie object without explicitly setting the Path, which could prevent
proper cookie deletion.

This change creates a new cookie with the minimal required attributes
(Name, Path, Value="", MaxAge=-1) using the path from the session store
options, ensuring the browser properly recognizes and deletes the cookie.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@Leo6Leo
Copy link
Contributor

Leo6Leo commented Dec 15, 2025

Assign for my own review tracking purposes:
/assign @Leo6Leo

@TheRealJon
Copy link
Member Author

/retest

1 similar comment
@TheRealJon
Copy link
Member Author

/retest

@TheRealJon
Copy link
Member Author

/jira refresh

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Dec 17, 2025
@openshift-ci-robot
Copy link
Contributor

@TheRealJon: This pull request references Jira Issue OCPBUGS-65967, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @jadhaj

Details

In response to this:

/jira refresh

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci bot requested a review from jadhaj December 17, 2025 15:50
@Leo6Leo
Copy link
Contributor

Leo6Leo commented Dec 17, 2025

From the code's perspective, I think there is no problem and it will solve the problem. Will need the help from QE to verify it on the real cluster if it actually solves the problem.

/lgtm

QE verification:
/assign @yapei

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Dec 17, 2025
@yapei
Copy link
Contributor

yapei commented Dec 25, 2025

@Leo6Leo @TheRealJon here are my steps to verify the changes, I created a testing cluster with cluster-bot (launch 4.21.0-0.nightly-2025-12-22-230304,openshift/console#15837 aws)

  1. initial status, given below console pods, we only have one cookie:
    openshift-session-token-console-68bf8595f-zwrc9
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-68bf8595f-h5jkx      1/1     Running   0          7m58s
console-68bf8595f-zwrc9      1/1     Running   0          5m24s
downloads-7dbb84fd5d-8fsjq   1/1     Running   0          78m
downloads-7dbb84fd5d-qnl29   1/1     Running   0          78m
  1. trigger a new rollout via oc rollout restart deployment/console -n openshift-console, we can see two openshift session cookies:
    openshift-session-token-console-68bf8595f-h5jkx
    openshift-session-token-console-68bf8595f-zwrc9
    [In each rollout, only one new pods will be re-created]
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-68bf8595f-dhtq6      1/1     Running   0          83s
console-68bf8595f-h5jkx      1/1     Running   0          11m
downloads-7dbb84fd5d-8fsjq   1/1     Running   0          81m
downloads-7dbb84fd5d-qnl29   1/1     Running   0          81m
  1. wait several minutes, trigger several new rollouts but openshift session token cookies are always two:
    openshift-session-token-console-68bf8595f-h5jkx
    openshift-session-token-console-68bf8595f-zwrc9
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-68bf8595f-5dhbx      1/1     Running   0          43s
console-68bf8595f-h5jkx      1/1     Running   0          13m
downloads-7dbb84fd5d-8fsjq   1/1     Running   0          83m
downloads-7dbb84fd5d-qnl29   1/1     Running   0          83m

$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-68bf8595f-h5jkx      1/1     Running   0          15m
console-68bf8595f-zhgfc      1/1     Running   0          28s
downloads-7dbb84fd5d-8fsjq   1/1     Running   0          85m
downloads-7dbb84fd5d-qnl29   1/1     Running   0          85m
  1. If I manually delete the oldest console pod console-68bf8595f-h5jkx, then we can see a new cookies is generated, there will be three cookies
    openshift-session-token-console-68bf8595f-h5jkx
    openshift-session-token-console-68bf8595f-zhgfc
    openshift-session-token-console-68bf8595f-zwrc9
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-68bf8595f-7lwfj      1/1     Running   0          29s
console-68bf8595f-zhgfc      1/1     Running   0          4m49s
downloads-7dbb84fd5d-8fsjq   1/1     Running   0          90m
downloads-7dbb84fd5d-qnl29   1/1     Running   0          90m

So it appears to me that the old session cookies are still not cleared as we expect?
I did the exactly same steps on a 421 cluster and it's behaving the same as on the cluster with bug fix
Could you please help confirm?

@openshift-bot
Copy link
Contributor

/jira refresh

The requirements for Jira bugs have changed (Jira issues linked to PRs on main branch need to target different OCP), recalculating validity.

@openshift-ci-robot
Copy link
Contributor

@openshift-bot: This pull request references Jira Issue OCPBUGS-65967, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @jadhaj

Details

In response to this:

/jira refresh

The requirements for Jira bugs have changed (Jira issues linked to PRs on main branch need to target different OCP), recalculating validity.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@TheRealJon
Copy link
Member Author

/hold
Hi @yapei! Thanks for your detailed verification as always! I'll run through these steps and make sure to get this working. Not sure why those aren't being cleared as expected!

@Leo6Leo
Copy link
Contributor

Leo6Leo commented Jan 29, 2026

To provide some updates after @yapei 's observation. I did reproduced the issues that @yapei reported on a freshly nightly build clusterbot cluster, and with the locally build console image that contain @TheRealJon 's changes in this PR.

Claude & I discovered few things:

  1. Session cookies have Path=/api - They are only sent by the browser to URLs under /api/*
  2. OAuth callback is at /auth/callback - This path doesn't match /api, so browsers don't send session cookies to the callback
  3. AddSession() is called during OAuth callback - At this point, r.Cookies() doesn't contain the old session cookies, so nothing gets cleaned up

See the patch fix here: https://gist.github.com/Leo6Leo/620999e1febdd7d11adc8998f20c9f42

Extract cookie cleanup logic into expireOldPodCookies helper method and
add proper cookie attributes (Secure, HttpOnly, SameSite) required for
browsers to properly delete cookies. Expand cleanup to GetSession and
UpdateTokens to handle all load balancing scenarios. Add comprehensive
test coverage for all cleanup paths.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Leo Li <leoli@redhat.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-ci openshift-ci bot removed the lgtm Indicates that a PR is ready to be merged. label Jan 30, 2026
@TheRealJon
Copy link
Member Author

@Leo6Leo I've reviewed and applied your patch. I'll spin up a cluster to check and make sure everything is working as expected. Thanks for the help while I was OOO!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/auth/sessions/combined_sessions.go`:
- Around line 54-62: The cookie-clearing code that calls http.SetCookie
currently omits the Domain attribute, which violates the "Must match all
attributes of the original cookie" rule; update both occurrences where
http.SetCookie is used (the block that sets Name: cookie.Name, Value: "",
MaxAge: -1) to include Domain: cs.clientStore.Options.Domain so the expired
cookie matches the original cookie attributes from cs.clientStore.Options and
will be properly deleted if Domain is later configured.

Comment on lines +54 to +62
http.SetCookie(w, &http.Cookie{
Name: cookie.Name,
Value: "",
Path: cs.clientStore.Options.Path,
MaxAge: -1,
Secure: cs.clientStore.Options.Secure,
HttpOnly: cs.clientStore.Options.HttpOnly,
SameSite: cs.clientStore.Options.SameSite,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find Domain configuration in cookie options
rg -n "\.Options\.Domain" --type=go

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Also check the Options struct definition
rg -n "type.*Options struct" --type=go -A 10

Repository: openshift/console

Length of output: 50374


🏁 Script executed:

# Examine the specific file and lines mentioned
cat -n pkg/auth/sessions/combined_sessions.go | head -250 | tail -200

Repository: openshift/console

Length of output: 9045


🏁 Script executed:

# Find where clientStore is defined and what type it is
rg -n "clientStore.*Options" pkg/auth/sessions/combined_sessions.go -B 5 -A 5

Repository: openshift/console

Length of output: 1662


🏁 Script executed:

# Search for the Options struct definition related to cookies/sessions
rg -n "type.*Options struct" pkg/auth/sessions/ -A 15

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Look for imports to understand what cookie store is being used
head -30 pkg/auth/sessions/combined_sessions.go

Repository: openshift/console

Length of output: 648


🏁 Script executed:

# Check the gorilla/sessions Options struct definition
rg -n "type Options struct" vendor/github.com/gorilla/sessions/ -A 20

Repository: openshift/console

Length of output: 1079


🏁 Script executed:

# Also verify if Domain is ever set on clientStore anywhere in the codebase
rg -n "\.Options\.Domain\s*=" --type=go

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Verify that Domain is never configured in clientStore.Options throughout the codebase
rg -n "clientStore\.Options" pkg/auth/sessions/ --type=go

Repository: openshift/console

Length of output: 1108


🏁 Script executed:

# Also check if Domain might be set through some other mechanism
rg -n "Domain\s*:" pkg/auth/sessions/ --type=go -B 2 -A 2

Repository: openshift/console

Length of output: 43


Include Domain in cookie expiration for consistency, even though it's not currently configured.

The comment on line 53 states "Must match all attributes of the original cookie," but the Domain attribute from cs.clientStore.Options is omitted. While Domain is not currently set in your cookie store initialization, including it makes the code more complete and defensive. If Domain configuration is ever added in the future, this ensures cookies will be properly deleted.

Proposed fix
 			http.SetCookie(w, &http.Cookie{
 				Name:     cookie.Name,
 				Value:    "",
 				Path:     cs.clientStore.Options.Path,
+				Domain:   cs.clientStore.Options.Domain,
 				MaxAge:   -1,
 				Secure:   cs.clientStore.Options.Secure,
 				HttpOnly: cs.clientStore.Options.HttpOnly,
 				SameSite: cs.clientStore.Options.SameSite,
 			})

Also applies to: 231-239

🤖 Prompt for AI Agents
In `@pkg/auth/sessions/combined_sessions.go` around lines 54 - 62, The
cookie-clearing code that calls http.SetCookie currently omits the Domain
attribute, which violates the "Must match all attributes of the original cookie"
rule; update both occurrences where http.SetCookie is used (the block that sets
Name: cookie.Name, Value: "", MaxAge: -1) to include Domain:
cs.clientStore.Options.Domain so the expired cookie matches the original cookie
attributes from cs.clientStore.Options and will be properly deleted if Domain is
later configured.

@TheRealJon
Copy link
Member Author

/unhold
I launched a cluster and followed @yapei's reproducer steps, now only one session token is retained even when console pods are rolled out or deleted.

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jan 30, 2026
@Leo6Leo
Copy link
Contributor

Leo6Leo commented Jan 30, 2026

I am not sure if I should be the one giving LGTM on this PR as it contains my patch. I will leave it to @jhadvig or @krishagarwal278 to give the final green light on this.

@yapei
Copy link
Contributor

yapei commented Feb 2, 2026

  1. initial status, given below pods, we only have one cookie: openshift-session-token-console-59657979f7-p76px
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-59657979f7-6gnvh     1/1     Running   0          14m
console-59657979f7-p76px     1/1     Running   0          14m
downloads-5dbdbd954f-8xslv   1/1     Running   0          33m
downloads-5dbdbd954f-nfzz6   1/1     Running   0          33m
  1. manually delete one pod, we still have one cookie: openshift-session-token-console-59657979f7-6gnvh
$ oc delete pod console-59657979f7-p76px  -n openshift-console
pod "console-59657979f7-p76px" deleted
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-59657979f7-6gnvh     1/1     Running   0          21m
console-59657979f7-ks58z     1/1     Running   0          63s
downloads-5dbdbd954f-8xslv   1/1     Running   0          40m
downloads-5dbdbd954f-nfzz6   1/1     Running   0          40m
  1. disable one dynamic plugin, trigger new rollout, we still have only one cookie: openshift-session-token-console-78d499854d-s2tgt
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-78d499854d-s2tgt     1/1     Running   0          73s
console-78d499854d-scr82     1/1     Running   0          73s
downloads-5dbdbd954f-8xslv   1/1     Running   0          47m
downloads-5dbdbd954f-nfzz6   1/1     Running   0          47m

/verified by @yapei

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Feb 2, 2026
@openshift-ci-robot
Copy link
Contributor

@yapei: This PR has been marked as verified by @yapei.

Details

In response to this:

  1. initial status, given below pods, we only have one cookie: openshift-session-token-console-59657979f7-p76px
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-59657979f7-6gnvh     1/1     Running   0          14m
console-59657979f7-p76px     1/1     Running   0          14m
downloads-5dbdbd954f-8xslv   1/1     Running   0          33m
downloads-5dbdbd954f-nfzz6   1/1     Running   0          33m
  1. manually delete one pod, we still have one cookie: openshift-session-token-console-59657979f7-6gnvh
$ oc delete pod console-59657979f7-p76px  -n openshift-console
pod "console-59657979f7-p76px" deleted
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-59657979f7-6gnvh     1/1     Running   0          21m
console-59657979f7-ks58z     1/1     Running   0          63s
downloads-5dbdbd954f-8xslv   1/1     Running   0          40m
downloads-5dbdbd954f-nfzz6   1/1     Running   0          40m
  1. disable one dynamic plugin, trigger new rollout, we still have only one cookie: openshift-session-token-console-78d499854d-s2tgt
$ oc get pods -n openshift-console
NAME                         READY   STATUS    RESTARTS   AGE
console-78d499854d-s2tgt     1/1     Running   0          73s
console-78d499854d-scr82     1/1     Running   0          73s
downloads-5dbdbd954f-8xslv   1/1     Running   0          47m
downloads-5dbdbd954f-nfzz6   1/1     Running   0          47m

/verified by @yapei

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@krishagarwal278
Copy link
Member

The fix looks good to me. @Leo6Leo your approach to identify these key issues, was significant 🙌🏻

To provide some updates after @yapei 's observation. I did reproduced the issues that @yapei reported on a freshly nightly build clusterbot cluster, and with the locally build console image that contain @TheRealJon 's changes in this PR.

Claude & I discovered few things:

  1. Session cookies have Path=/api - They are only sent by the browser to URLs under /api/*
  2. OAuth callback is at /auth/callback - This path doesn't match /api, so browsers don't send session cookies to the callback
  3. AddSession() is called during OAuth callback - At this point, r.Cookies() doesn't contain the old session cookies, so nothing gets cleaned up

See the patch fix here: https://gist.github.com/Leo6Leo/620999e1febdd7d11adc8998f20c9f42

@krishagarwal278
Copy link
Member

/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Feb 2, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 2, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: krishagarwal278, Leo6Leo, TheRealJon

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@Leo6Leo
Copy link
Contributor

Leo6Leo commented Feb 3, 2026

/retest-required

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 3, 2026

@TheRealJon: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@openshift-merge-bot openshift-merge-bot bot merged commit 4673e3a into openshift:main Feb 3, 2026
8 checks passed
@openshift-ci-robot
Copy link
Contributor

@TheRealJon: Jira Issue Verification Checks: Jira Issue OCPBUGS-65967
✔️ This pull request was pre-merge verified.
✔️ All associated pull requests have merged.
✔️ All associated, merged pull requests were pre-merge verified.

Jira Issue OCPBUGS-65967 has been moved to the MODIFIED state and will move to the VERIFIED state when the change is available in an accepted nightly payload. 🕓

Details

In response to this:

Problem

When users are load-balanced across multiple console pods, each pod creates a session cookie with a unique name based on POD_NAME: openshift-session-token-<POD_NAME>.

With a 1-month cookie expiration, users accumulate cookies from different pods without old ones being removed. This is especially problematic with Azure AD SSO where new authentication tokens are issued on each login, causing OpenShift to create new session cookies instead of reusing old ones.

Over time, the cookie header can exceed 4096 bytes, causing errors.

Solution

This fix cleans up session cookies from other pods when creating a new session, ensuring only one active session cookie exists at a time.

Changes

  • Modified AddSession() to expire old pod cookies before creating new session in pkg/auth/sessions/combined_sessions.go:46-73
  • Updated DeleteSession() to use modern cookie expiration pattern (removed outdated Go loop variable capture)
  • Added test TestCombinedSessionStore_AddSession_CleansUpOldPodCookies to verify old pod cookies are properly expired

Testing

  • All existing session tests pass ✓
  • New test verifies old pod cookies are expired on new session creation ✓

Impact

  • Backward compatible: Existing sessions continue to work
  • Low risk: Only affects cookie cleanup during new session creation
  • Performance: Minimal overhead - iterates through cookies once per login

Fixes: OCPBUGS-65967

🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@Leo6Leo
Copy link
Contributor

Leo6Leo commented Feb 3, 2026

/cherry-pick release-4.21

@openshift-cherrypick-robot

@Leo6Leo: new pull request created: #15985

Details

In response to this:

/cherry-pick release-4.21

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. component/backend Related to backend jira/severity-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants