JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted

32,854

Solution 1

I think EXPIRATION_IN_SECONDS should be in milliseconds, because you're adding it to getTime(), which is in milliseconds. So it should be 120000 actually.

Solution 2

Got the same issue, in my case the token passing from angular has quotes in start/end. Resolved by removing them.

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let token = localStorage.getItem('token');
    if (token) {
        token = token.replace(/^"(.*)"$/, '$1');
    }

    if (token) {
        request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });
    }

    if (!request.headers.has('Content-Type')) {
        request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
    }

    request = request.clone({ headers: request.headers.set('Accept', 'application/json') });
    console.log("............");
    return next.handle(request);
}

   
Share:
32,854
Bilal Ennouali
Author by

Bilal Ennouali

Data Scientist

Updated on April 20, 2022

Comments

  • Bilal Ennouali
    Bilal Ennouali about 2 years

    I am building a server side REST service application. I have a problem with the JWT authentication token. I can get the token easily after sign in (Here I use Postman).

    enter image description here

    But when I am trying to authenticate a request to access a protected REST controller using the same token, I get the following error:

    io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
        at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:354)
        at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
        at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
        at com.configuration.jwt.JwtTokenUtil.extractClaims(JwtTokenUtil.java:104)
        at com.configuration.jwt.JwtTokenUtil.getUsernameFromToken(JwtTokenUtil.java:39)
        at com.configuration.jwt.JwtAuthenticationFilter.doFilterInternal(JwtAuthenticationFilter.java:44)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    ...
    

    it's like the application doesn't remember the token it generated. Here is the get request from Postman that generated this error:

    enter image description here

    I guess the source of the exception is from the method extractClaims of my class JwtTokenUtil:

    @Component
    public final class JwtTokenUtil {
    
        public static final int EXPIRATION_IN_SECONDS = 120;
    
        private static final String JWT_SECRET = "Some$ecretKey";
    
        private Clock clock = DefaultClock.INSTANCE;
    
        @Value("${jwt.secret}")
        private String secret;
    
        @Value("${jwt.expiration}")
        private Long expiration;
    
        private JwtTokenUtil() {
            // Hide default constructor
        }
    
        public String getUsernameFromToken(String token) {
            return extractClaims(token).getSubject();
        }
    
        public Boolean validateToken(String token, UserDetails userDetails) {
            UserDetailsImp user = (UserDetailsImp) userDetails;
            final String username = getUsernameFromToken(token);
            return (username.equals(user.getUsername()) && !isTokenExpired(token));
        }
    
        public Date getIssuedAtDateFromToken(String token) {
            return extractClaims(token).getIssuedAt();
        }
    
        public String generateToken(UserDetails userDetails) {
            Map<String, Object> claims = new HashMap<String, Object>();
            return doGenerateToken(claims, userDetails.getUsername());
        }
    
        private String doGenerateToken(Map<String, Object> claims, String subject) {
            final Date createdDate = clock.now();
            final Date expirationDate = calculateExpirationDate(createdDate);
    
            return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(createdDate)
                    .setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
        }
    
        private Date calculateExpirationDate(Date createdDate) {
            return new Date(createdDate.getTime() + expiration * 1000);
        }
    
        public static String createToken(String username, Date issueDate) {
            String jwtToken = Jwts.builder().setSubject(username).setIssuedAt(issueDate)
                    .setExpiration(new Date(issueDate.getTime() + EXPIRATION_IN_SECONDS))
                    .signWith(SignatureAlgorithm.HS512, JWT_SECRET).compact();
    
            return jwtToken;
        }
    
        public static String getSubject(String token) {
            Claims claims = extractClaims(token);
            return claims.getSubject();
        }
    
        public static String refreshToken(String token, long expirationInSeconds) {
            final Claims claims = extractClaims(token);
    
            Date now = new Date();
            claims.setIssuedAt(now);
            claims.setExpiration(new Date(now.getTime() + EXPIRATION_IN_SECONDS));
    
            return createTokenFromClaims(claims);
        }
    
        public static boolean isTokenExpired(String token) {
            final Claims claims = extractClaims(token);
            Date now = new Date();
    
            return now.after(claims.getExpiration());
        }
    
        private static String createTokenFromClaims(Claims claims) {
            return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, JWT_SECRET).compact();
        }
    
        private static Claims extractClaims(String token) {
            return Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token).getBody();
        }
    
    }
    

    This is my JwtAuthenticationFilter class:

    public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Autowired
        private JwtTokenUtil jwtTokenUtil;
    
        @Override
        protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
                throws IOException, ServletException {
    
            String header = req.getHeader("Authorization");
            String username = null;
            String authToken = null;
    
            if (header != null && header.startsWith("Bearer ")) {
    
                authToken = header.replace("Bearer ", "");
    
                try {
    
                    username = jwtTokenUtil.getUsernameFromToken(authToken);
    
                } catch (IllegalArgumentException e) {
    
                    logger.error("an error occured during getting username from token", e);
    
                } catch (ExpiredJwtException e) {
    
                    logger.warn("the token is expired and not valid anymore", e);
                }
            } else {
                logger.warn("couldn't find bearer string, will ignore the header");
            }
    
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
    
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
    
                if (jwtTokenUtil.validateToken(authToken, userDetails)) {
    
                    String role = "";
    
                    role = userDetails.getAuthorities().size() > 1 ? "ROLE_ADMIN" : "ROLE_TOURIST";
    
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                            userDetails, null, Arrays.asList(new SimpleGrantedAuthority(role)));
    
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));
    
                    logger.info("authenticated user " + username + ", setting security context");
    
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
    
            chain.doFilter(req, res);
        }
    }
    

    and I don't know if the sign in controller have anything to do with the issue, but here is the code for it anyway:

    @PostMapping(value = "/signin")
        public ResponseEntity<?> signin(@Valid @RequestBody LoginForm loginForm) throws AuthenticationException {
    
            final Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(loginForm.getUsername(), loginForm.getPassword()));
            SecurityContextHolder.getContext().setAuthentication(authentication);
    
            final UserDetails user = userService.loadUserByUsername(loginForm.getUsername());
    
            final String token = jwtTokenUtil.generateToken(user);
    
            return ResponseEntity.ok(new JwtResponse(token, user.getUsername(), user.getAuthorities()));
        }
    

    I hope somebody can help.

    • Paul Gobin
      Paul Gobin over 3 years
      I was running into the same problem and this
  • Paul Gobin
    Paul Gobin over 3 years
    I was facing the same issue. stackoverflow.com/questions/57297249/… Solved it for me.