Summary

if (StringUtils.hasText(samlResponse.getDestination()) && !recipient.equals(samlResponse.getDestination())) {
            throw this.authException("invalid_destination", "Invalid SAML response destination: " + samlResponse.getDestination());
}
  • HttpServletRequest URL is returning the hostname of the app server from which the SAML request originated (localhost:port) rather than the dns name.
  • Our app is behind a load balancer.

Actual Behavior

  • code comments suggest this validation is optional, but unclear how
  • no apparent way to configure the context behind an LB

Expected Behavior

  • skip this validation OR
  • configure the host name in a similar way to SAMLContextProviderLB

Configuration

Relying Party Registration

@Bean
    public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() throws CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableEntryException, KeyStoreException {
        //SAML configuration
        //Mapping this application to one or more Identity Providers
        //remote WebSSO Endpoint - Where to Send AuthNRequests to
        String webSsoEndpoint = ssoBaseUrl;
        //local registration ID
        String registrationId = "appName";
        //local entity ID - autogenerated based on URL
        String localEntityIdTemplate = appNameHost + "/saml2/service-provider-metadata/{registrationId}";
        //local SSO URL - autogenerated, endpoint to receive SAML Response objects
        String acsUrlTemplate = appNameHost + "/login/saml2/sso/{registrationId}";
        //local signing (and local decryption key and remote encryption certificate)
        Saml2X509Credential signingCredential = getSigningCredential();
        //IDP certificate for verification of incoming messages
        Saml2X509Credential idpVerificationCertificate = getVerificationCert();
        RelyingPartyRegistration rp = RelyingPartyRegistration.withRegistrationId(registrationId)
                .remoteIdpEntityId(idpEntityId)
                .idpWebSsoUrl(webSsoEndpoint)
                .credentials((c -> c.add(signingCredential)))
                .credentials((c -> c.add(idpVerificationCertificate)))
                .localEntityIdTemplate(localEntityIdTemplate)
                .assertionConsumerServiceUrlTemplate(acsUrlTemplate)
                .build();
        return new InMemoryRelyingPartyRegistrationRepository(rp);
    }

Saml2LoginConfigurer

 http
            .logout()
                .logoutSuccessUrl(ssoRestartUrl)
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .and()
            .saml2Login()
                .successHandler(customAuthenticationSuccessHandler)
                .failureHandler(customAuthenticationFailureHandler)
                .addObjectPostProcessor(new ObjectPostProcessor<OpenSamlAuthenticationProvider>() {
                    public <O extends OpenSamlAuthenticationProvider> O postProcess(O samlAuthProvider) {
                        samlAuthProvider.setAuthoritiesExtractor(authoritiesExtractor);
                        samlAuthProvider.setAuthoritiesMapper(authoritiesMapper);
                        return samlAuthProvider;
                    }
                });

Version

Spring Security 5.2.1 Spring Framework 5.1.6

Comment From: fhanik

Hi @mftruso

The Response doesn't need to contain a Destination attribute. If the IDP omits it, the validation will not take place. Thus, it's optional for the IDP

I'm inclined to say that this is not an issue because there are options. When proxies were relatively new, the backend servers had to have workarounds, like the bean you mention.

Today, proxies should be sending X-Forwarded- headers, and a properly configured backend (ie, using Apache Tomcat, you'd use the RemoteIPValve) should read these values and mask it for the application itself.

In lieu of those headers, creating a simple filter that leverages a HttpServletRequestWrapper will also do the trick.

Comment From: mftruso

@fhanik thanks for the quick response!

Our IDP solution did not have the ability to remove the Destination attribute and institutional policy disallowed changing our Tomcat configuration.

Adding a filter using HttpServletRequestWrapper worked for us in this instance. Thanks again!

Comment From: fhanik

@mftruso Thank you for the feedback! I will close this issue as Will not fix. If you have further questions you can also find us on the Gitter channel - https://gitter.im/spring-projects/spring-security

Comment From: bwiegert

I am running into the same thing I think. I am using Okta and everything works fine on my workstation. When I am trying it on a Tomcat Instance that is behind LB, I am getting Invalid Destination for Saml Response error on the login page. In looking at the comments from @mftruso, I am not sure what would need to be done in a filter using HttpServletRequestWrapper. Would you be able to post yours as an example? Thanks in Advance!

Comment From: bwiegert

FYI - I was able to fix this by adding proxyName, proxyPort, scheme, and secure settings to my Connector defined in my Tomcat Server.xml. So, not a terrible workaround for the issue.