Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple applications using jjwt leading to ServiceConfigurationError io.jsonwebtoken.jackson.io.JacksonSerializer not a subtype #751

Closed
raphisuter opened this issue Aug 3, 2022 · 5 comments

Comments

@raphisuter
Copy link

Describe the bug
Since we updated jjwt from version 0.10.7 to 0.11.X we encounter an error saying io.jsonwebtoken.io.Serializer: Provider io.jsonwebtoken.jackson.io.JacksonSerializer not a subtype
We run multiple cxf rest clients on the same TomEE instance, all of them using our internal security module which is working with jjwt to create and validate jwts. Since we updated from version 0.10.7 to a version starting from 0.11.0 we encounter the problem that only the first application using jjwt functionality works like expected. All others following encounter following problem: io.jsonwebtoken.io.Serializer: Provider io.jsonwebtoken.jackson.io.JacksonSerializer not a subtype while creating a JWT. The order of the webservices creating a token doesn't matter. Trying to mix up which one calls for a jwt first still leads to errors on all other following. Creating a jwt for the first webservice still works after the others tried to get jwts and produce the exception.

This is the code we use to create JWTs:

public static String getNewJWT(@NotNull Key key, String userId, String username, Date expirationDate, HashMap<String, Object> additionalClaims) throws JwtException {
	JwtBuilder builder = Jwts.builder();

	if(!QUtils.mapNullOrEmpty(additionalClaims)) {
		builder.setClaims(additionalClaims);
	}

	return builder.setId(userId).setSubject(username).setExpiration(expirationDate)
		      .setIssuedAt(new Date()).setIssuer(CERTIFICATE_ISSUER).signWith(key)
		      .compact();
}

The Problem happens while doing the compact(). There the serializer is loaded which throws the error mentioned above.

Checking the code I can see the there was a change done starting from 0.11.0 which changed the way the serializer is loaded. Seeing the code my guess is a classLoader problem.

DefaultJwtBuilder.compact(); in V 0.10.7

    if (this.serializer == null) {
        //try to find one based on the runtime environment:
        InstanceLocator<Serializer<Map<String,?>>> locator =
            Classes.newInstance("io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator");
        this.serializer = locator.getInstance();
    }`

DefaultJwtBuilder.compact(); in V 0.11.0

    if (this.serializer == null) {
        // try to find one based on the services available
        // TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
        // use the previous commented out line instead
        this.serializer = LegacyServices.loadFirst(Serializer.class);
    }`

To Reproduce
Steps to reproduce the behavior:

  1. Doing authentication on first webservice
  2. DefaultJwtBuilder.compact() loads an instance of JacksonSerializer without problem
  3. Doing authentication on any following webservice
  4. DefaultJwtBuilder.compact() fails to load an instance of JacksonSerializer saying io.jsonwebtoken.io.Serializer: Provider io.jsonwebtoken.jackson.io.JacksonSerializer not a subtype

Expected behavior
DefaultJwtBuilder.compact() loads an instance of JacksonSerializer correctly

stacktrace.txt

@lhazlewood
Copy link
Contributor

Thanks for reporting this! It appears to be a classloader issue, but I'm not sure how we can re-create the issue for testing without some help setting up a test instance with multiple webservices.

That said, there is a solid workaround for this issue:

That logic only executes if you don't specify your own Serializer instance. You can instantiate JacksonSerializer yourself, and just configure that on the builder as covered here:

https://github.com/jwtk/jjwt#json-custom

where getMySerializer() in your case can be replaced with new JacksonSerializer() (or new JacksonSerializer(anObjectMapperInstance)).

While that workaround should get you moving forward with 0.11.x, I wonder if reverting to the previous Class loading strategy might be something we can look at again. That implementation used our Classes.newInstance method call, and that call does account for the various classloaders in Java Enterprise container environments. We moved to ServiceLoader because it provided an out-of-the-box pluggable implementation strategy, but apparently it only works in standard Java (non EE) environments. We might need to go back to the old approach. CC @bdemers.

@raphisuter
Copy link
Author

Hi @lhazlewood
Thank you for replying to the issue. I made the changes suggested and the workaround is working fine on our cxf webservices which share a single tomEE environment. I added a small private method to get a new JwtParserBuilder with the newly created JacksonSerializer instance.

 /**
 * Creates new JwtParserBuilder instance with new JacksonDeserializer instance to address classLoader Issue in JJWT Library (starting from V 0.11)<br/>
 * <b>{@see https://github.com/jwtk/jjwt/issues/751}<b/>
 *
 * @return new JacksonDeserializer instance with new JacksonDeserializer instance
 */
private static JwtParserBuilder getQJwtParserBuilder() {
	Deserializer<Map<String, ?>> deserialize = new JacksonDeserializer<>();
	return Jwts.parserBuilder().deserializeJsonWith(deserialize);
}

I sadly don't know either how to re-create this issue on an automated testing envirnoment. Let me know if I can help you if you decide to tackle this.

Kind regards, Raphael

@lhazlewood
Copy link
Contributor

@raphisuter Technically it's an issue with the JDK's ServiceLoader implementation, not directly with JJWT (JJWT uses ServiceLoader, and that's where the problem occurs).

That said, I don't know that we necessarily need to try and re-create the problem - I think a better approach is to remove the use of ServiceLoader entirely given the problems we've seen here and in #648 .

@lhazlewood
Copy link
Contributor

Thanks again for the report on this @raphisuter. I'm closing this issue now that we have #752 to represent work to address this issue as well as #648.

@raphisuter
Copy link
Author

Thank you @lhazlewood for your help and effort, really appreciate it.

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

No branches or pull requests

2 participants