Summary
- The SAML Extension had a specific bean for applications behind an LB
- OpenSamlAuthenticationProvider.validateSaml2Response auth exception shown below due to mismatch in HttpServletRequest URL and Destination URL in the IDP:
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.