jwtk/jjwt

Disable Jackson ObjectMapper FAIL_ON_UNKNOWN_PROPERTIES Deserialization Feature

amiriahmad72 opened this issue · 1 comments

Problem
I use JacksonDeserializer constructor with claimTypeMap argument in order to parse the values of the claims into my custom types. After a while, I remove unnecessary filed from my custom type. After that, I got Error for all old generated valid JWTs (UnrecognizedPropertyException).

Throwing UnrecognizedPropertyException when json has Additional/Unknown fields is the default behavior of Jackson deserializer. So if the JWT generator service change its Claim Objects and add some fields, I got error until I add those to my custom type (even if I don't need them).

Solution
There are two solution for this problem.

  1. Using @JsonIgnoreProperties(ignoreUnknown = true) in Class level
  2. Configure Jackson Object Mapper and disable DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES(set to false).

I know the first solution solve my problem but I think the purpose of using JacksonDeserializer constructor with claimTypeMap argument is just to parse the values of the claims into given types (and Not Additional/Unknown fields checking).
And also Others like Spring Boot disable this feature by default

So we can change the constructor like this:

public JacksonDeserializer(Map<String, Class<?>> claimTypeMap) {
    // DO NOT reuse JacksonSerializer.DEFAULT_OBJECT_MAPPER as this could result in sharing the custom deserializer
    // between instances
    this(new ObjectMapper());
    Assert.notNull(claimTypeMap, "Claim type map cannot be null.");
    // register a new Deserializer
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap)));
    objectMapper.registerModule(module);
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

But maybe there are some people who count on this exception and use it as a validation. So we can overload the constructor like this:

public JacksonDeserializer(Map<String, Class<?>> claimTypeMap) {
    this(claimTypeMap, false);
}

public JacksonDeserializer(Map<String, Class<?>> claimTypeMap, boolean failOnUnknownProperties) {
    // DO NOT reuse JacksonSerializer.DEFAULT_OBJECT_MAPPER as this could result in sharing the custom deserializer
    // between instances
    this(new ObjectMapper());
    Assert.notNull(claimTypeMap, "Claim type map cannot be null.");
    // register a new Deserializer
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap)));
    objectMapper.registerModule(module);
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
}

Fixed via #896