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

Fix the SAML logout support in authentication delegation #6205

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

leleuj
Copy link
Contributor

@leleuj leleuj commented Nov 13, 2024

Here is my use case: I have two CAS servers, the first one delegates the authentication to the second one acting as a SAML IdP. The first CAS server is defined by a SAML service with logoutType: FRONT_CHANNEL in the second one.

A logout is made on the second CAS server which propagates to the first one.
Two problems happen here:

  1. in case of an IFRAME SLO support (cas.slo.logout-propagation-type: IFRAME), the logout URL built is: http://localhost:8080/cas/login?client_name=Client1?logoutRequest=%3C%3Fxml+versi... There are two question marks. It is fixed in this PR by changing the CasThymeleafTemplatesDirector and the casPropagateLogoutView.html.

  2. The logout call is made using GET while currently, only POST is supported. It is fixed in this PR by changing the DelegatedSaml2ClientLogoutAction.java.

I have added unit tests, but no Puppeteer test as it would require to start two CAS servers.

@mmoayyed
Copy link
Member

Thank you.

The puppeteer setup allows one to run many CAS servers in parallel. Look for the ""instances": 2," property in the script.json files and you'll find examples. I am not sure it makes sense in this case; just wanted to point that out it's possible.

@leleuj
Copy link
Contributor Author

leleuj commented Nov 13, 2024

I didn't know about the "instances" option. I see that they can have different configurations as well. That's great!

I will try to setup something to demonstrate the scenario. It would make the fix more obvious.

Comment on lines 42 to 48
public String getSupplementedUrlExternalForm(final URL url) {
val externalForm = url.toExternalForm();
if (externalForm.contains("?")) {
return externalForm + "&";
}
return externalForm + "?";
}
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest that you modify the original function to handle this scenario, instead of a new one.

Also, this check if (externalForm.contains("?")) { is slightly strange. Would it make sense to see if the URL just ends with a ? ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In fact, the original function is already used in the casPropagateLogoutView.html to send the logout as an AJAX call:

    $.ajax({
        url: [[${@casThymeleafTemplatesDirector.getUrlExternalForm(entry.key.logoutUrl)}]],
        dataType: 'jsonp',
        async: true,
        contentType: [[${entry.value.contentType}]]
        , data: [[${entry.value.message}]]

In that case, the additionnal ? or & is not necessary as the message is not sent in the request URL.

Though, I can supplement the original function and not create a new one.

About the test, it really is a contains and not only a endsWith as the logout URL can be for example: http://localhost:8080/cas/login?client_name=XXX.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the notes. "Fixing" the original function sounds better to me. It seems less code to manage and test and regardless of where it's used getUrlExternalForm should just behave more correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK. I changed it

@leleuj
Copy link
Contributor Author

leleuj commented Nov 14, 2024

@mmoayyed I struggled a bit in configuration, but I managed to add a Puppeteer test to confirm the fix.

This is quite simple: localhost:8443 is the SAML server and localhost:8444 is the client.

I call localhost:8444/login, delegates to localhost:8443/login and log in.
Then, I call localhost:8443/logout and then localhost:8444/login to ensure I have been actually logged out.

@mmoayyed
Copy link
Member

Thanks much for working out the test.

Please look at the failing tests; there seems to be at least 1 test case in this area that deals with SLO and redirect URLs that likely is broken.

@leleuj
Copy link
Contributor Author

leleuj commented Nov 14, 2024

I guess you are referring to delegated-login-saml2-slo-idp: I will check

@leleuj
Copy link
Contributor Author

leleuj commented Nov 14, 2024

Indeed, the change in DelegatedSaml2ClientLogoutAction makes the delegated-login-saml2-slo-idp test fail.

To be able to handle the front channel scenario in an iframe, we must destroy the SSO session related to the session index for the GET HTTP method (while only the POST HTTP method is accepted currently).

I'm not sure of the behavior of the http://localhost:9443/simplesaml/saml2/idp/SingleLogoutService.php?ReturnTo=https://apereo.github.io call. I guess it sends a logout request to the CAS server. Why should it redirect to https://localhost:8443/cas/logout the first time and to https://apereo.github.io the second time?

@mmoayyed Any idea?

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

Successfully merging this pull request may close these issues.

3 participants