I've been struggling with this for too many days and could really use some help. I am trying to use spring-boot 3.3.0 to create an unusual authentication provider. I need it to work like this:
public class RefererAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("**","GET");
public RefererAuthenticationFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String url = request.getHeader( "Referer" );
String fetchDest = request.getHeader( "Sec-Fetch-Dest" );
String id = request.getParameter( "id" );
String name = request.getParameter( "name" );
String email = request.getParameter( "email" );
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
Referer referer = new Referer();
referer.setUrl(url);
referer.setFetchDest(fetchDest);
referer.setId(id);
referer.setName(name);
referer.setEmail(email);
referer.setIpAddress(ipAddress);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( referer, null );
return this.getAuthenticationManager().authenticate(authRequest);
}
}
@Component
public class RefererAuthenticationProvider implements AuthenticationProvider {
@Value("${referer}")
private String validReferer;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken challengeAuth = (UsernamePasswordAuthenticationToken) authentication;
log.debug( "starting authentication ..." );
Referer referer = (Referer) challengeAuth.getPrincipal();
if ( referer.getUrl().startsWith(validReferer) &&
referer.getFetchDest().equals("iframe") ) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<SimpleGrantedAuthority>();
grantedAuthorities.add(authority);
return new UsernamePasswordAuthenticationToken( referer, null, grantedAuthorities);
}
throw new BadCredentialsException("referer did not match within the iframe");
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals( UsernamePasswordAuthenticationToken.class );
}
}
@Configuration
@AllArgsConstructor
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Autowired
RefererAuthenticationProvider refererAuthenticationProvider;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(refererAuthenticationProvider);
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
RefererAuthenticationFilter refererAuthenticationFilter = new RefererAuthenticationFilter(authenticationManager);
refererAuthenticationFilter.setAuthenticationManager(authenticationManager);
return http
.csrf(AbstractHttpConfigurer::disable)
.addFilterAt(refererAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests( request -> { request
.anyRequest().authenticated();
})
.headers( headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable) )
.build();
}
}
This ends up just telling me ...
java.lang.NullPointerException: Cannot invoke "org.springframework.security.authentication.AuthenticationManager.authenticate(org.springframework.security.core.Authentication)"
because the return value of "com.example.security.filter.RefererAuthenticationFilter.getAuthenticationManager()" is null
I thought I could use the default AuthenticationManager.
To make the situation even more challenging I want multiple AuthenticationProviders. I want this one to be tried first, followed by okta-spring-boot-starter 3.0.6. I'm not sure if these can co-exist in the same AuthenticationManager, who provides it, and where exactly I would try to lay down my RefererAuthenticationFilter in the filter chain with Okta, which looks like
org.springframework.security.web.session.DisableEncodeUrlFilter@4bf806ab,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@2e7b49c0,
org.springframework.security.web.context.SecurityContextHolderFilter@1e26110d,
org.springframework.security.web.header.HeaderWriterFilter@1e1b3778,
org.springframework.web.filter.CorsFilter@5dd7342f,
org.springframework.security.web.authentication.logout.LogoutFilter@1865d91,
org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter@5d116afe,
org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter@1780aa5d,
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@264c6be6,
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@3f60f8c4,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@4650f833,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@3acc81dd,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@34a66cdd,
org.springframework.security.web.access.ExceptionTranslationFilter@206f53b0,
org.springframework.security.web.access.intercept.AuthorizationFilter@fbb75c2
The NullPointerException
you are encountering is due to the AuthenticationManager
not being properly initialized in your RefererAuthenticationFilter
. To resolve this issue, you need to explicitly set the AuthenticationManager
in your custom filter. Additionally, to support multiple AuthenticationProviders
, you can configure Spring Security to use a list of providers.
Here’s a updated version of your configuration:
public class RefererAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("**","GET");
public RefererAuthenticationFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String url = request.getHeader("Referer");
String fetchDest = request.getHeader("Sec-Fetch-Dest");
String id = request.getParameter("id");
String name = request.getParameter("name");
String email = request.getParameter("email");
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
Referer referer = new Referer();
referer.setUrl(url);
referer.setFetchDest(fetchDest);
referer.setId(id);
referer.setName(name);
referer.setEmail(email);
referer.setIpAddress(ipAddress);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(referer, null);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
Configure multiple AuthenticationProviders
and integrate them with the AuthenticationManager
in your SecurityConfig
:
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Autowired
private RefererAuthenticationProvider refererAuthenticationProvider;
@Autowired
private OktaOAuth2AuthenticationProvider oktaAuthenticationProvider; // Assuming you have a custom Okta provider
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(refererAuthenticationProvider);
authenticationManagerBuilder.authenticationProvider(oktaAuthenticationProvider);
AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
RefererAuthenticationFilter refererAuthenticationFilter = new RefererAuthenticationFilter(authenticationManager);
return http
.csrf(AbstractHttpConfigurer::disable)
.addFilterAt(refererAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(request -> request.anyRequest().authenticated())
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.build();
}
@Bean
public RefererAuthenticationProvider refererAuthenticationProvider() {
return new RefererAuthenticationProvider();
}
@Bean
public OktaOAuth2AuthenticationProvider oktaAuthenticationProvider() {
return new OktaOAuth2AuthenticationProvider(); // Customize as needed
}
}
making sureRefererAuthenticationProvider
and Okta provider implementations are properly defined:
@Component
public class RefererAuthenticationProvider implements AuthenticationProvider {
@Value("${referer}")
private String validReferer;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken challengeAuth = (UsernamePasswordAuthenticationToken) authentication;
log.debug("starting authentication ...");
Referer referer = (Referer) challengeAuth.getPrincipal();
if (referer.getUrl().startsWith(validReferer) && referer.getFetchDest().equals("iframe")) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(authority);
return new UsernamePasswordAuthenticationToken(referer, null, grantedAuthorities);
}
throw new BadCredentialsException("referer did not match within the iframe");
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}