Skip to content

Commit 8b2ac9c

Browse files
committed
Merge Add Jackson Mixin for WebAuthnAuthentication
2 parents 63d31d0 + 47146f3 commit 8b2ac9c

File tree

4 files changed

+218
-0
lines changed

4 files changed

+218
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.web.webauthn.jackson;
18+
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
21+
import org.springframework.security.web.webauthn.api.Bytes;
22+
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;
23+
24+
/**
25+
* Jackson mixin for {@link ImmutablePublicKeyCredentialUserEntity}
26+
*
27+
* @author Toshiaki Maki
28+
* @since 7.0.4
29+
*/
30+
abstract class ImmutablePublicKeyCredentialUserEntityMixin {
31+
32+
ImmutablePublicKeyCredentialUserEntityMixin(@JsonProperty("name") String name, @JsonProperty("id") Bytes id,
33+
@JsonProperty("displayName") String displayName) {
34+
}
35+
36+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.web.webauthn.jackson;
18+
19+
import java.util.Collection;
20+
21+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
22+
import com.fasterxml.jackson.annotation.JsonProperty;
23+
24+
import org.springframework.security.core.GrantedAuthority;
25+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
26+
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication;
27+
28+
/**
29+
* Jackson mixin for {@link WebAuthnAuthentication}
30+
*
31+
* @author Toshiaki Maki
32+
* @since 7.0.4
33+
*/
34+
@JsonIgnoreProperties({ "authenticated" })
35+
abstract class WebAuthnAuthenticationMixin {
36+
37+
WebAuthnAuthenticationMixin(@JsonProperty("principal") PublicKeyCredentialUserEntity principal,
38+
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
39+
}
40+
41+
}

webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebauthnJacksonModule.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@
3333
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
3434
import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;
3535
import org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;
36+
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;
3637
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
3738
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
3839
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
3940
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
4041
import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
4142
import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
43+
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication;
4244
import org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;
4345

4446
/**
@@ -47,6 +49,7 @@
4749
*
4850
* @author Sebastien Deleuze
4951
* @author Rob Winch
52+
* @author Toshiaki Maki
5053
* @since 7.0
5154
*/
5255
@SuppressWarnings("serial")
@@ -61,6 +64,8 @@ public WebauthnJacksonModule() {
6164

6265
@Override
6366
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
67+
builder.allowIfSubType(WebAuthnAuthentication.class)
68+
.allowIfSubType(ImmutablePublicKeyCredentialUserEntity.class);
6469
}
6570

6671
@Override
@@ -92,6 +97,9 @@ public void setupModule(SetupContext context) {
9297
context.setMixIn(RelyingPartyPublicKey.class, RelyingPartyPublicKeyMixin.class);
9398
context.setMixIn(ResidentKeyRequirement.class, ResidentKeyRequirementMixin.class);
9499
context.setMixIn(UserVerificationRequirement.class, UserVerificationRequirementMixin.class);
100+
context.setMixIn(WebAuthnAuthentication.class, WebAuthnAuthenticationMixin.class);
101+
context.setMixIn(ImmutablePublicKeyCredentialUserEntity.class,
102+
ImmutablePublicKeyCredentialUserEntityMixin.class);
95103
}
96104

97105
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.web.webauthn.jackson;
18+
19+
import java.util.List;
20+
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.Test;
23+
import org.skyscreamer.jsonassert.JSONAssert;
24+
import tools.jackson.databind.JacksonModule;
25+
import tools.jackson.databind.json.JsonMapper;
26+
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
27+
28+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
29+
import org.springframework.security.jackson.SecurityJacksonModules;
30+
import org.springframework.security.web.webauthn.api.Bytes;
31+
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;
32+
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
36+
/**
37+
* Tests for {@link WebAuthnAuthenticationMixin} and
38+
* {@link ImmutablePublicKeyCredentialUserEntityMixin} with polymorphic type handling.
39+
*
40+
* <p>
41+
* This test class is separate from {@link JacksonTests} because it requires a
42+
* {@link JsonMapper} configured with {@link SecurityJacksonModules} to enable polymorphic
43+
* type information ({@code @class}). {@link JacksonTests} uses a {@link JsonMapper}
44+
* configured only with {@link WebauthnJacksonModule}, and its existing custom serializers
45+
* are not compatible with the automatic inclusion of type information enabled by
46+
* {@link SecurityJacksonModules}.
47+
*
48+
* @author Toshiaki Maki
49+
* @since 7.1
50+
*/
51+
class WebAuthnAuthenticationMixinTests {
52+
53+
private JsonMapper mapper;
54+
55+
@BeforeEach
56+
void setup() {
57+
ClassLoader classLoader = getClass().getClassLoader();
58+
WebauthnJacksonModule webauthnJacksonModule = new WebauthnJacksonModule();
59+
BasicPolymorphicTypeValidator.Builder typeValidatorBuilder = BasicPolymorphicTypeValidator.builder();
60+
webauthnJacksonModule.configurePolymorphicTypeValidator(typeValidatorBuilder);
61+
List<JacksonModule> modules = SecurityJacksonModules.getModules(classLoader, typeValidatorBuilder);
62+
modules.add(webauthnJacksonModule);
63+
this.mapper = JsonMapper.builder().addModules(modules).build();
64+
}
65+
66+
@Test
67+
void writeWebAuthnAuthentication() throws Exception {
68+
ImmutablePublicKeyCredentialUserEntity principal = (ImmutablePublicKeyCredentialUserEntity) ImmutablePublicKeyCredentialUserEntity
69+
.builder()
70+
.name("user@example.localhost")
71+
.id(Bytes.fromBase64("oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w"))
72+
.displayName("User")
73+
.build();
74+
WebAuthnAuthentication authentication = new WebAuthnAuthentication(principal,
75+
List.of(new SimpleGrantedAuthority("ROLE_USER")));
76+
77+
String json = this.mapper.writeValueAsString(authentication);
78+
79+
String expected = """
80+
{
81+
"@class": "org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication",
82+
"principal": {
83+
"@class": "org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity",
84+
"name": "user@example.localhost",
85+
"id": "oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w",
86+
"displayName": "User"
87+
},
88+
"authorities": ["java.util.Collections$UnmodifiableRandomAccessList", [
89+
{
90+
"@class": "org.springframework.security.core.authority.SimpleGrantedAuthority",
91+
"authority": "ROLE_USER"
92+
}
93+
]]
94+
}
95+
""";
96+
JSONAssert.assertEquals(expected, json, false);
97+
assertThat(json).doesNotContain("\"authenticated\"");
98+
}
99+
100+
@Test
101+
void readWebAuthnAuthentication() throws Exception {
102+
String json = """
103+
{
104+
"@class": "org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication",
105+
"principal": {
106+
"@class": "org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity",
107+
"name": "user@example.localhost",
108+
"id": "oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w",
109+
"displayName": "User"
110+
},
111+
"authorities": ["java.util.Collections$UnmodifiableRandomAccessList", [
112+
{
113+
"@class": "org.springframework.security.core.authority.SimpleGrantedAuthority",
114+
"authority": "ROLE_USER"
115+
}
116+
]]
117+
}
118+
""";
119+
ImmutablePublicKeyCredentialUserEntity expectedPrincipal = (ImmutablePublicKeyCredentialUserEntity) ImmutablePublicKeyCredentialUserEntity
120+
.builder()
121+
.name("user@example.localhost")
122+
.id(Bytes.fromBase64("oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w"))
123+
.displayName("User")
124+
.build();
125+
WebAuthnAuthentication expected = new WebAuthnAuthentication(expectedPrincipal,
126+
List.of(new SimpleGrantedAuthority("ROLE_USER")));
127+
128+
WebAuthnAuthentication authentication = this.mapper.readValue(json, WebAuthnAuthentication.class);
129+
130+
assertThat(authentication).usingRecursiveComparison().isEqualTo(expected);
131+
}
132+
133+
}

0 commit comments

Comments
 (0)