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

Make SP URLs customizable via configuration #1815

Open
he247 opened this issue Jun 9, 2023 · 15 comments
Open

Make SP URLs customizable via configuration #1815

he247 opened this issue Jun 9, 2023 · 15 comments

Comments

@he247
Copy link

he247 commented Jun 9, 2023

Description
I already set a custom URL for those which presumably would be seen by users of the service in the authsources.php:
'SingleLogoutServiceBinding' => ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'], 'SingleLogoutServiceLocation' => $entityID.'/LocalServiceName/sp/logout', 'AssertionConsumerService' => [ [ 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', 'Location' => $entityID.'/LocalServiceName/sp/acs', 'index' => 0, ], [ 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact', 'Location' => $entityID.'/LocalServiceName/sp/acs', 'index' => 1, ], ],
So instead of e.g. $entityID.'/baseurlpath/saml/sp/saml2-acs.php' I would like to be able to set $entityID.'/baseurlpath/sp/acs'

One could argue that it is just aesthetics, but the main goal is to streamline the URL structure based on the main project where simplesaml is used as a module. Also, for the user, the recognizability of the service that is using saml is important this shall be achieved by using this standardized URL structure, at least for those URLs that are facing "outside" and may be seen by users.

Possible solution
Make URLs customizable via config-files instead of hardcoding URLs like in https://github.com/simplesamlphp/simplesamlphp/blob/master/modules/saml/src/Auth/Source/SP.php#L447

$ar->setAssertionConsumerServiceURL(Module::getModuleURL('saml/sp/saml2-acs.php/' . $this->authId));

Possible alternative
I could set up some complex rewrite rules in my Apache.

As reference: obsolete/archived Slack link https://simplesamlphp.slack.com/archives/CU5E4P8PK/p1686150989557749?thread_ts=1686146005.524799

@jaimeperez

@tvdijen
Copy link
Member

tvdijen commented Jun 9, 2023

I smell a lot of work and very little gain.
I also don't think it's possible, because you would also have to dynamically update the routes-files.

@jaimeperez
Copy link
Member

It's not an easy task because we would indeed need to find a way to override routes, but I think it's doable, and @he247 offered help with it, which is why I asked him to open a ticket so that we could discuss the proper way to do it 😄

@he247
Copy link
Author

he247 commented Jun 9, 2023

As a first start I tried this:
$ar->setAssertionConsumerServiceURL($this->metadata->getValue('AssertionConsumerServiceURL') ?? Module::getModuleURL('saml/sp/saml2-acs.php/' . $this->authId)); (I added AssertionConsumerServiceURL to the config for this trial)
It is working as expected and is the simplest/fastest way, but would need those who want to use it to set-up the proper Apache rewrite rules.

I don't have enough insight about this project. What would be your preferred approach to handle the override?

@he247
Copy link
Author

he247 commented Jun 13, 2023

I have recently seen that there is already an override for the logout URL:
https://github.com/simplesamlphp/simplesamlphp/blob/master/modules/saml/src/Auth/Source/SP.php#L418

$defaultLocation = Module::getModuleURL('saml/sp/saml2-logout.php/' . $this->getAuthId());
$location = $this->metadata->getOptionalString('SingleLogoutServiceLocation', $defaultLocation);

Wouldn't it make sense to do something similar for the acs?
$defaultLocation = Module::getModuleURL('saml/sp/saml2-acs.php/' . $this->authId);
$location = $this->metadata->getOptionalString('AssertionConsumerServiceURL', $defaultLocation);
ar->setAssertionConsumerServiceURL($location);

@MatteoBiagini
Copy link

Hello guys, I found this issue while trying to customize acs and slo urls.

We use SSP as a service provider in a multitenant application, where IDPs are out of our control.
Using "AssertionConsumerServiceURL" in the SP configuration helps with the metadata, but the authn request pointing to the original url makes it not really usable when the IDP validates the ACS url.

In our scenario, having full control of the urls is important for maintainability, as any future change would require coordinating with several different organizations.

Solving the routing issue at the Apache level with an alias or rewrite seems a good compromise to keep the code change small.
We could provide a PR, if there is any interest.

@he247
Copy link
Author

he247 commented Feb 13, 2024

Hi @MatteoBiagini,
I'm still interested in having a customisable solution. For the time being, I have kept the default behaviour because I have had to focus on getting the project up and running.
In general, I would prefer a solution to the customisable logout URL mentioned above, rather than a routing solution in Apache.

@monkeyiq
Copy link
Contributor

I get an empty slack window when trying to view the context link above. Does the desired solution come down to just the one line?

 $location = $this->metadata->getOptionalString('AssertionConsumerServiceURL', $defaultLocation);

I had a bit of a dig to see if it might trip up other places which might want to validate.

There is some checking in modules/saml/src/IdP/SAML2.php/getAssertionConsumerService(). I am mostly looking at the simplesamlphp-2.1 branch.

@he247
Copy link
Author

he247 commented Feb 20, 2024

Hi @monkeyiq,
unfortunately Slack only allows for 90 days of archive usage when having a free account. But basically there was no context that I didn't carry here.
Yes, in general I would be happy if we could have the implementation of the OptionalString similar to the one already implemented in

$location = $this->metadata->getOptionalString('SingleLogoutServiceLocation', $defaultLocation);

I think the Assertion is only to verify that the URL is not the same as any remote SP, that shouldn't make a problem, if I'm not mistaken.

if ($AssertionConsumerServiceURL !== null && $ep['Location'] !== $AssertionConsumerServiceURL) {

For the settings in config/authsources.php I planned something like this. (just as an example)

Update: using $baseURL instead of $entityID

$baseURL = empty(getenv('SERVER_URL')) ? 'https://devel.LocalServerName.de' : getenv('SERVER_URL');
$AssertionConsumerServiceURL = $baseURL.'/LocalServiceName/sp/acs';
[
...
        'SingleLogoutServiceBinding' => ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'],
        'SingleLogoutServiceLocation' => $baseURL.'/LocalServiceName/sp/logout',
        'AssertionConsumerService' => [
            [
                'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
                'Location' => $AssertionConsumerServiceURL,
                'index' => 0,
            ],
            [
                'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
                'Location' => $AssertionConsumerServiceURL,
                'index' => 1,
            ],
        ],
        'AssertionConsumerServiceURL' => $AssertionConsumerServiceURL,
...
] 

@tvdijen
Copy link
Member

tvdijen commented Feb 20, 2024

Please don't call the variable entityID because that's just not what it is in the context of SAML2 and will only cause for confusion.
Something like baseURL covers what you're doing here.

Also, what you are doing here should already be possible without any changes to SimpleSAMLphp.. You should be using one of AssertionConsumerService or AssertionConsumerServiceURL, not both. I believe AssertionConsumerService takes precedence over the AssertionConsumerServiceURL, so the latter will not have any effect (* not verified).

@MatteoBiagini
Copy link

What we are seeing, when using AssertionConsumerService in authsources.php to configure a custom ACS url, is:

  • The metadata XML contains the customized ACS url
  • The authnrequest message is still presenting the default ACS url.

Extract from the configuration:

<?php

// config/authsources.php

 'ourServiceProvider' => [
        'saml:SP',
        'entityID' => 'https://our-service-provider.com/saml/sp/metadata', 
        'acs.Bindings' => [
            'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
            'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
        ],
        'AssertionConsumerService' => [
            [
                'index' => 0,
                'isDefault' => TRUE,
                'Location' => 'https://our-service-provider.com/saml/sp/acs',
                'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
            ],
            [
                'index' => 1,
                'Location' => 'https://our-service-provider.com/saml/sp/acs', 
                'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
            ],
        ],
        '...' => [],
];

Extract from the metadata:

<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://our-service-provider.com/saml/sp/acs" index="0"/>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://our-service-provider.com/saml/sp/acs" index="1"/>

With the SP-initiated flow, this is the authn message we see:

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                    ID="_e67832df9fdc81b9c18dac61871cbcdb2120303926"
                    Version="2.0"
                    IssueInstant="2024-02-20T21:42:51Z"
                    Destination="https://identity-provider.com/auth/realms/Example-Realm/protocol/saml"
                    AssertionConsumerServiceURL="https://our-service-provider.com/saml/module.php/saml/sp/saml2-acs.php/ourServiceProvider"
                    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                    >
    <saml:Issuer>https://our-service-provider.com/saml/sp/metadata</saml:Issuer>
    <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
                        AllowCreate="true"
                        />
</samlp:AuthnRequest>

Here I would expect the "AssertionConsumerServiceURL" attribute filled with the custom URL provided in the configuration for the corresponding ProtocolBinding

@tvdijen
Copy link
Member

tvdijen commented Feb 20, 2024

Yes @MatteoBiagini, that's a bug..
However, how are our going to redirect /saml/sp/acs to an endpoint defined by this library?

@tvdijen
Copy link
Member

tvdijen commented Feb 20, 2024

It appears the AssertionConsumerService setting is not being used at all when creating the AuthnRequest:

https://github.com/simplesamlphp/simplesamlphp/blob/master/modules/saml/src/Auth/Source/SP.php#L473

@MatteoBiagini
Copy link

However, how are our going to redirect /saml/sp/acs to an endpoint defined by this library?

@tvdijen , we would add an alias in the Apache virtual host configuration.
We already maintain one alias to redirect to the library public folder, adding a couple more for the ACS and SLO endpoint is just fine.

@he247
Copy link
Author

he247 commented Feb 21, 2024

@tvdijen thank you for pointing that out baseURL is totally reasonable :)

Actually yes, these settings were taken into account for the metadata that I sent to the IdP but the generated Link in the authnrequest message was like @MatteoBiagini described. This induced an error from the IdP, as the ACS link in my metadata was not the same as in the authnrequest message.

I also used Apache RewriteRule for the custom links, just for reference:

  RewriteRule "^sp/metadata$" "module.php/saml/sp/metadata/LocalServiceName" [L]
  RewriteRule "^sp/(logout|acs)$" "module.php/saml/sp/saml2-$1.php/LocalServiceName" [L]
  RewriteRule "^sp/login$" "module.php/saml/sp/login/LocalServiceName" [L]

@tvdijen
Copy link
Member

tvdijen commented Feb 21, 2024

@he247 As a workaround, you could try signing your AuthnRequests. Usually IDPs will accept it then, even if it doesn't match the ACS urls in the metadata.

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

No branches or pull requests

5 participants