Just an update to say I've resolved this.
Unfortunately the ServiceProvider was unwilling to make changes on their end to use the RelayState parameter or sign the AuthnRequests because as far far as they are concerned if it works for Entra/Google then that's all they care about.
For anyone else in the same boat with, I modified the getAssertionConsumerService function in ./modules/saml/src/IdP/SAML2.php to allow use of the AssertionConsumerServiceURL even when the AuthnRequest is not signed:
private static function getAssertionConsumerService(
array $supportedBindings,
Configuration $spMetadata,
?string $AssertionConsumerServiceURL = null,
?string $ProtocolBinding = null,
?int $AssertionConsumerServiceIndex = null,
bool $authnRequestSigned = false,
): ?array {
/* We want to pick the best matching endpoint in the case where for example
* only the ProtocolBinding is given. We therefore pick endpoints with the
* following priority:
* 1. isDefault="true"
* 2. isDefault unset
* 3. isDefault="false"
*/
$firstNotFalse = null;
$firstFalse = null;
/* Check if AssertionConsumerServiceURL contains a query string to allow for
* using the provided URL as long as the base domain matches.
*/
if ($AssertionConsumerServiceURL !== null) {
$AssertionConsumerServiceBaseURL = null;
$hasQueryString = strpos($AssertionConsumerServiceURL, '?');
if ($hasQueryString) {
$AssertionConsumerServiceBaseURL = substr($AssertionConsumerServiceURL,0,$hasQueryString);
}
}
foreach ($spMetadata->getEndpoints('AssertionConsumerService') as $ep) {
if ($AssertionConsumerServiceBaseURL !== null && $ep['Location'] == $AssertionConsumerServiceBaseURL) {
/* Use the given ACSUrl */
$ep['Location'] = $AssertionConsumerServiceURL;
return $ep;
}
...