88from django .utils .deprecation import MiddlewareMixin
99from django .utils .translation import gettext as _
1010from requests import HTTPError
11+ from social_core import exceptions as social_exceptions
1112from social_django .middleware import SocialAuthExceptionMiddleware
1213
1314from common .djangoapps .student .helpers import get_next_url_for_login_page
1415
1516from . import pipeline
1617
18+ # Maps each social_core exception class to a stable, language-independent
19+ # error code. This is used by account_settings_redirect_view
20+ # (openedx/core/djangoapps/user_api/legacy_urls.py) to forward third-party-auth
21+ # errors to the Account MFE as a query param, since a plain RedirectView does
22+ # not forward Django messages. The Account MFE currently only renders
23+ # 'duplicate_provider' (see frontend-app-account, AccountSettingsPage.jsx);
24+ # the rest are included for forward-compatibility.
25+ TPA_ERROR_CODES = (
26+ (social_exceptions .AuthAlreadyAssociated , 'duplicate_provider' ),
27+ (social_exceptions .AuthCanceled , 'auth_canceled' ),
28+ (social_exceptions .AuthFailed , 'auth_failed' ),
29+ (social_exceptions .AuthTokenError , 'token_error' ),
30+ (social_exceptions .AuthStateMissing , 'state_missing' ),
31+ (social_exceptions .AuthStateForbidden , 'state_forbidden' ),
32+ (social_exceptions .AuthTokenRevoked , 'token_revoked' ),
33+ (social_exceptions .AuthUnreachableProvider , 'unreachable_provider' ),
34+ )
35+
36+ # Session keys used to pass the error code from the middleware to
37+ # account_settings_redirect_view.
38+ TPA_ERROR_CODE_SESSION_KEY = 'tpa_error_code'
39+ TPA_ERROR_BACKEND_SESSION_KEY = 'tpa_error_backend'
1740
1841class ExceptionMiddleware (SocialAuthExceptionMiddleware , MiddlewareMixin ):
1942 """Custom middleware that handles conditional redirection."""
@@ -32,8 +55,32 @@ def get_redirect_uri(self, request, exception):
3255 if auth_entry and auth_entry in pipeline .AUTH_DISPATCH_URLS :
3356 redirect_uri = pipeline .AUTH_DISPATCH_URLS [auth_entry ]
3457
58+ # For the account_settings flow, /account/settings is a plain
59+ # RedirectView that does not forward Django messages to the Account
60+ # MFE, so the error would otherwise be silently dropped. Save a
61+ # stable error code (and backend name) in the session here, while we
62+ # still have the real exception instance, so
63+ # account_settings_redirect_view can read it and forward it to the
64+ # MFE as a query param.
65+ if auth_entry == pipeline .AUTH_ENTRY_ACCOUNT_SETTINGS :
66+ self ._save_tpa_error_in_session (request , exception )
67+
3568 return redirect_uri
3669
70+ @staticmethod
71+ def _save_tpa_error_in_session (request , exception ):
72+ """
73+ Stores a stable error code for `exception` in the session, along with
74+ the backend name, if `exception` is a recognized third-party-auth
75+ error. No-ops otherwise.
76+ """
77+ for exc_class , code in TPA_ERROR_CODES :
78+ if isinstance (exception , exc_class ):
79+ request .session [TPA_ERROR_CODE_SESSION_KEY ] = code
80+ backend = getattr (request , 'backend' , None )
81+ request .session [TPA_ERROR_BACKEND_SESSION_KEY ] = getattr (backend , 'name' , None )
82+ break
83+
3784 def process_exception (self , request , exception ):
3885 """Handles specific exception raised by Python Social Auth eg HTTPError."""
3986
0 commit comments