Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAML redirect loses session data storing "next" url and RelayState cannot be used instead #481

Open
danjacob-anders opened this issue Aug 14, 2023 · 0 comments

Comments

@danjacob-anders
Copy link

danjacob-anders commented Aug 14, 2023

Expected behaviour

When using social core login with e.g. Django, the "next" parameter should be stored in the strategy session data. Upon authentication with the IdP, the SAML backend should then be able to retrieve the "next" parameter from the session and redirect the user to the desired destination.

If the session data is unavailable, then the redirect url can be obtained from the GET or POST params. In the case of SAML, RelayState should be available to use for such purposes.

Actual behaviour

  1. The session data is empty on redirect back to the client. This may be due to SameSite issues, however this is unclear. Other authentication backends e.g. Google do not lose session data, so this is only happening with SAML backend.
  2. If the session data is unavailable, we should be able to use RelayState to pass the "next" url instead. This can then be used to retrieve the required URL from the POST returned from the IdP.

Unfortunately RelayState appears to be hijacked by social_core to set the IdP name, and there does not appear to be a simple way to override this behaviour:

    def auth_url(self):
        """Get the URL to which we must redirect in order to
        authenticate the user"""
        try:
            idp_name = self.strategy.request_data()["idp"]
        except KeyError:
            raise AuthMissingParameter(self, "idp")
        auth = self._create_saml_auth(idp=self.get_idp(idp_name))
        # Below, return_to sets the RelayState, which can contain
        # arbitrary data.  We use it to store the specific SAML IdP
        # name, since we multiple IdPs share the same auth_complete
        # URL.
        return auth.login(return_to=idp_name)

    def auth_complete(self, *args, **kwargs):
        """
        The user has been redirected back from the IdP and we should
        now log them in, if everything checks out.
        """
        # RelayState can only have idp_name, or it breaks here
        idp_name = self.strategy.request_data()['RelayState']
        idp = self.get_idp(idp_name)
        auth = self._create_saml_auth(idp)
        # ...complete authentication

The reasoning appears to be to be able to support multiple providers; however it also breaks what appears to be basic SAML functionality, i.e. being able to use RelayState for arbitrary needs such as a dynamically generated redirect URL. In our case, we only require a single SAML provider at this stage.

This is outlined in more detail here:

https://stackoverflow.com/questions/70599909/django-social-auth-not-redirecting-to-provided-next-query-param-for-saml-login

Is there a way to be able to reliably pass a "next" parameter using SAML either through the session or request parameters?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant