From 8be41ba7d20334bdade114528f85f0cf60b2bc59 Mon Sep 17 00:00:00 2001 From: wucm667 Date: Fri, 1 May 2026 10:31:51 +0800 Subject: [PATCH] fix(middleware): redirect to login on fatal refresh error instead of showing error page When Keycloak (or other OIDC providers) returns "invalid_grant" during token refresh (e.g., "Session not active" when the Keycloak session expires), the session is correctly cleared from the store. However, the user sees an error page and must manually reload to trigger re-authentication. Fix: introduce errSessionCleared sentinel error so that getValidatedSession returns nil session and nil error when a fatal refresh error occurs. This allows the normal auth flow to redirect the user to the login page seamlessly instead of showing an error page. Fixes oauth2-proxy/oauth2-proxy#3402 Signed-off-by: wucm667 --- CHANGELOG.md | 4 ++++ pkg/middleware/stored_session.go | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320ba697..1a9b9681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ## Changes since v7.15.2 +- 🐛 Bug Fixes + - Fixed middleware to seamlessly redirect to login on fatal refresh errors + (e.g., Keycloak "Session not active") instead of showing an error page. + # V7.15.2 ## Release Highlights diff --git a/pkg/middleware/stored_session.go b/pkg/middleware/stored_session.go index 53238f19..1a148826 100644 --- a/pkg/middleware/stored_session.go +++ b/pkg/middleware/stored_session.go @@ -15,6 +15,13 @@ import ( "github.com/oauth2-proxy/oauth2-proxy/v7/providers" ) +var ( + // errSessionCleared indicates the session was cleared due to a fatal + // refresh error (e.g., session revoked at the provider). The caller + // should treat this as "no session" and trigger re-authentication. + errSessionCleared = errors.New("session cleared due to fatal refresh error") +) + const ( // When attempting to obtain the lock, if it's not done before this timeout // then exit and fail the refresh attempt. @@ -143,6 +150,12 @@ func (s *storedSessionLoader) getValidatedSession(rw http.ResponseWriter, req *h err = s.refreshSessionIfNeeded(rw, req, session) if err != nil { + if errors.Is(err, errSessionCleared) { + // Session was cleared due to a fatal refresh error (e.g., revoked). + // Return nil session and nil error so the normal auth flow + // redirects to login instead of showing an error page. + return nil, nil + } return nil, fmt.Errorf("error refreshing access token for session (%s): %v", session, err) } @@ -228,8 +241,10 @@ func (s *storedSessionLoader) refreshSessionIfNeeded(rw http.ResponseWriter, req logger.Errorf("failed clearing session: %v", err) } - // Return error immediately to force re-authentication - return fmt.Errorf("session invalidated due to fatal refresh error: %w", err) + // Return errSessionCleared so the caller returns nil session + // and nil error, triggering seamless re-authentication instead + // of showing an error page. + return errSessionCleared } // For non-fatal errors (network issues, timeouts), keep the session