spring-projects/spring-security · error · IllegalArgumentException

The class with {id} and name of {className} is not in the al

Error message

The class with {id} and name of {className} is not in the allowlist. If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. If the serialization is only done by a trusted source, you can also enable default typing. See https://github.com/spring-projects/spring-security/issues/4370 for details

What it means

SecurityJackson2Modules validates polymorphic deserialization targets against an allowlist of registered Spring Security classes before instantiating them. When Jackson's default typing resolves a type id to a class that was not explicitly enabled via SecurityJackson2Modules.enableDefaultTyping or a mixin, typeFromId rejects it with this IllegalArgumentException. This is a deliberate deserialization-safety guard introduced after spring-security#4370 to prevent gadget-class attacks through untrusted JSON.

Source

Thrown at core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java:299

		@Override
		public JavaType typeFromId(DatabindContext context, String id) throws IOException {
			DeserializationConfig config = (DeserializationConfig) context.getConfig();
			JavaType result = this.delegate.typeFromId(context, id);
			String className = result.getRawClass().getName();
			if (isInAllowlist(className)) {
				return result;
			}
			boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;
			if (isExplicitMixin) {
				return result;
			}
			JacksonAnnotation jacksonAnnotation = AnnotationUtils.findAnnotation(result.getRawClass(),
					JacksonAnnotation.class);
			if (jacksonAnnotation != null) {
				return result;
			}
			throw new IllegalArgumentException("The class with " + id + " and name of " + className
					+ " is not in the allowlist. "
					+ "If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. "
					+ "If the serialization is only done by a trusted source, you can also enable default typing. "
					+ "See https://github.com/spring-projects/spring-security/issues/4370 for details");
		}

		private boolean isInAllowlist(String id) {
			return ALLOWLIST_CLASS_NAMES.contains(id);
		}

		@Override
		public String getDescForKnownTypeIds() {
			return this.delegate.getDescForKnownTypeIds();
		}

		@Override
		public JsonTypeInfo.Id getMechanism() {
			return this.delegate.getMechanism();

View on GitHub (pinned to 96852e8860)

Solutions

  1. Register the class's mixin with SecurityJackson2Modules (mapper.registerMixins or SecurityJackson2Modules mixins map) so its type id resolves explicitly
  2. Use SecurityJackson2Modules.enableDefaultTyping(mapper, DefaultTyping.NON_FINAL, As.PROPERTY) on the ObjectMapper rather than generic activateDefaultTyping
  3. Prefer deserializing into known Spring Security types; for custom types, subclass and add a Jackson mixin declaring @JsonTypeInfo and @JsonDeserialize
  4. If payloads are only produced by trusted code, explicitly opt into default typing as the message suggests, and document the trust boundary

Example fix

// before
ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(...); // class not allowlisted -> IllegalArgumentException
// after
ObjectMapper mapper = new ObjectMapper();
SecurityJackson2Modules.enableDefaultTyping(mapper);
mapper.registerMixins(MyCustomToken.class, MyCustomTokenMixin.class);
Defensive patterns

Strategy: validation

Validate before calling

Class<?> raw = null;
try { raw = mapper.getTypeFactory().findClass(typeId); } catch (ClassNotFoundException e) { /* reject */ }
boolean allowlisted = raw != null && (SecurityJackson2Modules.isWellKnownReturnValueType(raw)
    || registeredMixins.containsKey(raw));
if (!allowlisted) { throw new IllegalArgumentException("Type not allowlisted: " + typeId); }

Type guard

static boolean isAllowlisted(Class<?> clazz) {
    return clazz != null && AnnotationUtils.findAnnotation(clazz, JacksonAnnotation.class) != null;
}

Try / catch

try {
    return mapper.readValue(json, Object.class);
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("is not in the allowlist")) {
        // log and reject payload; register missing mixin if legitimate
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling ObjectMapper.readValue (or the ObjectMapper from SecurityJackson2Modules) on JSON containing @class / type id fields naming classes not covered by the allowlist; deserializing payloads serialized with custom UserDetails/Auth implementations without registering their mixins; enabling default typing manually instead of via SecurityJackson2Modules.enableDefaultTyping.

Common situations: Spring Session / Spring Security OAuth cache deserialization after upgrading Spring Security and adding new class fields; round-tripping custom Authentication or UserDetails objects through Redis or HTTP session JSON; copying an ObjectMapper from another service that allowlists different classes.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/ba4c33c4c6e32691. Report an issue: GitHub.