diff --git a/webauthn/spring-security-webauthn.gradle b/webauthn/spring-security-webauthn.gradle index 39413d024f..b289714464 100644 --- a/webauthn/spring-security-webauthn.gradle +++ b/webauthn/spring-security-webauthn.gradle @@ -15,6 +15,7 @@ dependencies { optional 'org.springframework:spring-jdbc' optional 'org.springframework:spring-tx' + optional 'tools.jackson.core:jackson-databind' provided 'jakarta.servlet:jakarta.servlet-api' diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsFilter.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsFilter.java index 2a72efdf41..bd2bb76641 100644 --- a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsFilter.java +++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsFilter.java @@ -22,13 +22,13 @@ import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import tools.jackson.databind.json.JsonMapper; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -36,7 +36,7 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions; -import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module; +import org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule; import org.springframework.security.web.webauthn.management.ImmutablePublicKeyCredentialRequestOptionsRequest; import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations; import org.springframework.util.Assert; @@ -63,8 +63,8 @@ public class PublicKeyCredentialRequestOptionsFilter extends OncePerRequestFilte private PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository(); - private HttpMessageConverter converter = new MappingJackson2HttpMessageConverter( - Jackson2ObjectMapperBuilder.json().modules(new WebauthnJackson2Module()).build()); + private HttpMessageConverter converter = new JacksonJsonHttpMessageConverter( + JsonMapper.builder().addModule(new WebauthnJacksonModule()).build()); /** * Creates a new instance with the provided {@link WebAuthnRelyingPartyOperations}. diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilter.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilter.java index 03c7e1a1c5..6c83cab7ed 100644 --- a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilter.java +++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilter.java @@ -21,13 +21,14 @@ import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import tools.jackson.databind.json.JsonMapper; import org.springframework.core.ResolvableType; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.converter.GenericHttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.SmartHttpMessageConverter; +import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; @@ -40,7 +41,7 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse; import org.springframework.security.web.webauthn.api.PublicKeyCredential; import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions; -import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module; +import org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule; import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest; import org.springframework.util.Assert; @@ -49,8 +50,7 @@ import static org.springframework.security.web.servlet.util.matcher.PathPatternR /** * Authenticates {@code PublicKeyCredential} that is * parsed from the body of the {@link HttpServletRequest} using the - * {@link #setConverter(GenericHttpMessageConverter)}. An example request is provided - * below: + * {@link #setConverter(SmartHttpMessageConverter)}. An example request is provided below: * *
  * {
@@ -72,8 +72,8 @@ import static org.springframework.security.web.servlet.util.matcher.PathPatternR
  */
 public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 
-	private GenericHttpMessageConverter converter = new MappingJackson2HttpMessageConverter(
-			Jackson2ObjectMapperBuilder.json().modules(new WebauthnJackson2Module()).build());
+	private SmartHttpMessageConverter converter = new JacksonJsonHttpMessageConverter(
+			JsonMapper.builder().addModule(new WebauthnJacksonModule()).build());
 
 	private PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();
 
@@ -94,7 +94,7 @@ public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessi
 		PublicKeyCredential publicKeyCredential = null;
 		try {
 			publicKeyCredential = (PublicKeyCredential) this.converter
-				.read(resolvableType.getType(), getClass(), httpRequest);
+				.read(resolvableType, httpRequest, null);
 		}
 		catch (Exception ex) {
 			throw new BadCredentialsException("Unable to authenticate the PublicKeyCredential", ex);
@@ -114,10 +114,11 @@ public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessi
 	/**
 	 * Sets the {@link GenericHttpMessageConverter} to use for writing
 	 * {@code PublicKeyCredential} to the response. The
-	 * default is @{code MappingJackson2HttpMessageConverter}
+	 * default is @{code Jackson2HttpMessageConverter}
 	 * @param converter the {@link GenericHttpMessageConverter} to use. Cannot be null.
 	 */
-	public void setConverter(GenericHttpMessageConverter converter) {
+	// TODO Accept HttpMessageConverter
+	public void setConverter(SmartHttpMessageConverter converter) {
 		Assert.notNull(converter, "converter cannot be null");
 		this.converter = converter;
 	}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Mixin.java
new file mode 100644
index 0000000000..489acebfff
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Mixin.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
+
+/**
+ * Jackson mixin for {@link AttestationConveyancePreference}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AttestationConveyancePreferenceMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonSerialize(using = AttestationConveyancePreferenceJackson2Serializer.class)
+class AttestationConveyancePreferenceJackson2Mixin {
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Serializer.java
new file mode 100644
index 0000000000..99a93b4227
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Serializer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
+
+/**
+ * Jackson serializer for {@link AttestationConveyancePreference}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AttestationConveyancePreferenceSerializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class AttestationConveyancePreferenceJackson2Serializer extends StdSerializer {
+
+	AttestationConveyancePreferenceJackson2Serializer() {
+		super(AttestationConveyancePreference.class);
+	}
+
+	@Override
+	public void serialize(AttestationConveyancePreference preference, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeString(preference.getValue());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceMixin.java
index e3abe5c967..41140d54fd 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceMixin.java
@@ -16,7 +16,7 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceSerializer.java
index 81f331491c..7eea519353 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
 
@@ -38,8 +37,8 @@ class AttestationConveyancePreferenceSerializer extends StdSerializer {
+
+	/**
+	 * Creates a new instance.
+	 */
+	AuthenticationExtensionsClientInputJackson2Serializer() {
+		super(AuthenticationExtensionsClientInput.class);
+	}
+
+	@Override
+	public void serialize(AuthenticationExtensionsClientInput input, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeObjectField(input.getExtensionId(), input.getInput());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputMixin.java
index 0fa2f47e81..76b5b7113d 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputMixin.java
@@ -16,7 +16,7 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputSerializer.java
index ce90ebd117..225b697bb7 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;
 
@@ -41,9 +40,9 @@ class AuthenticationExtensionsClientInputSerializer extends StdSerializer {
+
+	/**
+	 * Creates a new instance.
+	 */
+	AuthenticationExtensionsClientInputsJackson2Serializer() {
+		super(AuthenticationExtensionsClientInputs.class);
+	}
+
+	@Override
+	public void serialize(AuthenticationExtensionsClientInputs inputs, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeStartObject();
+		for (AuthenticationExtensionsClientInput input : inputs.getInputs()) {
+			jgen.writeObject(input);
+		}
+		jgen.writeEndObject();
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsMixin.java
index 6d788e0837..11e9ed9ad7 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsMixin.java
@@ -16,7 +16,7 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsSerializer.java
index 5a56b0690f..7d5eda91b8 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
@@ -42,11 +41,11 @@ class AuthenticationExtensionsClientInputsSerializer extends StdSerializer> outputs = new ArrayList<>();
-		for (String key = parser.nextFieldName(); key != null; key = parser.nextFieldName()) {
+		for (String key = parser.nextName(); key != null; key = parser.nextName()) {
 			JsonToken startObject = parser.nextValue();
 			if (startObject != JsonToken.START_OBJECT) {
 				break;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Deserializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Deserializer.java
new file mode 100644
index 0000000000..85f3f4b2a3
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Deserializer.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutput;
+import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
+import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;
+import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;
+
+/**
+ * Provides Jackson deserialization of {@link AuthenticationExtensionsClientOutputs}.
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientOutputsDeserializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class AuthenticationExtensionsClientOutputsJackson2Deserializer
+		extends StdDeserializer {
+
+	private static final Log logger = LogFactory
+		.getLog(AuthenticationExtensionsClientOutputsJackson2Deserializer.class);
+
+	/**
+	 * Creates a new instance.
+	 */
+	AuthenticationExtensionsClientOutputsJackson2Deserializer() {
+		super(AuthenticationExtensionsClientOutputs.class);
+	}
+
+	@Override
+	public AuthenticationExtensionsClientOutputs deserialize(JsonParser parser, DeserializationContext ctxt)
+			throws IOException, JacksonException {
+		List> outputs = new ArrayList<>();
+		for (String key = parser.nextFieldName(); key != null; key = parser.nextFieldName()) {
+			JsonToken startObject = parser.nextValue();
+			if (startObject != JsonToken.START_OBJECT) {
+				break;
+			}
+			if (CredentialPropertiesOutput.EXTENSION_ID.equals(key)) {
+				CredentialPropertiesOutput output = parser.readValueAs(CredentialPropertiesOutput.class);
+				outputs.add(output);
+			}
+			else {
+				if (logger.isDebugEnabled()) {
+					logger.debug("Skipping unknown extension with id " + key);
+				}
+				parser.nextValue();
+			}
+		}
+
+		return new ImmutableAuthenticationExtensionsClientOutputs(outputs);
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Mixin.java
new file mode 100644
index 0000000000..75ab461e47
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Mixin.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
+
+/**
+ * Jackson mixin for {@link AuthenticationExtensionsClientOutputs}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientOutputsMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonDeserialize(using = AuthenticationExtensionsClientOutputsJackson2Deserializer.class)
+class AuthenticationExtensionsClientOutputsJackson2Mixin {
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsMixin.java
index e41e068536..c01f264426 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsMixin.java
@@ -16,7 +16,7 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonDeserialize;
 
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseJackson2Mixin.java
new file mode 100644
index 0000000000..3b6eba49ca
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseJackson2Mixin.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
+
+/**
+ * Jackson mixin for {@link AuthenticatorAssertionResponse}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAssertionResponseMixin}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@JsonDeserialize(builder = AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.class)
+class AuthenticatorAssertionResponseJackson2Mixin {
+
+	@JsonPOJOBuilder(withPrefix = "")
+	abstract class AuthenticatorAssertionResponseBuilderMixin {
+
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseMixin.java
index 2c28c59787..4e6c51dfa6 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseMixin.java
@@ -16,8 +16,8 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonPOJOBuilder;
 
 import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Deserializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Deserializer.java
new file mode 100644
index 0000000000..98cbf670bf
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Deserializer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
+
+/**
+ * Jackson deserializer for {@link AuthenticatorAttachment}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAttachmentDeserializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class AuthenticatorAttachmentJackson2Deserializer extends StdDeserializer {
+
+	AuthenticatorAttachmentJackson2Deserializer() {
+		super(AuthenticatorAttachment.class);
+	}
+
+	@Override
+	public @Nullable AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)
+			throws IOException, JacksonException {
+		String type = parser.readValueAs(String.class);
+		for (AuthenticatorAttachment publicKeyCredentialType : AuthenticatorAttachment.values()) {
+			if (publicKeyCredentialType.getValue().equals(type)) {
+				return publicKeyCredentialType;
+			}
+		}
+		return null;
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Mixin.java
new file mode 100644
index 0000000000..409f1a2f47
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Mixin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
+
+/**
+ * Jackson mixin for {@link AuthenticatorAttachment}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAttachmentMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonDeserialize(using = AuthenticatorAttachmentJackson2Deserializer.class)
+@JsonSerialize(using = AuthenticatorAttachmentJackson2Serializer.class)
+class AuthenticatorAttachmentJackson2Mixin {
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Serializer.java
new file mode 100644
index 0000000000..285f0d1dd8
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Serializer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
+
+/**
+ * Jackson serializer for {@link AuthenticatorAttachment}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAttachmentSerializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class AuthenticatorAttachmentJackson2Serializer extends StdSerializer {
+
+	AuthenticatorAttachmentJackson2Serializer() {
+		super(AuthenticatorAttachment.class);
+	}
+
+	@Override
+	public void serialize(AuthenticatorAttachment attachment, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeString(attachment.getValue());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentMixin.java
index 820b5477ed..df4a397754 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentMixin.java
@@ -16,8 +16,8 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentSerializer.java
index 3ccdf60514..9a854420fe 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
 
@@ -38,8 +37,8 @@ class AuthenticatorAttachmentSerializer extends StdSerializer transports);
+
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttestationResponseMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttestationResponseMixin.java
index cd43c3d59d..25c5c8f2c4 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttestationResponseMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttestationResponseMixin.java
@@ -20,8 +20,8 @@ import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonSetter;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonPOJOBuilder;
 
 import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
 import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Deserializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Deserializer.java
new file mode 100644
index 0000000000..3cea2df11e
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Deserializer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
+
+/**
+ * Jackson deserializer for {@link AuthenticatorTransport}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorTransportDeserializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class AuthenticatorTransportJackson2Deserializer extends StdDeserializer {
+
+	AuthenticatorTransportJackson2Deserializer() {
+		super(AuthenticatorTransport.class);
+	}
+
+	@Override
+	public @Nullable AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)
+			throws IOException, JacksonException {
+		String transportValue = parser.readValueAs(String.class);
+		for (AuthenticatorTransport transport : AuthenticatorTransport.values()) {
+			if (transport.getValue().equals(transportValue)) {
+				return transport;
+			}
+		}
+		return null;
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Mixin.java
new file mode 100644
index 0000000000..4f55835069
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Mixin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
+
+/**
+ * Jackson mixin for {@link AuthenticatorTransport}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorTransportMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonDeserialize(using = AuthenticatorTransportJackson2Deserializer.class)
+@JsonSerialize(using = AuthenticatorTransportJackson2Serializer.class)
+class AuthenticatorTransportJackson2Mixin {
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Serializer.java
new file mode 100644
index 0000000000..229c807174
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Serializer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
+
+/**
+ * Jackson serializer for {@link AuthenticatorTransport}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorTransportSerializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+class AuthenticatorTransportJackson2Serializer extends JsonSerializer {
+
+	@Override
+	public void serialize(AuthenticatorTransport transport, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeString(transport.getValue());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportMixin.java
index 76c97fc2c7..9be251fce6 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportMixin.java
@@ -16,8 +16,8 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportSerializer.java
index 92a485587a..3e9999f72c 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ValueSerializer;
 
 import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
 
@@ -30,11 +29,11 @@ import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
  * @author Rob Winch
  * @since 6.4
  */
-class AuthenticatorTransportSerializer extends JsonSerializer {
+class AuthenticatorTransportSerializer extends ValueSerializer {
 
 	@Override
-	public void serialize(AuthenticatorTransport transport, JsonGenerator jgen, SerializerProvider provider)
-			throws IOException {
+	public void serialize(AuthenticatorTransport transport, JsonGenerator jgen, SerializationContext ctxt)
+			throws JacksonException {
 		jgen.writeString(transport.getValue());
 	}
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Mixin.java
new file mode 100644
index 0000000000..b41a2db0e8
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Mixin.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.springframework.security.web.webauthn.api.Bytes;
+
+/**
+ * Jackson mixin for {@link Bytes}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.BytesMixin} based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonSerialize(using = BytesJackson2Serializer.class)
+final class BytesJackson2Mixin {
+
+	@JsonCreator
+	static Bytes fromBase64(String value) {
+		return Bytes.fromBase64(value);
+	}
+
+	private BytesJackson2Mixin() {
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Serializer.java
new file mode 100644
index 0000000000..88daaa0f17
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Serializer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.springframework.security.web.webauthn.api.Bytes;
+
+/**
+ * Jackson serializer for {@link Bytes}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.BytesSerializer} based on
+ * Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class BytesJackson2Serializer extends StdSerializer {
+
+	/**
+	 * Creates a new instance.
+	 */
+	BytesJackson2Serializer() {
+		super(Bytes.class);
+	}
+
+	@Override
+	public void serialize(Bytes bytes, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+		jgen.writeString(bytes.toBase64UrlString());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesMixin.java
index ea4567659a..74a256f765 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesMixin.java
@@ -17,7 +17,7 @@
 package org.springframework.security.web.webauthn.jackson;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.Bytes;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesSerializer.java
index f52d39b20c..0d2041c435 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.Bytes;
 
@@ -41,7 +40,7 @@ class BytesSerializer extends StdSerializer {
 	}
 
 	@Override
-	public void serialize(Bytes bytes, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+	public void serialize(Bytes bytes, JsonGenerator jgen, SerializationContext provider) throws JacksonException {
 		jgen.writeString(bytes.toBase64UrlString());
 	}
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Deserializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Deserializer.java
new file mode 100644
index 0000000000..ba2d2a14c7
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Deserializer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
+
+/**
+ * Jackson serializer for {@link COSEAlgorithmIdentifier}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.COSEAlgorithmIdentifierDeserializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class COSEAlgorithmIdentifierJackson2Deserializer extends StdDeserializer {
+
+	COSEAlgorithmIdentifierJackson2Deserializer() {
+		super(COSEAlgorithmIdentifier.class);
+	}
+
+	@Override
+	public @Nullable COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)
+			throws IOException, JacksonException {
+		Long transportValue = parser.readValueAs(Long.class);
+		for (COSEAlgorithmIdentifier identifier : COSEAlgorithmIdentifier.values()) {
+			if (identifier.getValue() == transportValue.longValue()) {
+				return identifier;
+			}
+		}
+		return null;
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Mixin.java
new file mode 100644
index 0000000000..f1ef94cacb
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Mixin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
+
+/**
+ * Jackson mixin for {@link COSEAlgorithmIdentifier}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.COSEAlgorithmIdentifierMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonSerialize(using = COSEAlgorithmIdentifierJackson2Serializer.class)
+@JsonDeserialize(using = COSEAlgorithmIdentifierJackson2Deserializer.class)
+abstract class COSEAlgorithmIdentifierJackson2Mixin {
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Serializer.java
new file mode 100644
index 0000000000..9f1a3771c9
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Serializer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
+
+/**
+ * Jackson serializer for {@link COSEAlgorithmIdentifier}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.COSEAlgorithmIdentifierSerializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class COSEAlgorithmIdentifierJackson2Serializer extends StdSerializer {
+
+	COSEAlgorithmIdentifierJackson2Serializer() {
+		super(COSEAlgorithmIdentifier.class);
+	}
+
+	@Override
+	public void serialize(COSEAlgorithmIdentifier identifier, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeNumber(identifier.getValue());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierMixin.java
index 0b04587ec7..2b02f42e33 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierMixin.java
@@ -16,8 +16,8 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierSerializer.java
index 9c05a79dfe..a772606357 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
 
@@ -38,8 +37,8 @@ class COSEAlgorithmIdentifierSerializer extends StdSerializercredProtect
+ * extension.
+ *
+ * @author Rob Winch
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.CredProtectAuthenticationExtensionsClientInputSerializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class CredProtectAuthenticationExtensionsClientInputJackson2Serializer
+		extends StdSerializer {
+
+	protected CredProtectAuthenticationExtensionsClientInputJackson2Serializer() {
+		super(CredProtectAuthenticationExtensionsClientInput.class);
+	}
+
+	@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);
+		}
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputMixin.java
index 2682eff186..f5ab374466 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputMixin.java
@@ -16,7 +16,7 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 @JsonSerialize(using = CredProtectAuthenticationExtensionsClientInputSerializer.class)
 class CredProtectAuthenticationExtensionsClientInputMixin {
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputSerializer.java
index ddda5fb22f..6bba9f2f8d 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;
 
@@ -41,11 +40,11 @@ class CredProtectAuthenticationExtensionsClientInputSerializer
 
 	@Override
 	public void serialize(CredProtectAuthenticationExtensionsClientInput input, JsonGenerator jgen,
-			SerializerProvider provider) throws IOException {
+			SerializationContext ctxt) throws JacksonException {
 		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = input.getInput();
 		String policy = toString(credProtect.getCredProtectionPolicy());
-		jgen.writeObjectField("credentialProtectionPolicy", policy);
-		jgen.writeObjectField("enforceCredentialProtectionPolicy", credProtect.isEnforceCredentialProtectionPolicy());
+		jgen.writePOJOProperty("credentialProtectionPolicy", policy);
+		jgen.writePOJOProperty("enforceCredentialProtectionPolicy", credProtect.isEnforceCredentialProtectionPolicy());
 	}
 
 	private static String toString(CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy policy) {
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredentialPropertiesOutputJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredentialPropertiesOutputJackson2Mixin.java
new file mode 100644
index 0000000000..f23f42ff17
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredentialPropertiesOutputJackson2Mixin.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;
+
+/**
+ * Jackson mixin for {@link CredentialPropertiesOutput}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.CredentialPropertiesOutputMixin}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@JsonIgnoreProperties(ignoreUnknown = true)
+abstract class CredentialPropertiesOutputJackson2Mixin {
+
+	CredentialPropertiesOutputJackson2Mixin(@JsonProperty("rk") boolean rk) {
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationJackson2Serializer.java
new file mode 100644
index 0000000000..cce50657ac
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationJackson2Serializer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+import java.time.Duration;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+/**
+ * Jackson serializer for {@link Duration}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.DurationSerializer} based on
+ * Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class DurationJackson2Serializer extends StdSerializer {
+
+	/**
+	 * Creates an instance.
+	 */
+	DurationJackson2Serializer() {
+		super(Duration.class);
+	}
+
+	@Override
+	public void serialize(Duration duration, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+		jgen.writeNumber(duration.toMillis());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationSerializer.java
index cdba90b484..9eba908b14 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationSerializer.java
@@ -16,12 +16,12 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
 import java.time.Duration;
 
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 /**
  * Jackson serializer for {@link Duration}
@@ -40,7 +40,7 @@ class DurationSerializer extends StdSerializer {
 	}
 
 	@Override
-	public void serialize(Duration duration, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+	public void serialize(Duration duration, JsonGenerator jgen, SerializationContext ctxt) throws JacksonException {
 		jgen.writeNumber(duration.toMillis());
 	}
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsJackson2Mixin.java
new file mode 100644
index 0000000000..2115f7ef58
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsJackson2Mixin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.time.Duration;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
+
+/**
+ * Jackson mixin for {@link PublicKeyCredentialCreationOptions}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialCreationOptionsMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+abstract class PublicKeyCredentialCreationOptionsJackson2Mixin {
+
+	@JsonSerialize(using = DurationJackson2Serializer.class)
+	private @Nullable Duration timeout;
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsMixin.java
index 7b21e07fe4..4131e95409 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsMixin.java
@@ -19,8 +19,8 @@ package org.springframework.security.web.webauthn.jackson;
 import java.time.Duration;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import org.jspecify.annotations.Nullable;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialJackson2Mixin.java
new file mode 100644
index 0000000000..8e5755f434
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialJackson2Mixin.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+import org.springframework.security.web.webauthn.api.PublicKeyCredential;
+
+/**
+ * Jackson mixin for {@link PublicKeyCredential}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialMixin}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@JsonDeserialize(builder = PublicKeyCredential.PublicKeyCredentialBuilder.class)
+class PublicKeyCredentialJackson2Mixin {
+
+	@JsonPOJOBuilder(withPrefix = "")
+	static class PublicKeyCredentialBuilderMixin {
+
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialMixin.java
index 31eda4c206..ee9498f07e 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialMixin.java
@@ -16,8 +16,8 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonPOJOBuilder;
 
 import org.springframework.security.web.webauthn.api.PublicKeyCredential;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsJackson2Mixin.java
new file mode 100644
index 0000000000..b95160864e
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsJackson2Mixin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.time.Duration;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
+
+/**
+ * Jackson mixin for {@link PublicKeyCredentialRequestOptions}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialRequestOptionsMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonInclude(content = JsonInclude.Include.NON_NULL)
+class PublicKeyCredentialRequestOptionsJackson2Mixin {
+
+	@JsonSerialize(using = DurationJackson2Serializer.class)
+	private final @Nullable Duration timeout = null;
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsMixin.java
index b2bc403c87..67425588f3 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsMixin.java
@@ -19,8 +19,8 @@ package org.springframework.security.web.webauthn.jackson;
 import java.time.Duration;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import org.jspecify.annotations.Nullable;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeDeserializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeDeserializer.java
index 94f76b3262..7af1ae2b35 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeDeserializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeDeserializer.java
@@ -16,12 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JacksonException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonParser;
+import tools.jackson.databind.DeserializationContext;
+import tools.jackson.databind.deser.std.StdDeserializer;
 
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
 
@@ -42,8 +40,7 @@ class PublicKeyCredentialTypeDeserializer extends StdDeserializer {
+
+	/**
+	 * Creates a new instance.
+	 */
+	PublicKeyCredentialTypeJackson2Deserializer() {
+		super(PublicKeyCredentialType.class);
+	}
+
+	@Override
+	public PublicKeyCredentialType deserialize(JsonParser parser, DeserializationContext ctxt)
+			throws IOException, JacksonException {
+		String type = parser.readValueAs(String.class);
+		return PublicKeyCredentialType.valueOf(type);
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Mixin.java
new file mode 100644
index 0000000000..09d579a5e8
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Mixin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
+
+/**
+ * Jackson mixin for {@link PublicKeyCredentialType}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialTypeMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonSerialize(using = PublicKeyCredentialTypeJackson2Serializer.class)
+@JsonDeserialize(using = PublicKeyCredentialTypeJackson2Deserializer.class)
+abstract class PublicKeyCredentialTypeJackson2Mixin {
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Serializer.java
new file mode 100644
index 0000000000..d2f71a4e83
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Serializer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
+
+/**
+ * Jackson serializer for {@link PublicKeyCredentialType}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialTypeSerializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class PublicKeyCredentialTypeJackson2Serializer extends StdSerializer {
+
+	/**
+	 * Creates a new instance.
+	 */
+	PublicKeyCredentialTypeJackson2Serializer() {
+		super(PublicKeyCredentialType.class);
+	}
+
+	@Override
+	public void serialize(PublicKeyCredentialType type, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeString(type.getValue());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeMixin.java
index fc9e6f533d..3918c22f95 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeMixin.java
@@ -16,8 +16,8 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeSerializer.java
index 9078dccc4e..e2b08d1388 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
 
@@ -41,8 +40,8 @@ class PublicKeyCredentialTypeSerializer extends StdSerializer credential,
+			@JsonProperty("label") String label) {
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Mixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Mixin.java
new file mode 100644
index 0000000000..26ed140b39
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Mixin.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
+
+/**
+ * Jackson mixin for {@link ResidentKeyRequirement}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.ResidentKeyRequirementMixin}
+ * based on Jackson 3
+ */
+@SuppressWarnings("removal")
+@Deprecated(forRemoval = true)
+@JsonSerialize(using = ResidentKeyRequirementJackson2Serializer.class)
+abstract class ResidentKeyRequirementJackson2Mixin {
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Serializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Serializer.java
new file mode 100644
index 0000000000..23947e6454
--- /dev/null
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Serializer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
+
+/**
+ * Jackson serializer for {@link ResidentKeyRequirement}
+ *
+ * @author Rob Winch
+ * @since 6.4
+ * @deprecated as of 7.0 in favor of
+ * {@link org.springframework.security.web.webauthn.jackson.ResidentKeyRequirementSerializer}
+ * based on Jackson 3
+ */
+@Deprecated(forRemoval = true)
+@SuppressWarnings("serial")
+class ResidentKeyRequirementJackson2Serializer extends StdSerializer {
+
+	/**
+	 * Creates a new instance.
+	 */
+	ResidentKeyRequirementJackson2Serializer() {
+		super(ResidentKeyRequirement.class);
+	}
+
+	@Override
+	public void serialize(ResidentKeyRequirement requirement, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeString(requirement.getValue());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementMixin.java
index b6527c3a0a..ed24f1f43b 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementMixin.java
@@ -16,7 +16,7 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementSerializer.java
index 554d3d5694..58c7ff5dc5 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
 
@@ -41,8 +40,8 @@ class ResidentKeyRequirementSerializer extends StdSerializer {
+
+	/**
+	 * Creates a new instance.
+	 */
+	UserVerificationRequirementJackson2Serializer() {
+		super(UserVerificationRequirement.class);
+	}
+
+	@Override
+	public void serialize(UserVerificationRequirement requirement, JsonGenerator jgen, SerializerProvider provider)
+			throws IOException {
+		jgen.writeString(requirement.getValue());
+	}
+
+}
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementMixin.java
index 06fa2a94bc..32e48eacef 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementMixin.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementMixin.java
@@ -16,7 +16,7 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import tools.jackson.databind.annotation.JsonSerialize;
 
 import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
 
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementSerializer.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementSerializer.java
index 29290f12b8..6ac2f344b4 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementSerializer.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementSerializer.java
@@ -16,11 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.databind.SerializationContext;
+import tools.jackson.databind.ser.std.StdSerializer;
 
 import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
 
@@ -41,8 +40,8 @@ class UserVerificationRequirementSerializer extends StdSerializer converter = new MappingJackson2HttpMessageConverter(
-			Jackson2ObjectMapperBuilder.json().modules(new WebauthnJackson2Module()).build());
+	private HttpMessageConverter converter = new JacksonJsonHttpMessageConverter(
+			JsonMapper.builder().addModule(new WebauthnJacksonModule()).build());
 
 	/**
 	 * Creates a new instance.
@@ -131,7 +131,7 @@ public class PublicKeyCredentialCreationOptionsFilter extends OncePerRequestFilt
 	/**
 	 * Set the {@link HttpMessageConverter} to read the
 	 * {@link WebAuthnRegistrationFilter.WebAuthnRegistrationRequest} and write the
-	 * response. The default is {@link MappingJackson2HttpMessageConverter}.
+	 * response. The default is {@link JacksonJsonHttpMessageConverter}.
 	 * @param converter the {@link HttpMessageConverter} to use. Cannot be null.
 	 */
 	public void setConverter(HttpMessageConverter converter) {
diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java
index c6359d0c4c..781faf7295 100644
--- a/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java
+++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java
@@ -18,7 +18,6 @@ package org.springframework.security.web.webauthn.registration;
 
 import java.io.IOException;
 
-import com.fasterxml.jackson.databind.json.JsonMapper;
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
@@ -26,13 +25,14 @@ import jakarta.servlet.http.HttpServletResponse;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.jspecify.annotations.Nullable;
+import tools.jackson.databind.json.JsonMapper;
 
 import org.springframework.http.HttpInputMessage;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
 import org.springframework.http.server.ServletServerHttpRequest;
 import org.springframework.http.server.ServletServerHttpResponse;
 import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
@@ -40,7 +40,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.security.web.webauthn.api.Bytes;
 import org.springframework.security.web.webauthn.api.CredentialRecord;
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
-import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module;
+import org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule;
 import org.springframework.security.web.webauthn.management.ImmutableRelyingPartyRegistrationRequest;
 import org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;
 import org.springframework.security.web.webauthn.management.UserCredentialRepository;
@@ -88,8 +88,8 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
 
 	private final UserCredentialRepository userCredentials;
 
-	private HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(
-			JsonMapper.builder().addModule(new WebauthnJackson2Module()).build());
+	private HttpMessageConverter converter = new JacksonJsonHttpMessageConverter(
+			JsonMapper.builder().addModule(new WebauthnJacksonModule()).build());
 
 	private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();
 
@@ -152,7 +152,7 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
 	/**
 	 * Set the {@link HttpMessageConverter} to read the
 	 * {@link WebAuthnRegistrationRequest} and write the response. The default is
-	 * {@link MappingJackson2HttpMessageConverter}.
+	 * {@link JacksonJsonHttpMessageConverter}.
 	 * @param converter the {@link HttpMessageConverter} to use. Cannot be null.
 	 */
 	public void setConverter(HttpMessageConverter converter) {
diff --git a/webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilterTests.java b/webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilterTests.java
index 74e2c8603f..a5853b91cd 100644
--- a/webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilterTests.java
+++ b/webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilterTests.java
@@ -27,7 +27,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 import org.skyscreamer.jsonassert.JSONAssert;
 
 import org.springframework.http.HttpStatus;
-import org.springframework.http.converter.GenericHttpMessageConverter;
+import org.springframework.http.converter.SmartHttpMessageConverter;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockServletContext;
@@ -79,7 +79,7 @@ class WebAuthnAuthenticationFilterTests {
 			""";
 
 	@Mock
-	private GenericHttpMessageConverter converter;
+	private SmartHttpMessageConverter converter;
 
 	@Mock
 	private PublicKeyCredentialRequestOptionsRepository requestOptionsRepository;
diff --git a/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJackson2Tests.java b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJackson2Tests.java
new file mode 100644
index 0000000000..dcc9521da1
--- /dev/null
+++ b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJackson2Tests.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;
+import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;
+
+/**
+ * Test Jackson serialization of CredProtectAuthenticationExtensionsClientInput
+ *
+ * @author Rob Winch
+ */
+class CredProtectAuthenticationExtensionsClientInputJackson2Tests {
+
+	private ObjectMapper mapper;
+
+	@BeforeEach
+	void setup() {
+		this.mapper = new ObjectMapper();
+		this.mapper.registerModule(new WebauthnJackson2Module());
+	}
+
+	@Test
+	void writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationOptional() throws Exception {
+		String expected = """
+					{
+						"credentialProtectionPolicy": "userVerificationOptional",
+						"enforceCredentialProtectionPolicy": true
+					}
+				""";
+
+		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(
+				CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,
+				true);
+		CredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(
+				credProtect);
+		ImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(
+				credProtectInput);
+
+		String actual = this.mapper.writeValueAsString(clientInputs);
+
+		JSONAssert.assertEquals(expected, actual, false);
+	}
+
+	@Test
+	void writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationOptionalWithCredentialIdList()
+			throws Exception {
+		String expected = """
+					{
+						"credentialProtectionPolicy": "userVerificationOptionalWithCredentialIdList",
+						"enforceCredentialProtectionPolicy": true
+					}
+				""";
+
+		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(
+				CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST,
+				true);
+		CredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(
+				credProtect);
+		ImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(
+				credProtectInput);
+
+		String actual = this.mapper.writeValueAsString(clientInputs);
+
+		JSONAssert.assertEquals(expected, actual, false);
+	}
+
+	@Test
+	void writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationRequired() throws Exception {
+		String expected = """
+					{
+						"credentialProtectionPolicy": "userVerificationRequired",
+						"enforceCredentialProtectionPolicy": true
+					}
+				""";
+
+		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(
+				CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_REQUIRED,
+				true);
+		CredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(
+				credProtect);
+		ImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(
+				credProtectInput);
+
+		String actual = this.mapper.writeValueAsString(clientInputs);
+
+		JSONAssert.assertEquals(expected, actual, false);
+	}
+
+	@Test
+	void writeAuthenticationExtensionsClientInputsWhenEnforceCredentialProtectionPolicyTrue() throws Exception {
+		String expected = """
+					{
+						"credentialProtectionPolicy": "userVerificationOptional",
+						"enforceCredentialProtectionPolicy": true
+					}
+				""";
+
+		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(
+				CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,
+				true);
+		CredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(
+				credProtect);
+		ImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(
+				credProtectInput);
+
+		String actual = this.mapper.writeValueAsString(clientInputs);
+
+		JSONAssert.assertEquals(expected, actual, false);
+	}
+
+	@Test
+	void writeAuthenticationExtensionsClientInputsWhenEnforceCredentialProtectionPolicyFalse() throws Exception {
+		String expected = """
+					{
+						"credentialProtectionPolicy": "userVerificationOptional",
+						"enforceCredentialProtectionPolicy": false
+					}
+				""";
+
+		CredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(
+				CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,
+				false);
+		CredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(
+				credProtect);
+		ImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(
+				credProtectInput);
+
+		String actual = this.mapper.writeValueAsString(clientInputs);
+
+		JSONAssert.assertEquals(expected, actual, false);
+	}
+
+}
diff --git a/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJacksonTests.java b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJacksonTests.java
index 50d52fa1d6..d2beb3d53f 100644
--- a/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJacksonTests.java
+++ b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJacksonTests.java
@@ -16,10 +16,10 @@
 
 package org.springframework.security.web.webauthn.jackson;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.skyscreamer.jsonassert.JSONAssert;
+import tools.jackson.databind.json.JsonMapper;
 
 import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;
 import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;
@@ -31,12 +31,11 @@ import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExte
  */
 class CredProtectAuthenticationExtensionsClientInputJacksonTests {
 
-	private ObjectMapper mapper;
+	private JsonMapper mapper;
 
 	@BeforeEach
 	void setup() {
-		this.mapper = new ObjectMapper();
-		this.mapper.registerModule(new WebauthnJackson2Module());
+		this.mapper = JsonMapper.builder().addModule(new WebauthnJacksonModule()).build();
 	}
 
 	@Test
diff --git a/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/Jackson2Tests.java b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/Jackson2Tests.java
new file mode 100644
index 0000000000..d93384cb13
--- /dev/null
+++ b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/Jackson2Tests.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2004-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.webauthn.jackson;
+
+import java.time.Duration;
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
+import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
+import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
+import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
+import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
+import org.springframework.security.web.webauthn.api.Bytes;
+import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;
+import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInput;
+import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;
+import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;
+import org.springframework.security.web.webauthn.api.PublicKeyCredential;
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
+import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
+import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
+import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class Jackson2Tests {
+
+	private ObjectMapper mapper;
+
+	@BeforeEach
+	void setup() {
+		this.mapper = new ObjectMapper();
+		this.mapper.registerModule(new WebauthnJackson2Module());
+	}
+
+	@Test
+	void readAuthenticatorTransport() throws Exception {
+		AuthenticatorTransport transport = this.mapper.readValue("\"hybrid\"", AuthenticatorTransport.class);
+
+		assertThat(transport).isEqualTo(AuthenticatorTransport.HYBRID);
+	}
+
+	@Test
+	void readAuthenticatorAttachment() throws Exception {
+		AuthenticatorAttachment value = this.mapper.readValue("\"cross-platform\"", AuthenticatorAttachment.class);
+		assertThat(value).isEqualTo(AuthenticatorAttachment.CROSS_PLATFORM);
+	}
+
+	@Test
+	void writeAuthenticatorAttachment() throws Exception {
+		String value = this.mapper.writeValueAsString(AuthenticatorAttachment.CROSS_PLATFORM);
+		assertThat(value).isEqualTo("\"cross-platform\"");
+	}
+
+	@Test
+	void readAuthenticationExtensionsClientOutputs() throws Exception {
+		String json = """
+				{
+					"credProps": {
+						"rk": false
+					}
+				}
+				""";
+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(
+				new CredentialPropertiesOutput(false));
+
+		AuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,
+				AuthenticationExtensionsClientOutputs.class);
+		assertThat(outputs).usingRecursiveComparison().isEqualTo(clientExtensionResults);
+	}
+
+	@Test
+	void readAuthenticationExtensionsClientOutputsWhenAuthenticatorDisplayName() throws Exception {
+		String json = """
+				{
+					"credProps": {
+						"rk": false,
+						"authenticatorDisplayName": "1Password"
+					}
+				}
+				""";
+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(
+				new CredentialPropertiesOutput(false));
+
+		AuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,
+				AuthenticationExtensionsClientOutputs.class);
+		assertThat(outputs).usingRecursiveComparison().isEqualTo(clientExtensionResults);
+	}
+
+	@Test
+	void readCredPropsWhenAuthenticatorDisplayName() throws Exception {
+		String json = """
+				{
+					"rk": false,
+					"authenticatorDisplayName": "1Password"
+				}
+				""";
+		CredentialPropertiesOutput credProps = new CredentialPropertiesOutput(false);
+
+		CredentialPropertiesOutput outputs = this.mapper.readValue(json, CredentialPropertiesOutput.class);
+		assertThat(outputs).usingRecursiveComparison().isEqualTo(credProps);
+	}
+
+	@Test
+	void readAuthenticationExtensionsClientOutputsWhenFieldAfter() throws Exception {
+		String json = """
+				{
+					"clientOutputs": {
+						"credProps": {
+							"rk": false
+						}
+					},
+					"label": "Cell Phone"
+				}
+				""";
+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(
+				new CredentialPropertiesOutput(false));
+
+		ClassWithOutputsAndAnotherField expected = new ClassWithOutputsAndAnotherField();
+		expected.setClientOutputs(clientExtensionResults);
+		expected.setLabel("Cell Phone");
+
+		ClassWithOutputsAndAnotherField actual = this.mapper.readValue(json, ClassWithOutputsAndAnotherField.class);
+		assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
+	}
+
+	@Test
+	void writePublicKeyCredentialCreationOptions() throws Exception {
+		String expected = """
+				{
+				    "attestation": "none",
+				    "authenticatorSelection": {
+				        "residentKey": "required"
+				    },
+				    "challenge": "q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc",
+				    "excludeCredentials": [],
+				    "extensions": {
+				        "credProps": true
+				    },
+				    "pubKeyCredParams": [
+				        {
+				            "alg": -7,
+				            "type": "public-key"
+				        },{
+				            "alg": -8,
+				            "type": "public-key"
+				        },
+				        {
+				            "alg": -257,
+				            "type": "public-key"
+				        }
+				    ],
+				    "rp": {
+				        "id": "example.localhost",
+				        "name": "SimpleWebAuthn Example"
+				    },
+				    "timeout": 300000,
+				    "user": {
+				        "displayName": "user@example.localhost",
+				        "id": "oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w",
+				        "name": "user@example.localhost"
+				    }
+				}
+				""";
+
+		PublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions
+			.createPublicKeyCredentialCreationOptions()
+			.build();
+
+		String string = this.mapper.writeValueAsString(options);
+
+		JSONAssert.assertEquals(expected, string, false);
+	}
+
+	@Test
+	void readPublicKeyCredentialAuthenticatorAttestationResponse() throws Exception {
+
+		PublicKeyCredential publicKeyCredential = this.mapper.readValue(
+				PublicKeyCredentialJson.PUBLIC_KEY_JSON,
+				new TypeReference>() {
+				});
+
+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(
+				new CredentialPropertiesOutput(false));
+
+		PublicKeyCredential expected = PublicKeyCredential.builder()
+			.id("AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM")
+			.rawId(Bytes
+				.fromBase64("AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM"))
+			.response(AuthenticatorAttestationResponse.builder()
+				.attestationObject(Bytes.fromBase64(
+						"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk"))
+				.clientDataJSON(Bytes.fromBase64(
+						"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0"))
+				.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL)
+				.build())
+			.type(PublicKeyCredentialType.PUBLIC_KEY)
+			.clientExtensionResults(clientExtensionResults)
+			.authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)
+			.build();
+
+		assertThat(publicKeyCredential).usingRecursiveComparison().isEqualTo(expected);
+	}
+
+	@Test
+	void readPublicKeyCredentialAuthenticatorAttestationResponseWhenExtraFields() throws Exception {
+		final String json = """
+				{
+					 "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk",
+					 "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
+					 "transports": [
+					   "hybrid",
+					   "internal"
+					 ],
+					 "publicKeyAlgorithm": -7,
+					 "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkr7Z6k8TDS6Mc36C9WnYend5_wLNTfOrA7nKXHwvY6wrnHk6VMYQ_EtL7zlMAAG6bhqpUrgJJYnstgN2SO4EuQ",
+					 "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk"
+				}
+				""";
+		AuthenticatorAttestationResponse response = this.mapper.readValue(json, AuthenticatorAttestationResponse.class);
+
+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(
+				new CredentialPropertiesOutput(false));
+
+		AuthenticatorAttestationResponse expected = AuthenticatorAttestationResponse.builder()
+			.attestationObject(Bytes.fromBase64(
+					"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk"))
+			.clientDataJSON(Bytes.fromBase64(
+					"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0"))
+			.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL)
+			.build();
+
+		assertThat(response).usingRecursiveComparison().isEqualTo(expected);
+	}
+
+	@Test
+	void writeAuthenticationOptions() throws Exception {
+		PublicKeyCredentialRequestOptions credentialRequestOptions = PublicKeyCredentialRequestOptions.builder()
+			.allowCredentials(Arrays.asList())
+			.challenge(Bytes.fromBase64("I69THX904Q8ONhCgUgOu2PCQCcEjTDiNmokdbgsAsYU"))
+			.rpId("example.localhost")
+			.timeout(Duration.ofMinutes(5))
+			.userVerification(UserVerificationRequirement.REQUIRED)
+			.build();
+		String actual = this.mapper.writeValueAsString(credentialRequestOptions);
+
+		String expected = """
+						{
+				    "challenge": "I69THX904Q8ONhCgUgOu2PCQCcEjTDiNmokdbgsAsYU",
+				    "allowCredentials": [],
+				    "timeout": 300000,
+				    "userVerification": "required",
+				    "rpId": "example.localhost"
+				  }
+
+				""";
+		JSONAssert.assertEquals(expected, actual, false);
+	}
+
+	@Test
+	void readPublicKeyCredentialAuthenticatorAssertionResponse() throws Exception {
+		String json = """
+					{
+					   "id": "IquGb208Fffq2cROa1ZxMg",
+					   "rawId": "IquGb208Fffq2cROa1ZxMg",
+					   "response": {
+						 "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA",
+						 "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaDB2Z3dHUWpvQ3pBekRVc216UHBrLUpWSUpSUmduMEw0S1ZTWU5SY0VaYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
+						 "signature": "MEUCIAdfzPAn3voyXynwa0IXk1S0envMY5KP3NEe9aj4B2BuAiEAm_KJhQoWXdvfhbzwACU3NM4ltQe7_Il46qFUwtpuTdg",
+						 "userHandle": "oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w"
+					   },
+					   "type": "public-key",
+					   "clientExtensionResults": {},
+					   "authenticatorAttachment": "cross-platform"
+					 }
+				""";
+		PublicKeyCredential publicKeyCredential = this.mapper.readValue(json,
+				new TypeReference>() {
+				});
+
+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs();
+
+		PublicKeyCredential expected = PublicKeyCredential.builder()
+			.id("IquGb208Fffq2cROa1ZxMg")
+			.rawId(Bytes.fromBase64("IquGb208Fffq2cROa1ZxMg"))
+			.response(AuthenticatorAssertionResponse.builder()
+				.authenticatorData(Bytes.fromBase64("SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA"))
+				.clientDataJSON(Bytes.fromBase64(
+						"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaDB2Z3dHUWpvQ3pBekRVc216UHBrLUpWSUpSUmduMEw0S1ZTWU5SY0VaYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0"))
+				.signature(Bytes.fromBase64(
+						"MEUCIAdfzPAn3voyXynwa0IXk1S0envMY5KP3NEe9aj4B2BuAiEAm_KJhQoWXdvfhbzwACU3NM4ltQe7_Il46qFUwtpuTdg"))
+				.userHandle(Bytes.fromBase64("oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w"))
+				.build())
+			.type(PublicKeyCredentialType.PUBLIC_KEY)
+			.clientExtensionResults(clientExtensionResults)
+			.authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)
+			.build();
+
+		assertThat(publicKeyCredential).usingRecursiveComparison().isEqualTo(expected);
+	}
+
+	@Test
+	void writeAuthenticationExtensionsClientInputsWhenCredPropsTrue() throws Exception {
+		String expected = """
+					{
+						"credProps": true
+					}
+				""";
+
+		ImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(
+				ImmutableAuthenticationExtensionsClientInput.credProps);
+
+		String actual = this.mapper.writeValueAsString(clientInputs);
+
+		JSONAssert.assertEquals(expected, actual, false);
+	}
+
+	public static class ClassWithOutputsAndAnotherField {
+
+		private String label;
+
+		private AuthenticationExtensionsClientOutputs clientOutputs;
+
+		public String getLabel() {
+			return this.label;
+		}
+
+		public void setLabel(String label) {
+			this.label = label;
+		}
+
+		public AuthenticationExtensionsClientOutputs getClientOutputs() {
+			return this.clientOutputs;
+		}
+
+		public void setClientOutputs(AuthenticationExtensionsClientOutputs clientOutputs) {
+			this.clientOutputs = clientOutputs;
+		}
+
+	}
+
+}
diff --git a/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/JacksonTests.java b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/JacksonTests.java
index ba970125e0..6c68662e80 100644
--- a/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/JacksonTests.java
+++ b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/JacksonTests.java
@@ -19,11 +19,11 @@ package org.springframework.security.web.webauthn.jackson;
 import java.time.Duration;
 import java.util.Arrays;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.skyscreamer.jsonassert.JSONAssert;
+import tools.jackson.core.type.TypeReference;
+import tools.jackson.databind.json.JsonMapper;
 
 import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
 import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
@@ -46,12 +46,11 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 class JacksonTests {
 
-	private ObjectMapper mapper;
+	private JsonMapper mapper;
 
 	@BeforeEach
 	void setup() {
-		this.mapper = new ObjectMapper();
-		this.mapper.registerModule(new WebauthnJackson2Module());
+		this.mapper = JsonMapper.builder().addModule(new WebauthnJacksonModule()).build();
 	}
 
 	@Test
diff --git a/webauthn/src/test/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationRequestJacksonTests.java b/webauthn/src/test/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationRequestJacksonTests.java
index f5db6c5227..34aebf6c91 100644
--- a/webauthn/src/test/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationRequestJacksonTests.java
+++ b/webauthn/src/test/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationRequestJacksonTests.java
@@ -16,9 +16,9 @@
 
 package org.springframework.security.web.webauthn.registration;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import tools.jackson.databind.json.JsonMapper;
 
 import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
 import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
@@ -29,7 +29,7 @@ import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExte
 import org.springframework.security.web.webauthn.api.PublicKeyCredential;
 import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
 import org.springframework.security.web.webauthn.jackson.PublicKeyCredentialJson;
-import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module;
+import org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule;
 import org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -40,12 +40,11 @@ import static org.assertj.core.api.Assertions.assertThat;
  */
 class WebAuthnRegistrationRequestJacksonTests {
 
-	private ObjectMapper mapper;
+	private JsonMapper mapper;
 
 	@BeforeEach
 	void setup() {
-		this.mapper = new ObjectMapper();
-		this.mapper.registerModule(new WebauthnJackson2Module());
+		this.mapper = JsonMapper.builder().addModule(new WebauthnJacksonModule()).build();
 	}
 
 	@Test