Improve JWT error handling and logging#2970
Conversation
📝 WalkthroughWalkthroughJwtAuthInterceptor replaces wildcard imports with explicit ones, adds ERROR_JWT_INVALID_SIGNATURE, changes handleRequest to map JWT failures (parsing, invalid signature, validation, missing) to ProblemDetails.security(...) with HTTP 401 and updated logs, logs null JWTs in handleJwt, and updates tests to match these behaviors. ChangesJWT Error Handling Improvements
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@core/src/main/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptor.java`:
- Line 131: The log call in JwtAuthInterceptor uses log.info("Could not retrieve
JWT: ", e.getMessage()) without a "{}" placeholder so the message is never
logged; update the log invocation in JwtAuthInterceptor (the log.info call) to
include a placeholder and/or pass the exception correctly, e.g. use
log.info("Could not retrieve JWT: {}", e.getMessage()) or log.info("Could not
retrieve JWT", e) to ensure the error message or stack trace is actually
recorded.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9e0edaa5-0999-49b6-a0f9-413924998e88
📒 Files selected for processing (1)
core/src/main/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptor.java
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
core/src/main/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptor.java (2)
123-125:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid exposing raw JWT validation exception text to clients.
detail(ERROR_VALIDATION_FAILED + ": " + e.getMessage())leaks internal validator output in the HTTP response. Keep client detail generic and retain specifics in logs only.🔧 Proposed fix
} catch (InvalidJwtException e) { log.info("JWT validation failed: {}", e.getErrorDetails()); security(router.getConfiguration().isProduction(), "jwt-auth") - .detail(ERROR_VALIDATION_FAILED + ": " + e.getMessage()) + .detail(ERROR_VALIDATION_FAILED) .addSubSee(ERROR_VALIDATION_FAILED_ID) .stacktrace(false) .status(401) .buildAndSetResponse(exc); return RETURN;Based on learnings, HTTP
ProblemDetails.detail(...)should avoid exposing internal implementation details to clients when a generic message is sufficient.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/main/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptor.java` around lines 123 - 125, In JwtAuthInterceptor, the security(...).detail(ERROR_VALIDATION_FAILED + ": " + e.getMessage()) call exposes internal validator text to clients; change it to set a generic client-facing detail (e.g. ERROR_VALIDATION_FAILED or a short message) and move the full exception message into a server log (use logger.error or similar) referencing the exception (e) so the detailed error is recorded but not returned; update the invocation around security(router.getConfiguration().isProduction(), "jwt-auth").detail(...) and keep addSubSee(ERROR_VALIDATION_FAILED_ID) as-is.Source: Learnings
96-101:⚠️ Potential issue | 🔴 CriticalPropagate
JWTException’s structured id intoProblemDetails(addSubSee)
JWTExceptionexposes the structured error id viagetId(), butJwtAuthInterceptor’scatch (JWTException e)branch (lines 96-101) only sets.detail(e.getMessage())and never calls.addSubSee(e.getId()); this drops the IDs from thrownnew JWTException(..., ERROR_JWT_NOT_FOUND_ID)/new JWTException(..., ERROR_UNKNOWN_KEY_ID)paths.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/main/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptor.java` around lines 96 - 101, In JwtAuthInterceptor's catch (JWTException e) block, include the exception's structured id into the ProblemDetails by calling addSubSee(e.getId()) on the security(...) builder before buildAndSetResponse(exc); specifically, after calling .detail(e.getMessage()) (and only if e.getId() is non-null/non-empty), invoke .addSubSee(e.getId()) on the same builder chain so JWTException.getId() values (e.g. ERROR_JWT_NOT_FOUND_ID / ERROR_UNKNOWN_KEY_ID) are propagated into the response.
🧹 Nitpick comments (1)
core/src/test/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptorTest.java (1)
117-121: ⚡ Quick winLock the new 401 contract with explicit status assertions.
These cases now validate detail prefixes but still only check
isUserError(). Since this PR’s behavior change is specifically401, assert status code explicitly to prevent silent regressions to other 4xx codes.✅ Minimal test hardening
assertTrue(exc.getResponse().isUserError()); + assertEquals(401, exc.getResponse().getStatusCode()); assertNull(exc.getProperties().get("jwt")); var detail = (String) unpackBody(exc).get("detail"); assertTrue(detail.startsWith(ERROR_JWT_INVALID_SIGNATURE));Also applies to: 170-174, 186-190, 202-206
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/test/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptorTest.java` around lines 117 - 121, The tests in JwtAuthInterceptorTest currently only assert exc.getResponse().isUserError(); update them to lock the 401 contract by asserting the explicit HTTP status instead (e.g. assertEquals(401, exc.getResponse().getStatus())) wherever the test checks isUserError() — specifically in the block that verifies ERROR_JWT_INVALID_SIGNATURE (uses unpackBody(exc) and variable exc) and the other similar blocks noted (lines referenced in the review: 170-174, 186-190, 202-206); keep the existing checks for null jwt and detail prefix assertions (ERROR_JWT_INVALID_SIGNATURE) and just add the explicit status assertion to prevent regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@core/src/main/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptor.java`:
- Around line 123-125: In JwtAuthInterceptor, the
security(...).detail(ERROR_VALIDATION_FAILED + ": " + e.getMessage()) call
exposes internal validator text to clients; change it to set a generic
client-facing detail (e.g. ERROR_VALIDATION_FAILED or a short message) and move
the full exception message into a server log (use logger.error or similar)
referencing the exception (e) so the detailed error is recorded but not
returned; update the invocation around
security(router.getConfiguration().isProduction(), "jwt-auth").detail(...) and
keep addSubSee(ERROR_VALIDATION_FAILED_ID) as-is.
- Around line 96-101: In JwtAuthInterceptor's catch (JWTException e) block,
include the exception's structured id into the ProblemDetails by calling
addSubSee(e.getId()) on the security(...) builder before
buildAndSetResponse(exc); specifically, after calling .detail(e.getMessage())
(and only if e.getId() is non-null/non-empty), invoke .addSubSee(e.getId()) on
the same builder chain so JWTException.getId() values (e.g.
ERROR_JWT_NOT_FOUND_ID / ERROR_UNKNOWN_KEY_ID) are propagated into the response.
---
Nitpick comments:
In
`@core/src/test/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptorTest.java`:
- Around line 117-121: The tests in JwtAuthInterceptorTest currently only assert
exc.getResponse().isUserError(); update them to lock the 401 contract by
asserting the explicit HTTP status instead (e.g. assertEquals(401,
exc.getResponse().getStatus())) wherever the test checks isUserError() —
specifically in the block that verifies ERROR_JWT_INVALID_SIGNATURE (uses
unpackBody(exc) and variable exc) and the other similar blocks noted (lines
referenced in the review: 170-174, 186-190, 202-206); keep the existing checks
for null jwt and detail prefix assertions (ERROR_JWT_INVALID_SIGNATURE) and just
add the explicit status assertion to prevent regressions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6ed5c6ae-5931-4dad-8d7e-aea9970c84af
📒 Files selected for processing (2)
core/src/main/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptor.javacore/src/test/java/com/predic8/membrane/core/interceptor/jwt/JwtAuthInterceptorTest.java
|
/ok-to-test |
1 similar comment
|
/ok-to-test |
|
This pull request needs "/ok-to-test" from an authorized committer. |
|
failing tests |
Summary by CodeRabbit
Bug Fixes
Tests