Skip to content

Unexpected Exception Handling in NimbusReactiveJwtDecoder decode Method  #14467

Closed
@Kardeen

Description

@Kardeen

Description

We are experiencing an issue where exceptions thrown by the decode method are not being caught as expected when used in a reactive stream. This is causing problems when we attempt to handle these exceptions using doOnError or onErrorReturn.

Steps to reproduce:

  1. Call the decode method with a token string that would fail the parsing of JWTParser.parse(String s) method e.g. eyyyyy.
  2. Use the returned Mono<Jwt> in a reactive stream.
  3. Attempt to catch any exceptions thrown by the decode method using doOnError or onErrorReturn.

Failing code

reactiveJwtDecoder
                .decode("tokenString")
                .doOnError(err -> log.error("Error decoding token", err))
                .flatMap(result -> Mono.just("decoded token successfully"))
                .onErrorReturn("could not decode token");

Observed Result:

The exceptions thrown by the decode method are not being caught by doOnError or onErrorReturn. Instead, they cause the reactive stream to terminate prematurely.

Expected Result:

The exceptions thrown by the decode method should be caught by doOnError or onErrorReturn and allow the reactive stream to continue processing regardless of whether the token string was provided wrapped in a Mono or directly to the method.

Additional Information:

The decode method is defined as follows:

@Override
public Mono<Jwt> decode(String token) throws JwtException {
JWT jwt = parse(token);
if (jwt instanceof PlainJWT) {
throw new BadJwtException("Unsupported algorithm of " + jwt.getHeader().getAlgorithm());
}
return this.decode(jwt);
}

And the parse method is defined as follows:

private JWT parse(String token) {
try {
return JWTParser.parse(token);
}
catch (Exception ex) {
throw new BadJwtException("An error occurred while attempting to decode the Jwt: " + ex.getMessage(), ex);
}
}

As a workaround, we have found that wrapping the token into a Mono and then using flatMap causes the exceptions to be caught correctly. However, this is not ideal as it requires modifying the code that calls the decode method.

Working workaround:

Mono.just("tokenString")
                .flatMap(reactiveJwtDecoder::decode)
                .doOnError(err -> log.error("Error decoding token", err))
                .flatMap(result -> Mono.just("decoded token successfully"))
                .onErrorReturn("could not decode token");

We believe the issue lies in the fact that the BadJwtException thrown by the parse method is not being wrapped into a Mono.error(). Similarly, the exception thrown when the jwt is an instance of PlainJWT should also be wrapped into a Mono.error().

We propose that the decode method be modified to wrap these exceptions into a Mono.error() so that they can be caught by doOnError or onErrorReturn.

Possible solution:

@Override
public Mono<Jwt> decode(String token) throws JwtException {
    try {
        JWT jwt = JWTParser.parse(token);
        if (jwt instanceof PlainJWT) {
            return Mono.error(new BadJwtException(
                "Unsupported algorithm of " + jwt.getHeader().getAlgorithm()));
        }
        return this.decode(jwt);
    } catch (Exception ex) {
        return Mono.error(new BadJwtException(
            "An error occurred while attempting to decode the Jwt: " + ex.getMessage(), ex));
    }
}

Thank you for considering this bug report. We appreciate your assistance in resolving this issue.

Metadata

Metadata

Assignees

Labels

in: oauth2An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)type: bugA general bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions