Skip to content

Commit bab5868

Browse files
Add serialize/deserialize PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions support with ObjectMapper
gh-16433 PublicKeyCredentialRequestOptions gh-16434 PublicKeyCredentialCreationOptions
1 parent d3332e1 commit bab5868

File tree

38 files changed

+1186
-90
lines changed

38 files changed

+1186
-90
lines changed

Diff for: web/spring-security-web.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies {
3636
api 'org.springframework:spring-web'
3737

3838
optional 'com.fasterxml.jackson.core:jackson-databind'
39+
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
3940
optional 'io.projectreactor:reactor-core'
4041
optional 'org.springframework:spring-jdbc'
4142
optional 'org.springframework:spring-tx'

Diff for: web/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorTransport.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* order to obtain an assertion for a specific credential.
2424
*
2525
* @author Rob Winch
26+
* @author Justin Cranford
2627
* @since 6.4
2728
*/
2829
public final class AuthenticatorTransport {
@@ -112,7 +113,7 @@ public static AuthenticatorTransport valueOf(String value) {
112113
}
113114

114115
public static AuthenticatorTransport[] values() {
115-
return new AuthenticatorTransport[] { USB, NFC, BLE, HYBRID, INTERNAL };
116+
return new AuthenticatorTransport[] { USB, NFC, BLE, SMART_CARD, HYBRID, INTERNAL };
116117
}
117118

118119
}

Diff for: web/src/main/java/org/springframework/security/web/webauthn/api/COSEAlgorithmIdentifier.java

+21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* used to identify a cryptographic algorithm.
2323
*
2424
* @author Rob Winch
25+
* @author Justin Cranford
2526
* @since 6.4
2627
* @see PublicKeyCredentialParameters#getAlg()
2728
*/
@@ -62,4 +63,24 @@ public static COSEAlgorithmIdentifier[] values() {
6263
return new COSEAlgorithmIdentifier[] { EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, RS1 };
6364
}
6465

66+
public static COSEAlgorithmIdentifier valueOf(long value) {
67+
if (COSEAlgorithmIdentifier.EdDSA.getValue() == value) {
68+
return EdDSA;
69+
} else if (COSEAlgorithmIdentifier.ES256.getValue() == value) {
70+
return ES256;
71+
} else if (COSEAlgorithmIdentifier.ES384.getValue() == value) {
72+
return ES384;
73+
} else if (COSEAlgorithmIdentifier.ES512.getValue() == value) {
74+
return ES512;
75+
} else if (COSEAlgorithmIdentifier.RS256.getValue() == value) {
76+
return RS256;
77+
} else if (COSEAlgorithmIdentifier.RS384.getValue() == value) {
78+
return RS384;
79+
} else if (COSEAlgorithmIdentifier.RS512.getValue() == value) {
80+
return RS512;
81+
} else if (COSEAlgorithmIdentifier.RS1.getValue() == value) {
82+
return RS1;
83+
}
84+
return new COSEAlgorithmIdentifier(value);
85+
}
6586
}

Diff for: web/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialParameters.java

+22
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* is used to supply additional parameters when creating a new credential.
2323
*
2424
* @author Rob Winch
25+
* @author Justin Cranford
2526
* @since 6.4
2627
* @see PublicKeyCredentialCreationOptions#getPubKeyCredParams()
2728
*/
@@ -96,4 +97,25 @@ public COSEAlgorithmIdentifier getAlg() {
9697
return this.alg;
9798
}
9899

