Skip to content

Commit 61d4792

Browse files
Add serialize/deserialize PublicKeyCredentialCreationOptions support with ObjectMapper
gh-16434
1 parent d3332e1 commit 61d4792

24 files changed

+1015
-13
lines changed

web/spring-security-web.gradle

Lines changed: 1 addition & 0 deletions
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'

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,24 @@ public static COSEAlgorithmIdentifier[] values() {
6262
return new COSEAlgorithmIdentifier[] { EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, RS1 };
6363
}
6464

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

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,25 @@ public COSEAlgorithmIdentifier getAlg() {
9696
return this.alg;
9797
}
9898

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

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

Lines changed: 21 additions & 0 deletions
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
}

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

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,41 @@
1717
package org.springframework.security.web.webauthn.jackson;
1818

1919
import java.time.Duration;
20+
import java.util.List;
2021

22+
import com.fasterxml.jackson.annotation.JsonCreator;
2123
import com.fasterxml.jackson.annotation.JsonInclude;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
2225
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2326

27+
import org.springframework.security.web.webauthn.api.AttestationConveyancePreference;
28+
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;
29+
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
30+
import org.springframework.security.web.webauthn.api.Bytes;
2431
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
32+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;
33+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters;
34+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;
35+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
2536

2637
/**
2738
* Jackson mixin for {@link PublicKeyCredentialCreationOptions}
2839
*
2940
* @author Rob Winch
3041
* @since 6.4
3142
*/
32-
@JsonInclude(JsonInclude.Include.NON_NULL)
33-
abstract class PublicKeyCredentialCreationOptionsMixin {
34-
35-
@JsonSerialize(using = DurationSerializer.class)
36-
private Duration timeout;
37-
43+
public abstract class PublicKeyCredentialCreationOptionsMixin {
44+
@JsonCreator
45+
public PublicKeyCredentialCreationOptionsMixin(
46+
@JsonProperty("rp") PublicKeyCredentialRpEntity rp,
47+
@JsonProperty("user") PublicKeyCredentialUserEntity user,
48+
@JsonProperty("challenge") Bytes challenge,
49+
@JsonProperty("pubKeyCredParams") List<PublicKeyCredentialParameters> pubKeyCredParams,
50+
@JsonProperty("timeout") Duration timeout,
51+
@JsonProperty("excludeCredentials") List<PublicKeyCredentialDescriptor> excludeCredentials,
52+
@JsonProperty("authenticatorSelection") AuthenticatorSelectionCriteria authenticatorSelection,
53+
@JsonProperty("attestation") AttestationConveyancePreference attestation,
54+
@JsonProperty("extensions") AuthenticationExtensionsClientInputs extensions
55+
) {
56+
}
3857
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.springframework.security.web.webauthn.jackson;
2+
3+
import com.fasterxml.jackson.core.JsonParser;
4+
import com.fasterxml.jackson.core.JsonToken;
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
7+
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
8+
import org.springframework.security.web.webauthn.api.Bytes;
9+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;
10+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor.PublicKeyCredentialDescriptorBuilder;
11+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
12+
13+
import java.io.IOException;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
17+
public class PublicKeyCredentialDescriptorDeserializer extends StdDeserializer<PublicKeyCredentialDescriptor> {
18+
19+
public PublicKeyCredentialDescriptorDeserializer() {
20+
super(PublicKeyCredentialDescriptor.class);
21+
}
22+
23+
@Override
24+
public PublicKeyCredentialDescriptor deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
25+
final PublicKeyCredentialDescriptorBuilder builder = PublicKeyCredentialDescriptor.builder();
26+
27+
while (p.nextToken() != JsonToken.END_OBJECT) {
28+
final String fieldName = p.currentName();
29+
if ("type".equals(fieldName)) {
30+
p.nextToken();
31+
builder.type(PublicKeyCredentialType.valueOf(p.getText()));
32+
} else if ("id".equals(fieldName)) {
33+
p.nextToken();
34+
builder.id(new Bytes(p.getBinaryValue()));
35+
} else if ("transports".equals(fieldName)) {
36+
p.nextToken();
37+
if (p.currentToken() != JsonToken.START_ARRAY) {
38+
throw new IOException("Expected array for 'transports', but found: " + p.currentToken());
39+
}
40+
final List<AuthenticatorTransport> transports = new ArrayList<>();
41+
while (p.nextToken() != JsonToken.END_ARRAY) {
42+
transports.add(AuthenticatorTransport.valueOf(p.getText()));
43+
}
44+
builder.transports(transports.toArray(new AuthenticatorTransport[0]));
45+
} else {
46+
p.skipChildren();
47+
}
48+
}
49+
50+
return builder.build();
51+
}
52+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.databind.annotation.JsonDeserialize;
20+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
21+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
22+
23+
/**
24+
* Jackson mixin for {@link PublicKeyCredentialType}
25+
*
26+
* @author Rob Winch
27+
* @since 6.4
28+
*/
29+
@JsonSerialize(using = PublicKeyCredentialDescriptorSerializer.class)
30+
@JsonDeserialize(using = PublicKeyCredentialDescriptorDeserializer.class)
31+
abstract class PublicKeyCredentialDescriptorMixin {
32+
33+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.springframework.security.web.webauthn.jackson;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.databind.SerializerProvider;
5+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
6+
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
7+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;
8+
9+
import java.io.IOException;
10+
11+
public class PublicKeyCredentialDescriptorSerializer extends StdSerializer<PublicKeyCredentialDescriptor> {
12+
13+
public PublicKeyCredentialDescriptorSerializer() {
14+
super(PublicKeyCredentialDescriptor.class);
15+
}
16+
17+
@Override
18+
public void serialize(PublicKeyCredentialDescriptor value, JsonGenerator gen, SerializerProvider provider) throws IOException {
19+
gen.writeStartObject();
20+
gen.writeFieldName("type");
21+
gen.writeString(value.getType().getValue());
22+
gen.writeFieldName("id");
23+
gen.writeBinary(value.getId().getBytes());
24+
gen.writeFieldName("transports");
25+
gen.writeStartArray();
26+
for (final AuthenticatorTransport transport : value.getTransports()) {
27+
gen.writeString(transport.getValue());
28+
}
29+
gen.writeEndArray();
30+
gen.writeEndObject();
31+
}
32+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.springframework.security.web.webauthn.jackson;
2+
3+
import com.fasterxml.jackson.core.JsonParser;
4+
import com.fasterxml.jackson.core.JsonToken;
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
7+
import org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;
8+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters;
9+
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
10+
11+
import java.io.IOException;
12+
13+
public class PublicKeyCredentialParametersDeserializer extends StdDeserializer<PublicKeyCredentialParameters> {
14+
15+
public PublicKeyCredentialParametersDeserializer() {
16+
super(PublicKeyCredentialParameters.class);
17+
}
18+
19+
@Override
20+
public PublicKeyCredentialParameters deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
21+
PublicKeyCredentialType type = null;
22+
COSEAlgorithmIdentifier alg = null;
23+
while (p.nextToken() != JsonToken.END_OBJECT) {
24+
final String fieldName = p.currentName();
25+
if ("type".equals(fieldName)) {
26+
p.nextToken();
27+
type = PublicKeyCredentialType.valueOf(p.getText());
28+
} else if ("alg".equals(fieldName)) {
29+
p.nextToken();
30+
alg = COSEAlgorithmIdentifier.valueOf(p.getLongValue());
31+
} else {
32+
p.skipChildren();
33+
}
34+
}
35+
36+
return PublicKeyCredentialParameters.valueOf(type, alg);
37+
}
38+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.springframework.security.web.webauthn.jackson;
2+
3+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
4+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
5+
6+
@JsonSerialize(using = PublicKeyCredentialParametersSerializer.class)
7+
@JsonDeserialize(using = PublicKeyCredentialParametersDeserializer.class)
8+
public abstract class PublicKeyCredentialParametersMixin {
9+
}

0 commit comments

Comments
 (0)