11"""Middleware classes for third_party_auth."""
22
33
4+ import json
5+ import urllib .parse
6+
47import six .moves .urllib .parse
8+ from django .conf import settings
59from django .contrib import messages
610from django .shortcuts import redirect
711from django .urls import reverse
812from django .utils .deprecation import MiddlewareMixin
913from django .utils .translation import gettext as _
1014from requests import HTTPError
15+ from social_core .exceptions import SocialAuthBaseException
1116from social_django .middleware import SocialAuthExceptionMiddleware
1217
1318from common .djangoapps .student .helpers import get_next_url_for_login_page
1419
15- from . import pipeline
20+ from . import pipeline , provider
21+
22+
23+ def _get_saml_provider_name (request ):
24+ """
25+ Try to resolve the human-readable provider name from the SAML RelayState
26+ that is present in the POST body of /auth/complete/tpa-saml/.
27+
28+ Returns the provider display name (e.g. "Cartão de Cidadão") or None if
29+ it cannot be determined.
30+ """
31+ try :
32+ backend = getattr (request , 'backend' , None )
33+ if backend is None :
34+ return None
35+ relay_state_str = backend .strategy .request_data ().get ('RelayState' , '' )
36+ relay_state = json .loads (relay_state_str )
37+ idp_slug = relay_state .get ('idp' )
38+ if not idp_slug :
39+ return None
40+ # provider_id for SAML providers is "saml-<slug>"
41+ saml_provider = provider .Registry .get (f'saml-{ idp_slug } ' )
42+ if saml_provider :
43+ return saml_provider .name
44+ except Exception : # pylint: disable=broad-except
45+ pass
46+ return None
1647
1748
1849class ExceptionMiddleware (SocialAuthExceptionMiddleware , MiddlewareMixin ):
@@ -32,6 +63,22 @@ def get_redirect_uri(self, request, exception):
3263 if auth_entry and auth_entry in pipeline .AUTH_DISPATCH_URLS :
3364 redirect_uri = pipeline .AUTH_DISPATCH_URLS [auth_entry ]
3465
66+ # For the account_settings flow, /account/settings is a plain RedirectView
67+ # that goes to the Account MFE without preserving Django messages. Build the
68+ # MFE URL directly so the ?duplicate_provider param reaches the frontend.
69+ if auth_entry == pipeline .AUTH_ENTRY_ACCOUNT_SETTINGS and isinstance (exception , SocialAuthBaseException ):
70+ account_mfe_url = getattr (settings , 'ACCOUNT_MICROFRONTEND_URL' , None )
71+ if account_mfe_url :
72+ # Prefer the human-readable provider name; fall back to backend name.
73+ provider_name = _get_saml_provider_name (request ) or getattr (
74+ getattr (request , 'backend' , None ), 'name' , None
75+ )
76+ if provider_name :
77+ redirect_uri = '{}?duplicate_provider={}' .format (
78+ account_mfe_url .rstrip ('/' ) + '/' ,
79+ urllib .parse .quote (provider_name , safe = '' ),
80+ )
81+
3582 return redirect_uri
3683
3784 def process_exception (self , request , exception ):
0 commit comments