5

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();
  }
}
Tom Panning
  • 4,613
  • 2
  • 26
  • 47

1 Answers1

9

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.

Tom Panning
  • 4,613
  • 2
  • 26
  • 47
  • Thank you for showing the `.saml2Login(saml2 -> saml2.authenticationManager...)` technique inside of the `configure(HttpSecurity..)` method. I had written a custom `AthenticationProvider` that internally uses `OpenSamlAuthenticationProvider` instance variable. I needed to use an instance variable because `OpenSamlAuthenticationProvider` is declared a final and so cannot be overridden. I tried to use the `configure(AuthenticationManagerBuilder)` override method to inject the provider and it was not working. Putting in `.saml2Login(saml2->..` finally worked. – Doraemon Mar 27 '22 at 06:21