From 23b6b2603bd4f8b81bae59183279830ebd0945bd Mon Sep 17 00:00:00 2001 From: JinnJarBurger Date: Wed, 23 Oct 2024 19:50:34 +0600 Subject: [PATCH] feat: Add custom JWT error messages for authentication failures (issue #22) --- .../security/JwtAuthenticationEntryPoint.java | 46 +++++++++++++++++++ .../WebSecurityConfig.java | 8 +++- 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/main/java/dev/nmarulo/depensaapp/security/JwtAuthenticationEntryPoint.java rename src/main/java/dev/nmarulo/depensaapp/{configuration => security}/WebSecurityConfig.java (93%) diff --git a/src/main/java/dev/nmarulo/depensaapp/security/JwtAuthenticationEntryPoint.java b/src/main/java/dev/nmarulo/depensaapp/security/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..b67a99a --- /dev/null +++ b/src/main/java/dev/nmarulo/depensaapp/security/JwtAuthenticationEntryPoint.java @@ -0,0 +1,46 @@ +package dev.nmarulo.depensaapp.security; + +import com.fasterxml.jackson.databind.ObjectMapper; +import dev.nmarulo.depensaapp.commons.dtos.ErrorRes; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException; +import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + + private final AuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint(); + + private final ObjectMapper mapper; + + @Override + public void commence(HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) throws IOException, ServletException { + + this.delegate.commence(request, response, authException); + + if (authException instanceof InvalidBearerTokenException bearerTokenException) { + var problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNAUTHORIZED, + bearerTokenException.getMessage()); + + problemDetail.setTitle("Invalid Token"); + + response.setContentType("application/json"); + + mapper.writeValue(response.getWriter(), new ErrorRes(problemDetail)); + } + } + +} diff --git a/src/main/java/dev/nmarulo/depensaapp/configuration/WebSecurityConfig.java b/src/main/java/dev/nmarulo/depensaapp/security/WebSecurityConfig.java similarity index 93% rename from src/main/java/dev/nmarulo/depensaapp/configuration/WebSecurityConfig.java rename to src/main/java/dev/nmarulo/depensaapp/security/WebSecurityConfig.java index f145f47..10fadb2 100644 --- a/src/main/java/dev/nmarulo/depensaapp/configuration/WebSecurityConfig.java +++ b/src/main/java/dev/nmarulo/depensaapp/security/WebSecurityConfig.java @@ -1,4 +1,4 @@ -package dev.nmarulo.depensaapp.configuration; +package dev.nmarulo.depensaapp.security; import com.nimbusds.jose.KeyLengthException; import com.nimbusds.jose.crypto.MACSigner; @@ -8,6 +8,7 @@ import dev.nmarulo.depensaapp.app.users.UserRepository; import dev.nmarulo.depensaapp.commons.component.LocalMessage; import dev.nmarulo.depensaapp.commons.converter.CustomJwtAuthenticationConverter; +import dev.nmarulo.depensaapp.configuration.AppProperties; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.ArrayUtils; import org.springframework.context.annotation.Bean; @@ -43,6 +44,8 @@ public class WebSecurityConfig { private final LocalMessage localeMessage; + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.securityMatcher(appProperties.getPathPrefix() + "/**") @@ -105,7 +108,8 @@ public JwtDecoder jwtDecoder() throws KeyLengthException { private Customizer> configOAuth2() { final var converter = new CustomJwtAuthenticationConverter(this.userRepository, this.localeMessage); - return oauth -> oauth.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(converter)); + return oauth -> oauth.authenticationEntryPoint(jwtAuthenticationEntryPoint) + .jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(converter)); } }