| 
					
				 | 
			
			
				@@ -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<AuthenticatorAttestationResponse> publicKeyCredential = this.mapper.readValue( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				PublicKeyCredentialJson.PUBLIC_KEY_JSON, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				new TypeReference<PublicKeyCredential<AuthenticatorAttestationResponse>>() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				new CredentialPropertiesOutput(false)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		PublicKeyCredential<AuthenticatorAttestationResponse> 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<AuthenticatorAssertionResponse> publicKeyCredential = this.mapper.readValue(json, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				new TypeReference<PublicKeyCredential<AuthenticatorAssertionResponse>>() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		PublicKeyCredential<AuthenticatorAssertionResponse> 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; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |