Skip to content

Commit

Permalink
Enhance logging and error handling for token exchange and JWT
Browse files Browse the repository at this point in the history
Added detailed trace ID and span ID headers in error responses across TokenExchangeController and JWTAuthenticationFilter. These changes improve debuggability by providing clearer error context and extended log information.
  • Loading branch information
mme-flendly committed Sep 24, 2024
1 parent e6e932e commit 376ea8c
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ public void doFilterInternal(@Nonnull HttpServletRequest request, @Nonnull HttpS
try {
authentication = tokenAuthenticationService.getAuthentication(request);
} catch (BadJOSEException e) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized");
response.setHeader("X-B3-TraceId", request.getHeader("X-B3-TraceId"));
response.setHeader("X-B3-SpanId", request.getHeader("X-B3-SpanId"));
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid token - Token expired");
}

SecurityContextHolder.getContext().setAuthentication(authentication);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ public class TokenExchangeController {

private final TokenExchangeService tokenExchangeService;

@PostMapping(consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
@PostMapping(consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
@Operation(summary = "Exchange Token", description = "Create an access or refresh token given a valide subject token.", responses = {
@ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = "application/json", schema = @Schema(implementation = TokenResponse.class))),
@ApiResponse(responseCode = "400", description = "Bad request", headers = @Header(name = "error", description = "invalid request"))
})
public ResponseEntity<Object> tokenExchange(@RequestBody @ModelAttribute TokenRequestForm tokenRequestForm, HttpServletRequest servletRequest) {
if (log.isTraceEnabled()) log.trace("POST tokenExchange started...");
@ApiResponse(responseCode = "400", description = "Bad request", headers = @Header(name = "error", description = "invalid request")) })
public ResponseEntity<Object> tokenExchange(@RequestBody @ModelAttribute TokenRequestForm tokenRequestForm,
HttpServletRequest servletRequest) {
if (log.isTraceEnabled())
log.trace("POST tokenExchange started...");

TokenExchangeRequest tokenExchange = getTokenExchangeRequest(tokenRequestForm, servletRequest);

Expand All @@ -56,24 +57,20 @@ public ResponseEntity<Object> tokenExchange(@RequestBody @ModelAttribute TokenRe
ResponseEntity<Object> errorData = ResponseUtils.invalidParam(e.getMessage());
return ResponseEntity.badRequest().body(errorData);
} catch (BadJOSEException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).header("source", "sts").body(e.getMessage());
return ResponseEntity.status(HttpStatus.FORBIDDEN).header("source", "sts")
.header("X-B3-TraceId", servletRequest.getHeader("X-B3-TraceId"))
.header("X-B3-SpanId", servletRequest.getHeader("X-B3-SpanId")).body(e.getMessage());
} finally {
if (log.isTraceEnabled()) log.trace("POST tokenExchange finished: {}", errorMessage);
if (log.isTraceEnabled())
log.trace("POST tokenExchange finished: {}", errorMessage);
}
}

private static TokenExchangeRequest getTokenExchangeRequest(TokenRequestForm tokenRequestForm, HttpServletRequest servletRequest) {
return TokenExchangeRequest.builder()
.grantType(tokenRequestForm.getGrantType())
.resources(tokenRequestForm.getResources())
.subjectToken(tokenRequestForm.getSubjectToken())
.subjectTokenType(tokenRequestForm.getSubjectTokenType())
.actorToken(tokenRequestForm.getActorToken())
.actorTokenType(tokenRequestForm.getActorTokenType())
.issuer(ResponseUtils.getIssuer(servletRequest))
.scope(tokenRequestForm.getScope())
.requestedTokenType(tokenRequestForm.getRequestedTokenType())
.audiences(tokenRequestForm.getAudiences())
.build();
return TokenExchangeRequest.builder().grantType(tokenRequestForm.getGrantType()).resources(tokenRequestForm.getResources())
.subjectToken(tokenRequestForm.getSubjectToken()).subjectTokenType(tokenRequestForm.getSubjectTokenType())
.actorToken(tokenRequestForm.getActorToken()).actorTokenType(tokenRequestForm.getActorTokenType())
.issuer(ResponseUtils.getIssuer(servletRequest)).scope(tokenRequestForm.getScope())
.requestedTokenType(tokenRequestForm.getRequestedTokenType()).audiences(tokenRequestForm.getAudiences()).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;

import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -47,7 +49,8 @@ public void testVerify_throwsBadJWTException_whenJWTIsExpired() {

@Test
public void testVerify_throwsBadJWTException_whenJWTIsNotBeforeNow() {
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().notBeforeTime(new Date(System.currentTimeMillis() + 60000)).build();
JWTClaimsSet claimsSet =
new JWTClaimsSet.Builder().notBeforeTime(new Date(System.currentTimeMillis() + 600000000)).issueTime(new Date()).build();
when(clock.instant()).thenReturn(Instant.now());
assertThrows(BadJWTException.class, () -> {
underTest.verify(claimsSet, null);
Expand Down

0 comments on commit 376ea8c

Please sign in to comment.