Skip to content

fix(auth): surface 401 session expiry to frontend with user notification#489

Draft
ayushshukla1807 wants to merge 4 commits intohatnote:masterfrom
ayushshukla1807:feat-auth-401-override
Draft

fix(auth): surface 401 session expiry to frontend with user notification#489
ayushshukla1807 wants to merge 4 commits intohatnote:masterfrom
ayushshukla1807:feat-auth-401-override

Conversation

@ayushshukla1807
Copy link
Copy Markdown

@ayushshukla1807 ayushshukla1807 commented Apr 11, 2026

Fixes the session dropping issue mentioned in #116.

If a user's session expired during a vote loop, the UserMiddleware dropped the cookie and returned an empty dict. The backend just forwarded a HTTP 200 with an error string attached, which meant the frontend .catch() block never triggered. The UI would move on like normal while the vote was lost.

I've updated the middleware to throw an explicit 401 when the cookie is invalid, so the frontend correctly halts and prompts a re-login.

@ayushshukla1807 ayushshukla1807 force-pushed the feat-auth-401-override branch from 7accc5b to 6c28b7a Compare April 11, 2026 15:19
Copy link
Copy Markdown

@userAdityaa userAdityaa left a comment

Choose a reason for hiding this comment

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

Have you tested the application flow manually?

Previously, when cookies were invalid or unknown, the middleware returned an empty dictionary, which Clastic/MessageMiddleware interpreted as a silent 200 OK response. With your changes, those same scenarios now return {'_status_code': 401}, causing Axios to treat the response as a failure and the frontend to surface a 401 error.

more changes will be required, please check this whole flow manually.

Comment thread montage/mw/__init__.py Outdated
else:
if ep_is_public:
return next(user=None, user_dao=None)
# @ayushshukla1807: we must explicitly return 401 here so MessageMiddleware
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry, but this comment doesn’t seem relevant to the actual fix.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for pointing that out, Aditya.

I've cleaned up the code by removing the redundant comments.

I also followed up on your suggestion to check the whole flow: I've updated all authentication failure paths in the middleware to return 401 (including missing cookies and unknown IDs) and added a global interceptor in the frontend to handle these cases gracefully.

This ensures that if a session drops mid-view, the app will now redirect back to home for a re-auth check instead of hanging or showing empty templates.

I've verified this behavior by simulating cookie drops locally.

- Replaced empty dict returns with explicit 401 status codes in UserMiddleware.
- Removed redundant self-referential comments.
- Added global Axios interceptor to catch 401s and redirect to Home, handling session drops gracefully.
- Addresses feedback on hatnote#489 and hardening requirements for hatnote#116.
@lgelauff
Copy link
Copy Markdown
Collaborator

Thanks for working on this. There's a related closed PR — #472 by @userAdityaa — that covered the same ground. It's worth reading both for how it surfaces the error message to the user and for
#472 (review).
A few concrete observations:
- The 401 intercept is added inside addInterceptors, which is called on both apiBackend and apiCommons. Please consider if this could result in side effects.

  • window.location.href = '#/' redirects silently with no message. Wouldn't that mean that the user loses in-progress work without knowing why? The backend already appends the error text to response_dict['errors'] — that could be shown before any redirect, or instead of one.
  • The third hunk in mw/init.py (the su_to branch) is a superuser impersonation misconfiguration, not a session expiry. Returning 401 there conflates two distinct failure modes and seems outside the scope of Save Changes sometimes does nothing #116. Please reflect.

@lgelauff lgelauff marked this pull request as draft April 14, 2026 10:31
- Added AlertService notification to Axios interceptor to prevent silent 401 redirects.
- Refactored superuser impersonation failure to 400 (Bad Request) instead of 401 to avoid session expiry conflation.
- Addresses maintainer feedback on hatnote#489 and hatnote#116.
@ayushshukla1807 ayushshukla1807 changed the title fix: Surface network 401 Auth Exceptions on Vote Save drops (#116) fix(auth): surface 401 session expiry to frontend with user notification Apr 15, 2026
Per @lgelauff's review:

1. Split interceptors: the 401 session-expiry handler is now in a
   separate addAuthInterceptor() applied ONLY to apiBackend.
   Previously, addInterceptors() added 401 handling to both
   apiBackend and apiCommons — but Commons API 401s are not
   Montage session failures and should not trigger a session
   redirect. This is now separated cleanly.

2. Show backend error text before redirect: rather than a generic
   'Session expired' message, the interceptor now reads
   error.response.data.errors[0] — the text the backend already
   appends — so users see the actual reason (e.g.
   'invalid cookie userid, try logging in again') before any
   redirect. This preserves context and prevents silent work loss.

3. su_to branch already returns 400 (not 401) — correctly
   distinguishes superuser impersonation misconfiguration from
   session expiry. No change needed here.
@ayushshukla1807
Copy link
Copy Markdown
Author

Thanks for the detailed review, @lgelauff — and for pointing to #472. I have addressed all three points:

1. Interceptor scope — Split into addLoadingInterceptors() (applied to both apiBackend and apiCommons) and a separate addAuthInterceptor() applied only to apiBackend. Commons API 401s are not Montage session failures and will no longer trigger a redirect.

2. Silent redirect — The interceptor now reads error.response.data.errors[0] — the text the backend already appends to response_dict['errors'] — so users see the actual failure reason (e.g. 'invalid cookie userid, try logging in again') before any navigation. The 2-second delay gives them time to read it.

3. su_to branch — Already returning 400 to distinguish superuser impersonation misconfiguration from session expiry. No change needed and I agree the distinction is correct.

Updated in the latest commit on this branch.

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.

3 participants