I’m creating an API gateway with Spring Boot, i’m using a jwt token to validate the user session and i came up with this issue with the SpringSecurity, so here is my SecurityFilterChain
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Value("${cognito.issuer}")
private String jwkIssuerUri;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling( exceptions -> exceptions
.authenticationEntryPoint(jwtAuthenticationEntryPoint))
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/auth/callback").permitAll()
.anyRequest().authenticated()
).oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.jwt(jwt ->
jwt.decoder(jwtDecoder())
)
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return JwtDecoders.fromOidcIssuerLocation(jwkIssuerUri);
}
}
As you can see i’m trying to handle the validation exception by implementing AuthenticationEntryPoint, this is my class:
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException {
logger.debug("Commence method called. Handling exception.");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
if("ExpiredJwtException".equals(authException.getClass().getSimpleName())) {
logger.debug("Authentication error:n", authException);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(String.format("{"error": "%s", "message": "%s"}",
HttpServletResponse.SC_UNAUTHORIZED, "Provided authorization token is expired"));
} else if("InsufficientAuthenticationException".equals(authException.getClass().getSimpleName())) {
logger.debug("Athentication error:n", authException);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(String.format("{"error": "%s", "message": "%s"}",
HttpServletResponse.SC_UNAUTHORIZED, "Authorization token must be provided to reach this endpoint"));
} else {
logger.debug("Authentication error:n", authException);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(String.format("{"error": "%s", "message": "%s"}",
HttpServletResponse.SC_UNAUTHORIZED, "Cannot validate the token"));
}
}
}
Now i was able to handle the InsufficientAuthenticationException
but other exceptions, like ExpiredJwtException
are not handled by this class but they’re beeing handled by spring security, here the logs of this case:
2024-05-09 15:12 |DEBUG| org.springframework.security.web.context.HttpSessionSecurityContextRepository.saveContext()
Did not store empty SecurityContext
2024-05-09 15:12 |DEBUG| org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter()
Cleared SecurityContextHolder to complete request
2024-05-09 15:13 |DEBUG| org.springframework.security.web.FilterChainProxy.doFilterInternal()
Securing GET /test
2024-05-09 15:13 |DEBUG| org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter()
Set SecurityContextHolder to empty SecurityContext
2024-05-09 15:13 |DEBUG| org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt()
Failed to authenticate since the JWT was invalid
2024-05-09 15:13 |DEBUG| org.springframework.security.web.context.HttpSessionSecurityContextRepository.saveContext()
Did not store empty SecurityContext
2024-05-09 15:13 |DEBUG| org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter()
Cleared SecurityContextHolder to complete request
I’ve tried to add an AccessDeniedHandler and even combine it with the AuthenticationEntryPoint but it didn’t work. Also i’ve tried to set up a custom filter that throws exceptions when they are raised, this didn’t work too. I want to be able to custom handling those authentication exception and to not let spring boot handle them. What it does is just changing the Authentication header but it isn’t what i want, i want to have a clear response from the APIs.