Skip to content

fix: show session expired modal instead of silent redirect to login#31

Open
wicky-zipstack wants to merge 1 commit intomainfrom
fix/session-expired-modal
Open

fix: show session expired modal instead of silent redirect to login#31
wicky-zipstack wants to merge 1 commit intomainfrom
fix/session-expired-modal

Conversation

@wicky-zipstack
Copy link
Copy Markdown
Contributor

What

  • Show "Session Expired" modal when a 401 response is received instead of silently redirecting to login
  • Suppress duplicate error toasts when the session expired modal is visible

Why

  • When a session expires (e.g., user logs out from another tab), clicking any menu silently redirects to the login page without any message
  • Users had no idea why they were logged out — no feedback, no error message
  • The modal infrastructure already existed in RequireAuth.js but was never triggered

How

  • axios-service.js: Trigger setShowSessionExpiredModal(true) on 401 response in the interceptor
  • RequireAuth.js: Show modal with "Session Expired" message before redirecting — user must click "Login" to proceed
  • notification-service.js: Suppress error toasts when showSessionExpiredModal is true to avoid duplicate notifications

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

  • No. The 401 handling flow is unchanged — session is still cleared, user still ends up at login. The only difference is a modal appears first explaining why. Works for both Cloud and OSS (same axios interceptor).

Database Migrations

  • None

Env Config

  • None

Relevant Docs

  • N/A

Related Issues or PRs

  • Fixes OR-1400: Session Timeout Does Not Display Error Message

Dependencies Versions

  • None

Notes on Testing

  • Login in one browser tab, logout from another tab
  • Go back to first tab and click any menu — should see "Session Expired" modal with Login button
  • No duplicate "Failed - Something went wrong" toast should appear
  • Verify works on both Cloud and OSS

Screenshots

N/A

Checklist

- Trigger showSessionExpiredModal on 401 in axios interceptor
- Show modal with "Session Expired" message before redirecting
- Suppress error toasts when session expired modal is showing
- User must click "Login" to proceed to login page
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 2, 2026

Greptile Summary

This PR replaces the silent redirect-to-login behavior on session expiry with a blocking "Session Expired" modal, improving UX by giving users explicit feedback before they are sent to the login page. The implementation is clean and well-scoped across three files.

Key changes:

  • axios-service.js: Triggers setShowSessionExpiredModal(true) via useSessionStore.getState() in the 401 interceptor — the correct Zustand pattern for non-hook contexts.
  • RequireAuth.js: Renders only the modal (no <Outlet />) when !isLoggedIn && showSessionExpiredModal, preventing protected content from being visible to unauthenticated users. closable={false} and maskClosable={false} force the user to acknowledge the expiry before proceeding.
  • notification-service.js: Suppresses error toasts while the modal is visible to avoid duplicate notifications. The suppression is intentionally broader than just 401 errors (covers all concurrent failures while the modal is open) — worth confirming this is the desired scope.
  • showSessionExpiredModal is correctly excluded from Zustand's partialize so it is never persisted to localStorage, meaning a page refresh will fall back to a silent redirect (expected behavior).

Confidence Score: 5/5

  • Safe to merge — the session flow is unchanged and the only user-visible difference is the modal appearing before the login redirect.
  • All findings are P2 (style/documentation). No logic errors, no data integrity concerns, no regressions introduced. The single comment is a clarification request about intentional broadened suppression scope, not a blocker.
  • frontend/src/service/notification-service.js — confirm the broader error-toast suppression (all errors while modal is open, not just 401s) is intentional.

Important Files Changed

Filename Overview
frontend/src/auth/RequireAuth.js Refactored to show a blocking "Session Expired" modal (closable={false}, maskClosable={false}) when !isLoggedIn && showSessionExpiredModal, instead of silently redirecting. handleLoginRedirect now resets the modal flag before navigating. The Outlet is no longer co-rendered with the modal, preventing protected content from appearing to an unauthenticated user.
frontend/src/service/axios-service.js Added useSessionStore.getState().setShowSessionExpiredModal(true) in the 401 interceptor path, using the correct Zustand out-of-hook pattern for an effect callback context.
frontend/src/service/notification-service.js Imports useSessionStore and uses getState() inside notify to suppress all error toasts while showSessionExpiredModal is true. This broadens the original 401-only suppression to cover all concurrent error notifications while the modal is visible.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[API Request] --> B{401 Response?}
    B -- Yes --> C[axios-service: setShowSessionExpiredModal true]
    C --> D[axios-service: clear sessionDetails]
    D --> E{RequireAuth renders}
    E -- isLoggedIn=false AND showSessionExpiredModal=true --> F[Show Session Expired Modal]
    F --> G[User clicks Login]
    G --> H[setShowSessionExpiredModal false]
    H --> I[navigate to /login]
    B -- No --> J[Normal response handling]
    E -- isLoggedIn=false AND no modal flag --> I
    E -- isLoggedIn=true --> K[Render Outlet]
    F --> L[notification-service suppresses all error toasts]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: frontend/src/service/notification-service.js