100+
public static PublicKeyCredentialParameters valueOf(PublicKeyCredentialType type, COSEAlgorithmIdentifier alg) {
101+
if (PublicKeyCredentialParameters.EdDSA.getType().equals(type) && PublicKeyCredentialParameters.EdDSA.getAlg().equals(alg)) {
102+
return EdDSA;
103+
} else if (PublicKeyCredentialParameters.ES256.getType().equals(type) && PublicKeyCredentialParameters.ES256.getAlg().equals(alg)) {
104+
return ES256;
105+
} else if (PublicKeyCredentialParameters.ES384.getType().equals(type) && PublicKeyCredentialParameters.ES256.getAlg().equals(alg)) {
106+
return ES384;
107+
} else if (PublicKeyCredentialParameters.ES512.getType().equals(type) && PublicKeyCredentialParameters.ES256.getAlg().equals(alg)) {
108+
return ES512;
109+
} else if (PublicKeyCredentialParameters.RS256.getType().equals(type) && PublicKeyCredentialParameters.ES256.getAlg().equals(alg)) {
110+
return RS256;
111+
} else if (PublicKeyCredentialParameters.RS384.getType().equals(type) && PublicKeyCredentialParameters.ES256.getAlg().equals(alg)) {
112+
return RS384;
113+
} else if (PublicKeyCredentialParameters.RS512.getType().equals(type) && PublicKeyCredentialParameters.ES256.getAlg().equals(alg)) {
114+
return RS512;
115+
} else if (PublicKeyCredentialParameters.RS1.getType().equals(type) && PublicKeyCredentialParameters.ES256.getAlg().equals(alg)) {
116+
return RS1;
117+
}
118+
return new PublicKeyCredentialParameters(type, alg);
119+
}
120+
99121
}

Diff for: web/src/main/java/org/springframework/security/web/webauthn/api/UserVerificationRequirement.java

