diff --git a/internal/outpost/proxyv2/application/oauth_state.go b/internal/outpost/proxyv2/application/oauth_state.go index 0da7a53464c4..a851e7e4d4c2 100644 --- a/internal/outpost/proxyv2/application/oauth_state.go +++ b/internal/outpost/proxyv2/application/oauth_state.go @@ -71,7 +71,15 @@ func (a *Application) checkRedirectParam(r *http.Request) (string, bool) { func (a *Application) createState(r *http.Request, w http.ResponseWriter, fwd string) (string, error) { s, err := a.sessions.Get(r, a.SessionName()) if err != nil { - return "", fmt.Errorf("failed to get session: %w", err) + // Session file may not exist (e.g., after outpost restart or logout) + // Delete the stale session cookie and continue with the new empty session + a.log.WithError(err).Debug("failed to get session, clearing stale cookie") + s.Options.MaxAge = -1 + if saveErr := s.Save(r, w); saveErr != nil { + a.log.WithError(saveErr).Warning("failed to delete stale session cookie") + } + // Get a fresh session after clearing the stale cookie + s, _ = a.sessions.Get(r, a.SessionName()) } if s.ID == "" { // Ensure session has an ID diff --git a/internal/outpost/proxyv2/application/session_test.go b/internal/outpost/proxyv2/application/session_test.go index e2b323e5807c..0b44c3c0effc 100644 --- a/internal/outpost/proxyv2/application/session_test.go +++ b/internal/outpost/proxyv2/application/session_test.go @@ -154,3 +154,39 @@ func TestStateFromRequestDeletesStaleCookie(t *testing.T) { } assert.True(t, foundDeleteCookie, "Expected stale session cookie to be deleted") } + +func TestCreateStateWithStaleCookie(t *testing.T) { + a := newTestApplication() + _ = a.configureProxy() + + // Create a request with a stale session cookie (simulates outpost restart or user change) + req, _ := http.NewRequest("GET", "https://ext.t.goauthentik.io/outpost.goauthentik.io/start", nil) + + // Add a cookie for a non-existent session + nonExistentSessionID := uuid.New().String() + req.AddCookie(&http.Cookie{ + Name: a.SessionName(), + Value: "encoded_session_data_" + nonExistentSessionID, + Path: "/", + }) + + rr := httptest.NewRecorder() + + // Call createState which should succeed despite the stale cookie + state, err := a.createState(req, rr, "/redirect") + + // Verify createState succeeded + assert.NoError(t, err) + assert.NotEmpty(t, state) + + // Verify the response includes a Set-Cookie header to delete the stale cookie + cookies := rr.Result().Cookies() + var foundDeleteCookie bool + for _, cookie := range cookies { + if cookie.Name == a.SessionName() && cookie.MaxAge < 0 { + foundDeleteCookie = true + break + } + } + assert.True(t, foundDeleteCookie, "Expected stale session cookie to be deleted") +}