Repository navigation
Render the enterprise-branded logistration experience in the authn MFE #1691
Description
Context
We want to retire the legacy server-rendered login/registration page (DEPR openedx/public-engineering#81, tracked in openedx/openedx-platform#38936), but we can't yet, and enterprise is the reason. The legacy page renders an enterprise-branded logistration experience (branded sidebar, provider suppression, consent-gated SSO registration) that frontend-app-authn doesn't, so the platform deliberately keeps enterprise/SSO learners on the old page. You can see the guard in openedx/core/djangoapps/user_authn/views/login_form.py:176-190:
has_external_provider = bool(tpa_hint_provider or saml_provider)
# NOTE: SAML/TPA users authenticating externally must remain on the legacy login/registration page
# because the new AuthN MFE does not yet fully support provider branding, hinted-login dialog, etc.
# TODO: Remove the `has_external_provider` check as soon as the AuthN MFE is fully-featured.
if should_redirect_to_authn_microfrontend() and not has_external_provider:
return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path)This issue is about building that enterprise experience in the MFE so we can drop the guard and remove the legacy page.
Groundwork already in place
The backend was refactored so enterprise logic reaches logistration (the MFE included) through openedx-filters pipeline steps instead of inline platform code:
- feat: add filters to support logistration integration openedx-filters#337 - added the authn filter types, including
AuthnMFEContextGenerated. - feat: add pipeline steps for logistration filters edx-enterprise#2551 (2026-08-07) - added the enterprise pipeline steps, including
AuthnMFEEnterpriseContextEnricher(enterprise/filters/logistration.py:98), which adds anenterpriseBrandingpayload to the MFE context, plusLoginFormEnterpriseOverrides/RegistrationFormEnterpriseOverrides. - feat: replace enterprise logistration logic with openedx-filters hooks openedx-platform#38105 (2026-08-11) - wired those hooks into
user_authn(views/utils.py:162firesAuthnMFEContextGeneratedon the/api/mfe_contextpath).
Keeping enterprise pluggable (open question)
Enterprise is intentionally not built into the platform by default - that's the whole point of the filter refactor above: the platform ships no enterprise code, and edx-enterprise plugs in. The MFE should hold the same line, so I don't think we want to hardcode the enterprise experience into frontend-app-authn. The likely shape is a frontend plugin that installs into authn plugin slots and renders enterprise from the enterpriseBranding context - the frontend counterpart to how edx-enterprise plugs into the backend - but the details are unclear, and pinning them down is part of this work:
- What slots does the authn MFE need to expose for this - an enterprise sidebar region, the provider list, messaging, registration fields? Today there's only
LoginComponentSlot, so this overlaps with Plugin slots and customization settings for authn #1686 (plugin slots for authn). - Where does the enterprise plugin live and who owns it - a separate frontend package that's the edx-enterprise counterpart?
- How does the plugin get the context - read
enterpriseBrandingoff the mfe_context the core app already fetches, or fetch its own?
So full implementation is unclear precisely because enterprise is meant to be pluggable. Settling this architecture is the first task, ahead of the rendering itself.
What's left to do
1. Populate the enterpriseBranding payload (backend dependency). AuthnMFEEnterpriseContextEnricher builds its payload from build_enterprise_branding_for_authn_mfe(...), which isn't implemented yet - it's imported with a None fallback (edx-enterprise enterprise/filters/logistration.py:40-41) and is absent from openedx-platform master. This helper (referenced under ENT-11576) has to land so /api/mfe_context actually returns enterprise branding. It may be owned separately; I'm calling it out as a prerequisite.
2. Render the enterprise experience (as a plugin, per the open question above). Today the MFE has none, and the DISABLE_ENTERPRISE_LOGIN flag doesn't help: setting it false only swaps the institution-login button for a link out to the LMS /enterprise/login page (ThirdPartyAuth.jsx:47-72), it doesn't render any enterprise branding, suppression, or consent - so flipping it isn't a solution. EnterpriseSSO.jsx is likewise just a generic single-provider button. We need parity with the legacy openedx/features/enterprise_support/ behavior:
- Enterprise sidebar / branding - consume
enterpriseBrandingand render the enterprise logo + branded welcome. - Generic-provider suppression - in an enterprise SSO context, show only the enterprise provider and hide the generic social/TPA providers.
- Consent-gated auto-registration - for
skip_registration_formSSO, don't silently auto-submit; show the auto-register welcome message and a "Continue" button. - Enterprise-branded TPA error messaging ("not authorized via this channel / contact your administrator") instead of the generic error.
- Enterprise registration-field handling - drop excluded fields (
ENTERPRISE_EXCLUDED_REGISTRATION_FIELDS) and show SSO-synced profile fields read-only. - Account-linking + multiple-enterprise selection - the "you already have an account with your {enterprise} email" copy, and routing learners in more than one enterprise to the selection page (
/enterprise/select/active, LMS-hosted). Confirm how much of this is MFE-side vs. delegated.
3. Confirm what already flows vs. what's net-new. LoginFormEnterpriseOverrides / RegistrationFormEnterpriseOverrides modify the shared form-description context the MFE already reads, so some of the above (provider suppression, consent-gated auto-register, field handling) may already arrive via /api/mfe_context. Worth auditing which overrides reach the MFE today before building anything new.
4. Remove the platform guard. Once the MFE renders these flows at parity, remove the has_external_provider guard in login_form.py:176-190 so enterprise/SSO learners are served by the MFE. That's the step that finally unblocks the legacy removal.
Definition of done
- A decided pluggable architecture - which authn slots to expose and where the enterprise plugin lives - so enterprise stays opt-in, not built into the MFE by default.
/api/mfe_contextreturns a populatedenterpriseBrandingpayload.frontend-app-authnrenders enterprise SSO / proxy-login flows at parity with the legacy page.- The
has_external_providerguard can be removed and enterprise/SSO learners are served by the MFE.
References
- DEPR this blocks: [DEPR]: Logistration Pages -> micro-frontend public-engineering#81 (legacy Logistration removal); tracker Inventory the current state of the MFEs and Legacy Pages openedx-platform#38936 (Login / Registration row).
- Groundwork: feat: add filters to support logistration integration openedx-filters#337, feat: add pipeline steps for logistration filters edx-enterprise#2551, feat: replace enterprise logistration logic with openedx-filters hooks openedx-platform#38105.
- Backend seam:
edx-enterpriseenterprise/filters/logistration.py; platformuser_authn/views/utils.py:162. - Related: frontend-app-authn#1686 (plugin slots / theming - overlaps on branding extensibility but doesn't cover the enterprise flow).
Activity