+21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* is used by the Relying Party to indicate if user verification is needed.
2323
*
2424
* @author Rob Winch
25+
* @author Justin Cranford
2526
* @since 6.4
2627
*/
2728
public final class UserVerificationRequirement {
@@ -66,4 +67,24 @@ public String getValue() {
6667
return this.value;
6768
}
6869

70+
/**
71+
* Gets the value
72+
* @param value the string
73+
* @return the value
74+
* @author Justin Cranford
75+
* @since 6.5
76+
*/
77+
public static UserVerificationRequirement valueOf(String value) {
78+
if (DISCOURAGED.getValue().equals(value)) {
79+
return DISCOURAGED;
80+
}
81+
if (PREFERRED.getValue().equals(value)) {
82+
return PREFERRED;
83+
}
84+
if (REQUIRED.getValue().equals(value)) {
85+
return REQUIRED;
86+
}
87+
return new UserVerificationRequirement(value);
88+
}
89+
6990
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2002-2024 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.core.JsonParser;
20+
import com.fasterxml.jackson.core.JsonToken;
21+
import com.fasterxml.jackson.databind.DeserializationContext;
22+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
23+
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;
24+
import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInput;
25+
26+
import java.io.IOException;
27+
28+
/**
29+
* Jackson deserializer for {@link AuthenticationExtensionsClientInput}
30+
*
31+
* @author Justin Cranford
32+
* @since 6.5
33+
*/
34+
@SuppressWarnings("serial")
35+
class AuthenticationExtensionsClientInputDeserializer extends StdDeserializer<AuthenticationExtensionsClientInput> {
36+
37+
AuthenticationExtensionsClientInputDeserializer() {
38+
super(AuthenticationExtensionsClientInput.class);
39+
}
40+
41+
@Override
42+
public AuthenticationExtensionsClientInput deserialize(JsonParser parser, DeserializationContext ctxt)
43+
throws IOException {
44+
if (parser.nextToken() != JsonToken.END_OBJECT) {
45+
String extensionId = parser.currentName();
46+
Object value = parser.readValueAs(Object.class);
47+
return new ImmutableAuthenticationExtensionsClientInput(extensionId, value);
48+
}
49+
return null;
50+
}
51+
}

Diff for: web/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputMixin.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,19 @@
1616

1717
package org.springframework.security.web.webauthn.jackson;
1818

19+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
1920
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
20-
2121
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
2222

2323
/**
2424
* Jackson mixin for {@link AuthenticationExtensionsClientInputs}
2525
*
2626
* @author Rob Winch
27+
* @author Justin Cranford
2728
* @since 6.4
2829
*/
2930
@JsonSerialize(using = AuthenticationExtensionsClientInputSerializer.class)
31+
@JsonDeserialize(using = AuthenticationExtensionsClientInputDeserializer.class)
3032
class AuthenticationExtensionsClientInputMixin {
3133

3234
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2002-2024 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.core.JsonParser;
20+
import com.fasterxml.jackson.core.JsonToken;
21+
import com.fasterxml.jackson.databind.DeserializationContext;
22+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
23+
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;
24+
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
25+
import org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;
26+
27+
import java.io.IOException;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
31+
/**
32+
* Jackson deserializer for {@link AuthenticationExtensionsClientInputs}
33+
*
34+
* @author Justin Cranford
35+
* @since 6.5
36+
*/
37+
@SuppressWarnings("serial")
38+
class AuthenticationExtensionsClientInputsDeserializer extends StdDeserializer<AuthenticationExtensionsClientInputs> {
39+
40+
AuthenticationExtensionsClientInputsDeserializer() {
41+
super(AuthenticationExtensionsClientInputs.class);
42+
}
43+
44+
@Override
45+
public AuthenticationExtensionsClientInputs deserialize(JsonParser parser, DeserializationContext ctxt)
46+
throws IOException {
47+
final AuthenticationExtensionsClientInputDeserializer authenticationExtensionsClientInputDeserializer = new AuthenticationExtensionsClientInputDeserializer();
48+
49+
final List<AuthenticationExtensionsClientInput> extensions = new ArrayList<>();
50+
while (parser.nextToken() != JsonToken.END_OBJECT) {
51+
extensions.add(authenticationExtensionsClientInputDeserializer.deserialize(parser, ctxt));
52+
}
53+
return new ImmutableAuthenticationExtensionsClientInputs(extensions);
54+
}
55+
}

Diff for: web/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsMixin.java

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.security.web.webauthn.jackson;
1818

19+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
1920
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2021

2122
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
@@ -27,6 +28,7 @@
2728
* @since 6.4
2829
*/
2930
@JsonSerialize(using = AuthenticationExtensionsClientInputsSerializer.class)
31+
@JsonDeserialize(using = AuthenticationExtensionsClientInputsDeserializer.class)
3032
class AuthenticationExtensionsClientInputsMixin {
3133

3234
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2002-2024 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.core.JsonParser;
20+
import com.fasterxml.jackson.core.JsonToken;
21+
import com.fasterxml.jackson.databind.DeserializationContext;
22+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
23+
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
24+
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
25+
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder;
26+
import org.springframework.security.web.webauthn.api.ResidentKeyRequirement;
27+
import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
28+
29+
import java.io.IOException;
30+
31+
/**
32+
* Jackson deserializer for {@link AuthenticatorSelectionCriteria}
33+
*
34+
* @author Justin Cranford
35+
* @since 6.5
36+
*/
37+
@SuppressWarnings("serial")
38+
class AuthenticatorSelectionCriteriaDeserializer extends StdDeserializer<AuthenticatorSelectionCriteria> {
39+
40+
AuthenticatorSelectionCriteriaDeserializer() {
41+
super(AuthenticatorSelectionCriteria.class);
42+
}
43+
44+
@Override
45+
public AuthenticatorSelectionCriteria deserialize(JsonParser parser, DeserializationContext ctxt)
46+
throws IOException {
47+
final AuthenticatorSelectionCriteriaBuilder builder = AuthenticatorSelectionCriteria.builder();
48+
while (parser.nextToken() != JsonToken.END_OBJECT) {
49+
final String fieldName = parser.currentName();
50+
parser.nextToken();
51+
if ("authenticatorAttachment".equals(fieldName)) {
52+
builder.authenticatorAttachment(AuthenticatorAttachment.valueOf(parser.getText()));
53+
} else if ("residentKey".equals(fieldName)) {
54+
builder.residentKey(ResidentKeyRequirement.valueOf(parser.getText()));
55+
} else if ("userVerification".equals(fieldName)) {
56+
builder.userVerification(UserVerificationRequirement.valueOf(parser.getText()));
57+
} else {
58+
throw new IOException("Unsupported field name: " + fieldName);
59+
}
60+
}
61+
return builder.build();
62+
}
63+
64+
}

Diff for: web/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorSelectionCriteriaMixin.java

+5
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@
1818

1919
import com.fasterxml.jackson.annotation.JsonInclude;
2020

21+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
22+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2123
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
2224

2325
/**
2426
* Jackson mixin for {@link AuthenticatorSelectionCriteria}
2527
*
2628
* @author Rob Winch
29+
* @author Justin Cranford
2730
* @since 6.4
2831
*/
32+
@JsonSerialize(using = AuthenticatorSelectionCriteriaSerializer.class)
33+
@JsonDeserialize(using = AuthenticatorSelectionCriteriaDeserializer.class)
2934
@JsonInclude(JsonInclude.Include.NON_NULL)
3035
abstract class AuthenticatorSelectionCriteriaMixin {
3136

0 commit comments

Comments
 (0)