Expected behavior
An authenticated OIDC user stays logged in while navigating the UI within the session timeout.
Actual behavior
OIDC users get logged out intermittently (often within seconds of login). The browser ends up holding a session cookie (sid) that points to an empty session in Redis, so every subsequent request resolves to an anonymous security context → 401 → the SPA bounces to login, frequently in a loop.
Root cause analysis
In src/core/session/session.go, SessionRegenerate saves an empty session when the oldsid no longer exists:
if isExist, _ := rp.SessionExist(ctx, oldsid); !isExist {
err := rp.c.Save(ctx, sid, map[any]any{}, maxlifetime) // empty session persisted
}
Under the real Angular UI (many concurrent XHR per page load + background polling), a concurrent request renames oldsid before this path runs, so the regenerate stores an empty session for the new sid, and that new sid is sent to the browser via Set-Cookie. The user is now anonymous despite a valid-looking cookie.
PR #22882 / #22873 / #22726 only changed the stored empty value ("" → map[any]any{}) and the lifetime; the empty-session-on-race behavior remains in main.
Evidence
Redis DB0 accumulated 54 empty 23-byte sessions vs 2 real ~4760-byte ones. The affected user's cookie sid existed in Redis with a healthy ~46 min TTL but was 23 bytes (empty). Core logs (log.level: debug): the session works ~13 s after callback, then all requests go unauthorized at once; the same endpoint (/api/v2.0/configurations) returns 200 then 401 seconds later.
Environment
- Harbor v2.15.1 (the relevant code is also present in
main)
- Auth: OIDC (Keycloak), auto-onboard enabled, external PostgreSQL, embedded
redis-photon
- Single
harbor-core replica, behind an external reverse proxy
Steps to reproduce
- Configure OIDC auth mode.
- Log in and navigate quickly across admin pages that fire many parallel API calls (e.g. project quotas / configuration).
- Observe intermittent
401s and empty 23-byte sessions piling up in Redis DB0.
Expected behavior
An authenticated OIDC user stays logged in while navigating the UI within the session timeout.
Actual behavior
OIDC users get logged out intermittently (often within seconds of login). The browser ends up holding a session cookie (
sid) that points to an empty session in Redis, so every subsequent request resolves to an anonymous security context →401→ the SPA bounces to login, frequently in a loop.Root cause analysis
In
src/core/session/session.go,SessionRegeneratesaves an empty session when theoldsidno longer exists:Under the real Angular UI (many concurrent XHR per page load + background polling), a concurrent request renames
oldsidbefore this path runs, so the regenerate stores an empty session for the newsid, and that newsidis sent to the browser viaSet-Cookie. The user is now anonymous despite a valid-looking cookie.PR #22882 / #22873 / #22726 only changed the stored empty value (
""→map[any]any{}) and the lifetime; the empty-session-on-race behavior remains inmain.Evidence
Redis DB0 accumulated 54 empty 23-byte sessions vs 2 real ~4760-byte ones. The affected user's cookie
sidexisted in Redis with a healthy ~46 min TTL but was 23 bytes (empty). Core logs (log.level: debug): the session works ~13 s after callback, then all requests go unauthorized at once; the same endpoint (/api/v2.0/configurations) returns200then401seconds later.Environment
main)redis-photonharbor-corereplica, behind an external reverse proxySteps to reproduce
401s and empty 23-byte sessions piling up in Redis DB0.