Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions src/main/java/com/iemr/tm/utils/JwtUtil.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.iemr.tm.utils;

import java.security.Key;
import java.util.Date;
import java.util.function.Function;
import javax.crypto.SecretKey;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

@Component
Expand All @@ -18,10 +17,11 @@ public class JwtUtil {
@Value("${jwt.secret}")
private String SECRET_KEY;

private static final long EXPIRATION_TIME = 24L * 60 * 60 * 1000; // 1 day in milliseconds
@Autowired
private TokenDenylist tokenDenylist;

// Generate a key using the secret
private Key getSigningKey() {
private SecretKey getSigningKey() {
if (SECRET_KEY == null || SECRET_KEY.isEmpty()) {
throw new IllegalStateException("JWT secret key is not set in application.properties");
}
Expand All @@ -31,7 +31,20 @@ private Key getSigningKey() {
// Validate and parse JWT Token
public Claims validateToken(String token) {
try {
return Jwts.parser().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
Claims claims = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();

String jti = claims.getId();

// Check if token is denylisted (only if jti exists)
if (jti != null && tokenDenylist.isTokenDenylisted(jti)) {
return null;
}

return claims;
} catch (Exception e) {
return null; // Handle token parsing/validation errors
}
Expand All @@ -43,10 +56,14 @@ public String extractUsername(String token) {

public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
return claims != null ? claimsResolver.apply(claims) : null;
}

private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/iemr/tm/utils/TokenDenylist.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.iemr.tm.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class TokenDenylist {
private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

private static final String PREFIX = "denied_";

@Autowired
private RedisTemplate<String, Object> redisTemplate;

private String getKey(String jti) {
return PREFIX + jti;
}

// Add a token's jti to the denylist with expiration time
public void addTokenToDenylist(String jti, Long expirationTime) {
if (jti == null || jti.trim().isEmpty()) {
return;
}
if (expirationTime == null || expirationTime <= 0) {
throw new IllegalArgumentException("Expiration time must be positive");
}

try {
String key = getKey(jti); // Use helper method to get the key
redisTemplate.opsForValue().set(key, " ", expirationTime, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new RuntimeException("Failed to denylist token", e);
}
}
Comment on lines +25 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Improve exception handling consistency.

The method has good input validation but lacks error logging consistency compared to other methods in the class.

Apply this diff to add consistent error logging:

     } catch (Exception e) {
+        logger.error("Failed to add token to denylist for jti: " + jti, e);
         throw new RuntimeException("Failed to denylist token", e);
     }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void addTokenToDenylist(String jti, Long expirationTime) {
if (jti == null || jti.trim().isEmpty()) {
return;
}
if (expirationTime == null || expirationTime <= 0) {
throw new IllegalArgumentException("Expiration time must be positive");
}
try {
String key = getKey(jti); // Use helper method to get the key
redisTemplate.opsForValue().set(key, " ", expirationTime, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new RuntimeException("Failed to denylist token", e);
}
}
public void addTokenToDenylist(String jti, Long expirationTime) {
if (jti == null || jti.trim().isEmpty()) {
return;
}
if (expirationTime == null || expirationTime <= 0) {
throw new IllegalArgumentException("Expiration time must be positive");
}
try {
String key = getKey(jti); // Use helper method to get the key
redisTemplate.opsForValue().set(key, " ", expirationTime, TimeUnit.MILLISECONDS);
} catch (Exception e) {
logger.error("Failed to add token to denylist for jti: " + jti, e);
throw new RuntimeException("Failed to denylist token", e);
}
}
πŸ€– Prompt for AI Agents
In src/main/java/com/iemr/tm/utils/TokenDenylist.java between lines 25 and 39,
the addTokenToDenylist method catches exceptions but does not log errors
consistently with other methods. Modify the catch block to log the exception
details using the class logger before throwing the RuntimeException, ensuring
error logging is consistent across the class.


// Check if a token's jti is in the denylist
public boolean isTokenDenylisted(String jti) {
if (jti == null || jti.trim().isEmpty()) {
return false;
}
try {
String key = getKey(jti); // Use helper method to get the key
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
} catch (Exception e) {
logger.error("Failed to check denylist status for jti: " + jti, e);
// In case of Redis failure, consider the token as not denylisted to avoid blocking all requests
return false;
}
}
}
Loading