Skip to content

feat: Support IdP-initiated SAML auth with redirect destination in RelayState - #38939

Open
jono-booth wants to merge 2 commits into
openedx:masterfrom
edx:jb/backport-108-saml-idp-redirect
Open

feat: Support IdP-initiated SAML auth with redirect destination in RelayState#38939
jono-booth wants to merge 2 commits into
openedx:masterfrom
edx:jb/backport-108-saml-idp-redirect

Conversation

@jono-booth

Copy link
Copy Markdown

Description

Backport of edx#108, originally merged to our release-ulmo release branch.

Adds support for IdP-initiated SAML login flows (where the user doesn't first hit /auth/login/...) to carry a post-auth redirect destination. Since some IdPs (e.g. Auth0) can only reliably influence the SAML POST via RelayState, RelayState may now encode both the IdP slug and a next destination as "<idp_slug>|<next>". SAMLAuthBackend.auth_complete extracts and validates next (via the existing is_safe_login_or_logout_redirect safety check) before rewriting RelayState back down to just the slug so the rest of the SAML pipeline continues to work unmodified.

Backport notes

master has diverged from where this was originally written (Feb 2026): common/djangoapps/third_party_auth/saml.py's EdXSAMLIdentityProvider.get_attr and its test were independently reworked upstream in #37550 (social-auth-core unpinning) to handle the same social-core compatibility issue this PR's second commit also touched. Rather than reintroducing the older approach, this backport keeps master's existing get_attr implementation as-is and carries forward only the new RelayState redirect logic, which applied cleanly on top of it.

Testing

Copilot AI review requested due to automatic review settings July 30, 2026 14:42
@jono-booth jono-booth changed the title Jb/backport 108 saml idp redirect feat: Support IdP-initiated SAML auth with redirect destination in RelayState- #38938 Jul 30, 2026
@jono-booth jono-booth changed the title feat: Support IdP-initiated SAML auth with redirect destination in RelayState- #38938 feat: Support IdP-initiated SAML auth with redirect destination in RelayState Jul 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds support for IdP-initiated SAML authentication flows to carry a post-auth redirect destination by encoding both the IdP slug and a next path into RelayState ("<idp_slug>|<next>"). The backend extracts/validates the redirect target using existing redirect-safety logic and then rewrites RelayState back to just the slug so the existing SAML provider lookup continues to work.

Changes:

  • Parse combined RelayState values, validate and persist a safe next destination in the session, and rewrite RelayState to the IdP slug.
  • Harden handling of missing RelayState by raising AuthMissingParameter from MultiValueDictKeyError.
  • Add unit tests covering safe vs unsafe next values embedded in RelayState.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
common/djangoapps/third_party_auth/saml.py Adds RelayState parsing/validation and session storage for next, then rewrites RelayState back to the IdP slug.
common/djangoapps/third_party_auth/tests/test_saml.py Adds unit tests for splitting RelayState and setting/dropping next based on redirect safety.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +143 to +152
if is_safe_login_or_logout_redirect(
redirect_to=next_decoded,
request_host=request.get_host(),
dot_client_id=(request.GET.get('client_id') if hasattr(request, 'GET') else None),
require_https=request.is_secure(),
):
request.session['next'] = next_decoded
else:
# RelayState included an unsafe destination; clear any stale 'next' value
request.session.pop('next', None)
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.

2 participants