Line: 54-60

Comment:
**Overly broad notification suppression**

The added `useSessionStore.getState().showSessionExpiredModal` condition now suppresses **all** error notifications while the modal is visible — not just those originating from 401 responses. If any in-flight request (e.g., a concurrent 500 or network error) fails after the session-expired modal appears, its error toast will be silently swallowed.

The original intent (per the PR description) was to avoid *duplicate* toasts for the 401 that triggered the modal, but this check goes further and mutes all error feedback.

If the goal is only to avoid the duplicate 401 toast, the original condition (`error?.response?.status === 401`) already covered that. The extra clause only matters for non-401 errors that arrive while the modal is open. Consider scoping the suppression to 401 only, or adding a comment acknowledging the broader suppression is intentional:

```suggestion
        if (
          error?.response?.status === 401 ||
          // Suppress all error toasts while the session-expired modal is shown,
          // since any concurrent request failures are likely a consequence of
          // the expired session and would only add noise.
          useSessionStore.getState().showSessionExpiredModal
        ) {
          return;
        }
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: show session expired modal instead ..." | Re-trigger Greptile

Comment on lines +54 to 60
// Skip all notifications when session expired modal is showing
if (
error?.response?.status === 401 ||
useSessionStore.getState().showSessionExpiredModal
) {
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Overly broad notification suppression

The added useSessionStore.getState().showSessionExpiredModal condition now suppresses all error notifications while the modal is visible — not just those originating from 401 responses. If any in-flight request (e.g., a concurrent 500 or network error) fails after the session-expired modal appears, its error toast will be silently swallowed.

The original intent (per the PR description) was to avoid duplicate toasts for the 401 that triggered the modal, but this check goes further and mutes all error feedback.

If the goal is only to avoid the duplicate 401 toast, the original condition (error?.response?.status === 401) already covered that. The extra clause only matters for non-401 errors that arrive while the modal is open. Consider scoping the suppression to 401 only, or adding a comment acknowledging the broader suppression is intentional:

Suggested change
// Skip all notifications when session expired modal is showing
if (
error?.response?.status === 401 ||
useSessionStore.getState().showSessionExpiredModal
) {
return;
}
if (
error?.response?.status === 401 ||
// Suppress all error toasts while the session-expired modal is shown,
// since any concurrent request failures are likely a consequence of
// the expired session and would only add noise.
useSessionStore.getState().showSessionExpiredModal
) {
return;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/service/notification-service.js
Line: 54-60

Comment:
**Overly broad notification suppression**

The added `useSessionStore.getState().showSessionExpiredModal` condition now suppresses **all** error notifications while the modal is visible — not just those originating from 401 responses. If any in-flight request (e.g., a concurrent 500 or network error) fails after the session-expired modal appears, its error toast will be silently swallowed.

The original intent (per the PR description) was to avoid *duplicate* toasts for the 401 that triggered the modal, but this check goes further and mutes all error feedback.

If the goal is only to avoid the duplicate 401 toast, the original condition (`error?.response?.status === 401`) already covered that. The extra clause only matters for non-401 errors that arrive while the modal is open. Consider scoping the suppression to 401 only, or adding a comment acknowledging the broader suppression is intentional:

```suggestion
        if (
          error?.response?.status === 401 ||
          // Suppress all error toasts while the session-expired modal is shown,
          // since any concurrent request failures are likely a consequence of
          // the expired session and would only add noise.
          useSessionStore.getState().showSessionExpiredModal
        ) {
          return;
        }
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — when the session expired modal is showing, the user is about to be redirected to login. Any concurrent errors (500s from other in-flight requests) are irrelevant at that point since the session is dead. Suppressing all toasts avoids a noisy screen full of stacked error notifications.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant