Search code examples
spring-securityspring-security-saml2

Customizing OpenSaml4AuthenticationProvider in Spring Security SAML2


I need to use a legacy UserDetailsService with Spring Security SAML2, so I'm following these instructions from Spring. However, I get an error when I just try to replace the AuthenticationProvider with the supposedly "default" one according to that documentation:

public class WigWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider();
    // I've tried removing these 2 lines and I get the same error
    authenticationProvider.setAssertionValidator(OpenSaml4AuthenticationProvider.createDefaultAssertionValidator());
    authenticationProvider.setResponseAuthenticationConverter(OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter());

    httpSecurity.authorizeRequests(authz -> authz.anyRequest().authenticated())
      .saml2Login(saml2 -> saml2.authenticationManager(new ProviderManager(authenticationProvider)));
  }
}

When I do this, I get the following error when I try to authenticate:

java.lang.NoSuchMethodError: org.opensaml.saml.saml2.assertion.SAML20AssertionValidator.<init>(Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Lorg/opensaml/saml/saml2/assertion/AssertionValidator;Lorg/opensaml/xmlsec/signature/support/SignatureTrustEngine;Lorg/opensaml/xmlsec/signature/support/SignaturePrevalidator;)V
    at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider$SAML20AssertionValidators$3.<init>(OpenSaml4AuthenticationProvider.java:732)
    at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider$SAML20AssertionValidators.<clinit>(OpenSaml4AuthenticationProvider.java:731)
    at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.lambda$createDefaultAssertionSignatureValidator$8(OpenSaml4AuthenticationProvider.java:572)
    at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.lambda$createAssertionValidator$11(OpenSaml4AuthenticationProvider.java:654)
    at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.process(OpenSaml4AuthenticationProvider.java:495)
    at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.authenticate(OpenSaml4AuthenticationProvider.java:448)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
    at org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter.attemptAuthentication(Saml2WebSsoAuthenticationFilter.java:113)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:222)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)

But when I use the same code without setting the authenticationManager, then the SAML authentication works fine. (Any page that wants to use my custom UserDetails fails of course, because it's not being populated, but all the SAML authentication steps are working fine.):

public class WigWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {

    httpSecurity.authorizeRequests(authz -> authz.anyRequest().authenticated())
      .saml2Login();
  }
}

Solution

  • It turns out that I was using org.opensaml:opensaml-api 3.4.6, and you need to be using 4.x to use the class OpenSaml4AuthenticationProvider. If you're using 3.x you need to use the deprecated class OpenSamlAuthenticationProvider. I wasn't able to upgrade the opensaml dependency because I'm using Java 8, so this is the code that works for me:

    public class WigWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
      @Override
      protected void configure(HttpSecurity httpSecurity) throws Exception {
        // This class is deprecated, but you have to use it if you're using OpenSAML < 4.0
        OpenSamlAuthenticationProvider authenticationProvider = new OpenSamlAuthenticationProvider();
        authenticationProvider.setAssertionValidator(OpenSamlAuthenticationProvider.createDefaultAssertionValidator());
        authenticationProvider.setResponseAuthenticationConverter(OpenSamlAuthenticationProvider.createDefaultResponseAuthenticationConverter());
    
        httpSecurity.authorizeRequests(authz -> authz.anyRequest().authenticated())
          .saml2Login(saml2 -> saml2.authenticationManager(new ProviderManager(authenticationProvider)));
      }
    }
    

    I finally found the answer when I discovered that that is what Saml2LoginConfigurer does internally.