My backend is a Spring Boot application and on the frontend I am using Angular.
I want to achieve the following authentication process:
- Use SSO with the Windows credentials using Kerberos/Spnego
- Some users are not included in LDAP and can't be authenticated with SSO. These users are created by other users and stored in a Postgres database and should be able to authenticate via username/password.
- If the user is currently not in the VPN/domain (and therefore SSO is not working) the user should also be able to authenticate via username/password with LDAP verificiation in the backend.
I was able to achieve SSO and also authentication via username+password. However, I do not know how to combine these two approaches and use the authentication via username+password as a fallback.
This is my current SecurityConfig:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
KerberosAuthenticationProvider kerberosAuthenticationProvider = spnegoConfig.kerberosAuthenticationProvider();
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = spnegoConfig.kerberosServiceAuthenticationProvider();
ProviderManager providerManager = new ProviderManager(kerberosAuthenticationProvider, kerberosServiceAuthenticationProvider);
http
.exceptionHandling()
.authenticationEntryPoint(spnegoConfig.spnegoEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.httpBasic(withDefaults())
.authorizeHttpRequests((authRegistry) ->
authRegistry
.antMatchers("/index.html", "/", "/login").permitAll()
.anyRequest().authenticated()
)
// TODO: chaining of authenticationProviders do not work with SSO
.authenticationProvider(kerberosAuthenticationProvider)
.authenticationProvider(kerberosServiceAuthenticationProvider)
.authenticationProvider(customDaoAuthenticationProvider)
.authenticationProvider(customLdapAuthenticationProvider)
.addFilterBefore(spnegoConfig.spnegoAuthenticationProcessingFilter(providerManager),
BasicAuthenticationFilter.class)
.logout()
.permitAll()
.and()
.csrf().disable();
return http.build();
}
This is my SpnegoConfig:
@Configuration
@RequiredArgsConstructor
public class SpnegoConfig {
@Value("${servicePrincipal}")
private String servicePrincipal;
@Value("${keyTabLocation}")
private String keyTabLocation;
private final CustomLdapUserDetailsService customLdapUserDetailsService;
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
provider.setKerberosClient(client);
provider.setUserDetailsService(customLdapUserDetailsService);
return provider;
}
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(customLdapUserDetailsService);
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(servicePrincipal);
ticketValidator.setKeyTabLocation(new ClassPathResource(keyTabLocation));
return ticketValidator;
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint();
}
}