spring-projects/spring-security · error · IllegalArgumentException

Unsupported ProtectionPolicy

Error message

Unsupported ProtectionPolicy <policy>

What it means

The Jackson2 serializer for CredProtectAuthenticationExtensionsClientInput maps each ProtectionPolicy enum constant to its WebAuthn wire-format string; if the switch sees a policy constant it does not recognize, it throws this IllegalArgumentException. This only occurs with an enum value added in a newer webauthn4j version than this serializer supports.

Solutions

  1. Align webauthn4j and spring-security-webauthn versions (upgrade spring-security so its serializer knows the new enum constant, or downgrade webauthn4j to the supported version).
  2. Log the policy value to identify which constant is missing and add a case for it in the switch.
  3. If client-supplied, reject or coerce unknown policy values before building the extension input object.

Example fix

// before (dependency)
<dependency><groupId>com.webauthn4j</groupId><artifactId>webauthn4j-core</artifactId><version>0.29.0.RELEASE</version></dependency>
// after — match the version spring-security-webauthn was built against
<dependency><groupId>com.webauthn4j</groupId><artifactId>webauthn4j-core</artifactId><version>0.28.5.RELEASE</version></dependency>
Defensive patterns

Strategy: validation

Validate before calling

ProtectionPolicy policy = input.getProtectionPolicy();
if (!(policy == ProtectionPolicy.USER_VERIFICATION_OPTIONAL
   || policy == ProtectionPolicy.USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST
   || policy == ProtectionPolicy.USER_VERIFICATION_REQUIRED)) {
    throw new IllegalArgumentException("Unsupported policy before serialization: " + policy);
}

Type guard

public static boolean isSerializablePolicy(ProtectionPolicy p) {
    return switch (p) {
        case USER_VERIFICATION_OPTIONAL,
             USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST,
             USER_VERIFICATION_REQUIRED -> true;
        default -> false;
    };
}

Try / catch

try {
    objectMapper.writeValueAsString(extensionInput);
} catch (IllegalArgumentException e) {
    if (!e.getMessage().startsWith("Unsupported ProtectionPolicy")) throw e;
    log.error("Unknown credProtect policy {} — check webauthn4j version alignment", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: Serializing a CredProtectExtensionOutput/CredProtectAuthenticationExtensionsClientInput whose ProtectionPolicy is USER_VERIFICATION_OPTIONAL_AND_CREDENTIAL_ID_LIST (or any constant not covered by the serializer's cases), typically after upgrading webauthn4j without updating the Jackson serializer's switch.

Common situations: Dependency version mismatch: spring-security-webauthn compiled against an older webauthn4j while a newer one (with a new enum constant) is on the classpath; manually constructing a ProtectionPolicy from client extension output carrying an unexpected policy.

Related errors


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

Appendix: source

Thrown at webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJackson2Serializer.java:64

	@Override
	public void serialize(CredProtectAuthenticationExtensionsClientInput input, JsonGenerator jgen,
			SerializerProvider provider) throws IOException {
		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = input.getInput();
		String policy = toString(credProtect.getCredProtectionPolicy());
		jgen.writeObjectField("credentialProtectionPolicy", policy);
		jgen.writeObjectField("enforceCredentialProtectionPolicy", credProtect.isEnforceCredentialProtectionPolicy());
	}

	private static String toString(CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy policy) {
		switch (policy) {
			case USER_VERIFICATION_OPTIONAL:
				return "userVerificationOptional";
			case USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST:
				return "userVerificationOptionalWithCredentialIdList";
			case USER_VERIFICATION_REQUIRED:
				return "userVerificationRequired";
			default:
				throw new IllegalArgumentException("Unsupported ProtectionPolicy " + policy);
		}
	}

}

View on GitHub (pinned to 96852e8860)