Skip to content

Commit 9710e05

Browse files
committed
feat: Enhance SAML provider name resolution and update redirect handling for account settings flow
1 parent c64a1d8 commit 9710e05

1 file changed

Lines changed: 48 additions & 1 deletion

File tree

common/djangoapps/third_party_auth/middleware.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,49 @@
11
"""Middleware classes for third_party_auth."""
22

33

4+
import json
5+
import urllib.parse
6+
47
import six.moves.urllib.parse
8+
from django.conf import settings
59
from django.contrib import messages
610
from django.shortcuts import redirect
711
from django.urls import reverse
812
from django.utils.deprecation import MiddlewareMixin
913
from django.utils.translation import gettext as _
1014
from requests import HTTPError
15+
from social_core.exceptions import SocialAuthBaseException
1116
from social_django.middleware import SocialAuthExceptionMiddleware
1217

1318
from 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

1849
class 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

Comments
 (0)