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 <stevenwucongmin@gmail.com>
This commit is contained in:
wucm667 2026-05-01 10:31:51 +08:00
parent 65037b086c
commit 8be41ba7d2
2 changed files with 21 additions and 2 deletions

View File

@ -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

View File

@ -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