Skip to content

Conversation

ravishanigarapu
Copy link
Member

@ravishanigarapu ravishanigarapu commented May 27, 2025

πŸ“‹ Description

JIRA ID: AMM-1507

Please provide a summary of the change and the motivation behind it. Include relevant context and details.


βœ… Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • πŸ”₯ Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • πŸ›  Refactor (change that is neither a fix nor a new feature)
  • βš™οΈ Config change (configuration file or build script updates)
  • πŸ“š Documentation (updates to docs or readme)
  • πŸ§ͺ Tests (adding new or updating existing tests)
  • 🎨 UI/UX (changes that affect the user interface)
  • πŸš€ Performance (improves performance)
  • 🧹 Chore (miscellaneous changes that don't modify src or test files)

ℹ️ Additional Information

Please describe how the changes were tested, and include any relevant screenshots, logs, or other information that provides additional context.

Summary by CodeRabbit

  • New Features
    • Introduced token blacklisting to enhance security during force logout. Blacklisted tokens are now invalidated and cannot be reused.
  • Bug Fixes
    • Improved token validation to reject blacklisted tokens immediately.
  • Chores
    • Added a new constant for JWT token handling.

Copy link
Contributor

coderabbitai bot commented May 27, 2025

Walkthrough

This change introduces a JWT token blacklist mechanism. It adds a new constant for the JWT token name, implements an in-memory blacklist with expiration, updates the force logout flow to blacklist tokens instead of invalidating cookies, and modifies token validation to reject blacklisted tokens.

Changes

File(s) Summary
src/main/java/com/iemr/common/constant/Constants.java Added JWT_TOKEN constant for JWT token name.
src/main/java/com/iemr/common/controller/users/IEMRAdminController.java Updated force logout to blacklist JWT tokens; added helper to extract token from cookies; added blacklist expiration field.
src/main/java/com/iemr/common/utils/JwtUtil.java Modified validateToken to check blacklist before validating JWT.
src/main/java/com/iemr/common/utils/TokenBlacklist.java Introduced new in-memory token blacklist class with expiration support.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Controller
    participant TokenBlacklist
    participant JwtUtil

    User->>Controller: Force Logout Request
    Controller->>TokenBlacklist: blacklistToken(token)
    Note right of Controller: (Extract token from cookies/header)
    Controller-->>User: Respond (logout complete)

    User->>JwtUtil: validateToken(token)
    JwtUtil->>TokenBlacklist: isTokenBlacklisted(token)
    alt Token is blacklisted
        JwtUtil-->>User: Return null (invalid token)
    else Token not blacklisted
        JwtUtil-->>User: Validate JWT, return claims if valid
    end
Loading

Possibly related PRs

Suggested reviewers

  • drtechie

Poem

In the meadow of code, a new rule takes hold,
Blacklist the tokens, let no old ones unfold.
Cookies may crumble, but blacklists remain,
Guarding our sessions from unauthorized gain.
With constants and helpers, security's tightβ€”
The bunnies rejoice, for logout is right!
πŸ‡βœ¨

✨ Finishing Touches
  • πŸ“ Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share
πŸͺ§ Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

πŸ“œ Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between fac5606 and 7ad12c1.

πŸ“’ Files selected for processing (4)
  • src/main/java/com/iemr/common/constant/Constants.java (1 hunks)
  • src/main/java/com/iemr/common/controller/users/IEMRAdminController.java (4 hunks)
  • src/main/java/com/iemr/common/utils/JwtUtil.java (1 hunks)
  • src/main/java/com/iemr/common/utils/TokenBlacklist.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/com/iemr/common/utils/JwtUtil.java (1)
src/main/java/com/iemr/common/utils/TokenBlacklist.java (1)
  • TokenBlacklist (10-36)
πŸͺ› GitHub Check: SonarCloud
src/main/java/com/iemr/common/utils/TokenBlacklist.java

[notice] 11-11: Injecting data into static fields is not supported by Spring

Remove this injection annotation targeting the static field.

See more on SonarQube Cloud

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: style-check / checkstyle
  • GitHub Check: Package-test
  • GitHub Check: Build
  • GitHub Check: Analyze (java)
πŸ”‡ Additional comments (6)
src/main/java/com/iemr/common/constant/Constants.java (1)

14-14: LGTM! Clean constant addition.

The JWT token constant is properly defined and will improve code maintainability by centralizing the token name reference.

src/main/java/com/iemr/common/utils/JwtUtil.java (2)

60-62: LGTM! Proper blacklist integration.

The blacklist check is correctly placed before token parsing, providing an efficient early exit for blacklisted tokens.


64-64: LGTM! Clean code refactoring.

The chained method call improves readability while maintaining the same functionality.

src/main/java/com/iemr/common/controller/users/IEMRAdminController.java (3)

47-47: LGTM! Proper imports added.

The necessary imports for Constants and TokenBlacklist are correctly added.

Also applies to: 60-60


954-964: LGTM! Well-implemented helper method.

The helper method correctly extracts JWT tokens from cookies with proper null checks and case-insensitive comparison.


945-945: Good practice: commenting out instead of removing.

Keeping the cookie invalidation code commented out allows for easy rollback if needed while transitioning to the token blacklist approach.

Comment on lines 11 to 12
@Value("${jwt.blacklist.expiration}")
private static long BLACK_LIST_EXPIRATION_TIME;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix Spring injection into static field.

Spring cannot inject values into static fields using @Value. This injection will fail and BLACK_LIST_EXPIRATION_TIME will remain 0.

Convert to instance-based approach:

-@Value("${jwt.blacklist.expiration}")
-private static long BLACK_LIST_EXPIRATION_TIME;
+@Component
+public class TokenBlacklist {
+    @Value("${jwt.blacklist.expiration}")
+    private long blackListExpirationTime;
+    
+    private static TokenBlacklist instance;
+    
+    @PostConstruct
+    public void init() {
+        instance = this;
+    }
+    
+    public static void blacklistToken(String token) {
+        instance.blacklistTokenInternal(token);
+    }

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
πŸͺ› GitHub Check: SonarCloud

[notice] 11-11: Injecting data into static fields is not supported by Spring

Remove this injection annotation targeting the static field.

See more on SonarQube Cloud

πŸ€– Prompt for AI Agents
In src/main/java/com/iemr/common/utils/TokenBlacklist.java around lines 11 to
12, the @Value annotation is used on a static field BLACK_LIST_EXPIRATION_TIME,
which Spring cannot inject. To fix this, remove the static modifier from
BLACK_LIST_EXPIRATION_TIME and make it an instance field. Then ensure the class
is a Spring-managed bean so that Spring can inject the value properly at
runtime.

Comment on lines 938 to 943
String token = null;
token = getJwtTokenFromCookies(httpRequest);
if(null == token) {
token = httpRequest.getHeader(Constants.JWT_TOKEN);
}
TokenBlacklist.blacklistToken(token);
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

Add error handling and null check for token blacklisting.

The token blacklisting should handle cases where no token is found and should occur even if the service call fails to ensure security.

 // Perform the force logout logic
-iemrAdminUserServiceImpl.forceLogout(request);
 String token = null;
 token = getJwtTokenFromCookies(httpRequest);
 if(null == token) {
     token = httpRequest.getHeader(Constants.JWT_TOKEN);
 }
-TokenBlacklist.blacklistToken(token);
+if (token != null) {
+    TokenBlacklist.blacklistToken(token);
+}
+
+// Perform the force logout logic
+iemrAdminUserServiceImpl.forceLogout(request);

Committable suggestion skipped: line range outside the PR's diff.

πŸ€– Prompt for AI Agents
In src/main/java/com/iemr/common/controller/users/IEMRAdminController.java
around lines 938 to 943, add a null check before calling
TokenBlacklist.blacklistToken to avoid null pointer exceptions if no token is
found. Also, wrap the blacklisting call in a try-catch block to handle any
exceptions gracefully, ensuring the blacklisting attempt occurs regardless of
service call failures for security purposes.

Copy link

private CookieUtil cookieUtil;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Value("${jwt.blacklist.expiration}")

Check notice

Code scanning / SonarCloud

Injecting data into static fields is not supported by Spring

<!--SONAR_ISSUE_KEY:AZcTCO8gnd2xMJsJ5hH3-->Remove this injection annotation targeting the static field. <p>See more on <a href="https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZcTCO8gnd2xMJsJ5hH3&open=AZcTCO8gnd2xMJsJ5hH3&pullRequest=207">SonarQube Cloud</a></p>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (4)
src/main/java/com/iemr/common/controller/users/IEMRAdminController.java (4)

89-90: ⚠️ Potential issue

Fix Spring injection into static field.

Spring cannot inject values into static fields using @Value. This injection will fail and BLACK_LIST_EXPIRATION_TIME will remain 0, causing incorrect blacklist expiration times.

Convert to instance field approach:

-@Value("${jwt.blacklist.expiration}")
-private static long BLACK_LIST_EXPIRATION_TIME;
+@Value("${jwt.blacklist.expiration}")
+private long blackListExpirationTime;

Then update the usage in the forceLogout method:

-TokenBlacklist.blacklistToken(token,BLACK_LIST_EXPIRATION_TIME);
+TokenBlacklist.blacklistToken(token, blackListExpirationTime);
🧰 Tools
πŸͺ› GitHub Check: SonarCloud

[notice] 89-89: Injecting data into static fields is not supported by Spring

Remove this injection annotation targeting the static field.

See more on SonarQube Cloud


941-946: πŸ› οΈ Refactor suggestion

Add null check before token blacklisting.

The token extraction could return null if no JWT token is found in cookies or headers. Blacklisting a null token should be avoided.

 String token = null;
 token = getJwtTokenFromCookies(httpRequest);
 if(null == token) {
     token = httpRequest.getHeader(Constants.JWT_TOKEN);
 }
-TokenBlacklist.blacklistToken(token,BLACK_LIST_EXPIRATION_TIME);
+if (token != null) {
+    TokenBlacklist.blacklistToken(token, BLACK_LIST_EXPIRATION_TIME);
+}

89-90: ⚠️ Potential issue

Fix Spring injection into static field.

Spring cannot inject values into static fields using @Value. This will cause BLACK_LIST_EXPIRATION_TIME to remain 0, making the blacklist ineffective.

Convert to instance field and ensure proper injection:

-@Value("${jwt.blacklist.expiration}")
-private static long BLACK_LIST_EXPIRATION_TIME;
+@Value("${jwt.blacklist.expiration}")
+private long blackListExpirationTime;

Then update the usage in the forceLogout method to use the instance field.

🧰 Tools
πŸͺ› GitHub Check: SonarCloud

[notice] 89-89: Injecting data into static fields is not supported by Spring

Remove this injection annotation targeting the static field.

See more on SonarQube Cloud


941-946: ⚠️ Potential issue

Add null check and improve error handling for token blacklisting.

The token blacklisting should include proper null checking and error handling. Additionally, for security reasons, the blacklisting should occur regardless of the service call outcome.

Apply this improvement:

+// Blacklist token first for security
+String token = getJwtTokenFromCookies(httpRequest);
+if (token == null) {
+    token = httpRequest.getHeader(Constants.JWT_TOKEN);
+}
+if (token != null && !token.trim().isEmpty()) {
+    try {
+        TokenBlacklist.blacklistToken(token, blackListExpirationTime);
+    } catch (Exception e) {
+        logger.warn("Failed to blacklist token during force logout", e);
+    }
+}
+
 // Perform the force logout logic
 iemrAdminUserServiceImpl.forceLogout(request);
-String token = null;
-token = getJwtTokenFromCookies(httpRequest);
-if(null == token) {
-    token = httpRequest.getHeader(Constants.JWT_TOKEN);
-}
-TokenBlacklist.blacklistToken(token,BLACK_LIST_EXPIRATION_TIME);
🧹 Nitpick comments (1)
src/main/java/com/iemr/common/utils/TokenBlacklist.java (1)

3-6: Clean up unused imports.

Remove the unused import statements to improve code cleanliness.

-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
πŸ“œ Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 7ad12c1 and 1d20ed1.

πŸ“’ Files selected for processing (2)
  • src/main/java/com/iemr/common/controller/users/IEMRAdminController.java (6 hunks)
  • src/main/java/com/iemr/common/utils/TokenBlacklist.java (1 hunks)
🧰 Additional context used
πŸͺ› GitHub Check: SonarCloud
src/main/java/com/iemr/common/controller/users/IEMRAdminController.java

[notice] 89-89: Injecting data into static fields is not supported by Spring

Remove this injection annotation targeting the static field.

See more on SonarQube Cloud

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Package-test
  • GitHub Check: Analyze (java)
  • GitHub Check: Build
πŸ”‡ Additional comments (5)
src/main/java/com/iemr/common/utils/TokenBlacklist.java (3)

15-24: LGTM! Thread-safe implementation with proper validation.

The implementation correctly addresses the previous concerns:

  • Uses ConcurrentHashMap for thread safety
  • Includes proper null/empty validation
  • Calculates expiration timestamp correctly

28-40: LGTM! Proper token validation with cleanup logic.

The method correctly:

  • Validates input parameters
  • Handles expired token cleanup automatically
  • Returns appropriate boolean values for different scenarios

1-42: LGTM! Well-implemented token blacklist utility.

The implementation correctly addresses all the concerns raised in previous reviews:

  • Uses ConcurrentHashMap for thread safety
  • Includes proper null/empty token validation
  • Correctly calculates expiration timestamps (System.currentTimeMillis() + blackListExpirationTime)
  • Implements automatic cleanup of expired tokens

The utility provides a clean and secure in-memory token blacklist mechanism.

src/main/java/com/iemr/common/controller/users/IEMRAdminController.java (2)

957-967: LGTM! Clean helper method implementation.

The method properly extracts JWT tokens from cookies with case-insensitive matching, which aligns with the existing codebase pattern.


957-967: LGTM! Clean helper method implementation.

The getJwtTokenFromCookies method is well-implemented with proper null checking and case-insensitive cookie name matching.

@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Value("${jwt.blacklist.expiration}")
private static long BLACK_LIST_EXPIRATION_TIME;
Copy link
Member

Choose a reason for hiding this comment

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

Each token will have it's own expiration time. Why would this be a constant?

return outputResponse.toString();
}

private String getJwtTokenFromCookies(HttpServletRequest request) {
Copy link
Member

Choose a reason for hiding this comment

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

Look up for current token seems to be getting repeated in so many places. Can we make this a reusable method?

if(null == token) {
token = httpRequest.getHeader(Constants.JWT_TOKEN);
}
TokenBlacklist.blacklistToken(token,BLACK_LIST_EXPIRATION_TIME);
Copy link
Member

Choose a reason for hiding this comment

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

space after comma

Comment on lines +948 to 949
// invalidateJwtCookie(httpRequest, response);

Copy link
Member

Choose a reason for hiding this comment

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

If this method is not going to be used, let's remove it from code.
Setting cookie to null with Set-cookie header is still valid though.


import org.springframework.beans.factory.annotation.Value;

public class TokenBlacklist {
Copy link
Member

Choose a reason for hiding this comment

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

Please use "Deny" instead of Black

public class TokenBlacklist {


// Store blacklisted tokens (in-memory)
Copy link
Member

Choose a reason for hiding this comment

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

What does in memory mean? We should store this in Redis.

Long expiry = blacklistedTokens.get(token);
if (expiry == null) return false;
// If token is expired, remove it from blacklist and treat as not blacklisted
if (System.currentTimeMillis() > expiry) {
Copy link
Member

Choose a reason for hiding this comment

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

This looks like application memory.
Not the best place to keep.
Also it looks like token gets removed only in subsequent request.

@drtechie drtechie closed this May 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants