#1065·jjwt

`JacksonSerializer` does not respect `JsonTypeInfo`

Author: beroalCreated Jul 15, 2026Updated Aug 24, 2026

To Reproduce

java
final MacAlgorithm macAlgorithm = Jwts.SIG.HS256;
final SecretKey secretKey = macAlgorithm.key().build();

final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);
final String NAME = "additional";
final JacksonSerializer jacksonSerializer = new JacksonSerializer(objectMapper);
final JacksonDeserializer jacksonDeserializer = new JacksonDeserializer(
    objectMapper,
    Map.of(NAME, SimpleGrantedAuthority.class)
);

final JwtBuilder builder = Jwts.builder().json(jacksonSerializer);
builder.claims().add(NAME, new SimpleGrantedAuthority("ROLE_ADMIN"));
final String jwt = builder.signWith(secretKey, macAlgorithm).compact();

final JwtParser parser = Jwts.parser().json(jacksonDeserializer).verifyWith(secretKey).build();
final Object payload = parser.parse(jwt).getPayload();
System.out.println(new String((byte[]) payload));

The above program prints

{"additional":{"authority":"ROLE_ADMIN"}}

In the above program, JwtParser returns a byte[] payload.

Expected behavior

It should return Claims.

Explanation

The reason is that Jackson can't parse the payload because the JSON object corresponding to SimpleGrantedAuthority doesn't contain the "@class" property. The exception that Jackson throws is

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class org.springframework.security.core.authority.SimpleGrantedAuthority]: missing type id property '@class'  at [Source: UNKNOWN; line: -1, column: -1]

The SimpleGrantedAuthorityMixin has the following annotation on it:

java
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)

so the JSON object should contain the "@class" property.

BTW, JJWT drops this exception. This makes harder to understand why parsing of the payload failed.

For reference:

java
public final class SimpleGrantedAuthority implements GrantedAuthority {

    private static final long serialVersionUID = 620L;

    private final String role;

    public SimpleGrantedAuthority(String authority) {
        Assert.hasText(authority, "A granted authority textual representation is required");
        this.role = authority;
    }

    @Override
    public String getAuthority() {
        return this.role;
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        /* skipped */
    }

    @Override
    public int hashCode() {
        return this.role.hashCode();
    }

    @Override
    public String toString() {
        return this.role;
    }

}

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
        getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public abstract class SimpleGrantedAuthorityMixin {

    @JsonCreator
    public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) {
    }

}