Principal Object is null when jwt token is not valid

Viewed 1418

I'm using spring security to authenticate users using jwt tokens.
The authentication works fine, when the token is malformed or expired i get a 403 Http status, as shown in the below configuration:

 @Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
    return http
            .exceptionHandling()
            .authenticationEntryPoint((swe, e) -> {
                return Mono.fromRunnable(() -> {
                    swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); 
                });
            }).accessDeniedHandler((swe, e) -> {
                return Mono.fromRunnable(() -> {
                    swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                });
            }).and()
            .csrf().disable()
            .formLogin().disable()
            .httpBasic().disable()
            .authenticationManager(authenticationManager)
            .securityContextRepository(securityContextRepository)
            .authorizeExchange()
            .pathMatchers(HttpMethod.OPTIONS).permitAll()
            .pathMatchers("/**").permitAll()
            .anyExchange().authenticated()
            .and().build();
}

However when i use Principal or Authentication from spring security context to get user information from the jwt token, like in the below code:

@ResponseStatus(HttpStatus.OK)
@GetMapping("/me")
public Mono<ClientDTO> find(final Principal principal) {
    return this.clientService.findByMail(principal.getName());
}

If the token is malformed or expired, i get a null pointer exception from the Principal object and an 500 https status.

2 Answers

I want to apologize right away if my answer does not help with your problem, since I do not see all your code.


I have a suitable project where I was able to check the work with the Principal object in similar conditions and everything works for me. Therefore, the problem is not in it.


You probably throw an exception in the token validation method. Something like that:

public boolean validateToken(String token) {
    try {
        Jws<Claims> claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
        return true;
    } catch (JwtException | IllegalArgumentException e) {
        throw new JwtAuthenticationException("JWT token is expired or invalid", e);
    }
}

In this case, the exception is caught before you catch it in the configuration. The solution may be to reject a new exception in the token validation method in favor of outputting information to the log:

public boolean validateToken(String authToken) {
      try {
         Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);
         return true;
      } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
         log.info("Invalid JWT signature.");
         log.trace("Invalid JWT signature trace: {}", e);
      } catch (ExpiredJwtException e) {
         log.info("Expired JWT token.");
         log.trace("Expired JWT token trace: {}", e);
      } catch (UnsupportedJwtException e) {
         log.info("Unsupported JWT token.");
         log.trace("Unsupported JWT token trace: {}", e);
      } catch (IllegalArgumentException e) {
         log.info("JWT token compact of handler are invalid.");
         log.trace("JWT token compact of handler are invalid trace: {}", e);
      }
      return false;
}

I will be glad if this helps you. Otherwise, me need more information about your code.

@ResponseStatus(HttpStatus.OK)
@GetMapping("/me")
public Mono<ClientDTO> find(final Principal principal) { 

if(principal!=null) return this.clientService.findByMail(principal.getName());
else throw new CustomTokenExpiredException();
}

and remove
.pathMatchers("/**").permitAll()

Related