diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ImmutablePublicKeyCredentialUserEntityMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ImmutablePublicKeyCredentialUserEntityMixin.java new file mode 100644 index 00000000000..3ab59c43927 --- /dev/null +++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ImmutablePublicKeyCredentialUserEntityMixin.java @@ -0,0 +1,36 @@ +/* + * 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.JsonProperty; + +import org.springframework.security.web.webauthn.api.Bytes; +import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity; + +/** + * Jackson mixin for {@link ImmutablePublicKeyCredentialUserEntity} + * + * @author Toshiaki Maki + * @since 7.1 + */ +abstract class ImmutablePublicKeyCredentialUserEntityMixin { + + ImmutablePublicKeyCredentialUserEntityMixin(@JsonProperty("name") String name, @JsonProperty("id") Bytes id, + @JsonProperty("displayName") String displayName) { + } + +} diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixin.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixin.java new file mode 100644 index 00000000000..f9810f8cf65 --- /dev/null +++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixin.java @@ -0,0 +1,41 @@ +/* + * 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.util.Collection; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity; +import org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication; + +/** + * Jackson mixin for {@link WebAuthnAuthentication} + * + * @author Toshiaki Maki + * @since 7.1 + */ +@JsonIgnoreProperties({ "authenticated" }) +abstract class WebAuthnAuthenticationMixin { + + WebAuthnAuthenticationMixin(@JsonProperty("principal") PublicKeyCredentialUserEntity principal, + @JsonProperty("authorities") Collection extends GrantedAuthority> authorities) { + } + +} diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebauthnJacksonModule.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebauthnJacksonModule.java index 2a6bd62056c..f8820773c30 100644 --- a/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebauthnJacksonModule.java +++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebauthnJacksonModule.java @@ -33,12 +33,14 @@ import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier; import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput; import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput; +import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity; 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.ResidentKeyRequirement; import org.springframework.security.web.webauthn.api.UserVerificationRequirement; +import org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication; import org.springframework.security.web.webauthn.management.RelyingPartyPublicKey; /** @@ -47,6 +49,7 @@ * * @author Sebastien Deleuze * @author Rob Winch + * @author Toshiaki Maki * @since 7.0 */ @SuppressWarnings("serial") @@ -61,6 +64,8 @@ public WebauthnJacksonModule() { @Override public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) { + builder.allowIfSubType(WebAuthnAuthentication.class) + .allowIfSubType(ImmutablePublicKeyCredentialUserEntity.class); } @Override @@ -92,6 +97,9 @@ public void setupModule(SetupContext context) { context.setMixIn(RelyingPartyPublicKey.class, RelyingPartyPublicKeyMixin.class); context.setMixIn(ResidentKeyRequirement.class, ResidentKeyRequirementMixin.class); context.setMixIn(UserVerificationRequirement.class, UserVerificationRequirementMixin.class); + context.setMixIn(WebAuthnAuthentication.class, WebAuthnAuthenticationMixin.class); + context.setMixIn(ImmutablePublicKeyCredentialUserEntity.class, + ImmutablePublicKeyCredentialUserEntityMixin.class); } } diff --git a/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixinTests.java b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixinTests.java new file mode 100644 index 00000000000..426acafd976 --- /dev/null +++ b/webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixinTests.java @@ -0,0 +1,133 @@ +/* + * 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.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import tools.jackson.databind.JacksonModule; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator; + +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.jackson.SecurityJacksonModules; +import org.springframework.security.web.webauthn.api.Bytes; +import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity; +import org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebAuthnAuthenticationMixin} and + * {@link ImmutablePublicKeyCredentialUserEntityMixin} with polymorphic type handling. + * + *
+ * This test class is separate from {@link JacksonTests} because it requires a
+ * {@link JsonMapper} configured with {@link SecurityJacksonModules} to enable polymorphic
+ * type information ({@code @class}). {@link JacksonTests} uses a {@link JsonMapper}
+ * configured only with {@link WebauthnJacksonModule}, and its existing custom serializers
+ * are not compatible with the automatic inclusion of type information enabled by
+ * {@link SecurityJacksonModules}.
+ *
+ * @author Toshiaki Maki
+ * @since 7.1
+ */
+class WebAuthnAuthenticationMixinTests {
+
+ private JsonMapper mapper;
+
+ @BeforeEach
+ void setup() {
+ ClassLoader classLoader = getClass().getClassLoader();
+ WebauthnJacksonModule webauthnJacksonModule = new WebauthnJacksonModule();
+ BasicPolymorphicTypeValidator.Builder typeValidatorBuilder = BasicPolymorphicTypeValidator.builder();
+ webauthnJacksonModule.configurePolymorphicTypeValidator(typeValidatorBuilder);
+ List