浏览代码

webauthn registerCredential returns transports

The webauthn support previously did not pass the transports to webauthn4j.
This meant that the result of
Webauthn4jRelyingPartyOperations.registerCredential did not have any
transports either.

This commit ensures that the transports are passed to the webauth4j lib
and then returned in the result of registerCredential.

Closes gh-16084
Rob Winch 8 月之前
父节点
当前提交
9c3b11914d

+ 15 - 1
web/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java

@@ -20,6 +20,7 @@ import java.time.Duration;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -254,7 +255,9 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
 		boolean userPresenceRequired = true;
 		List<com.webauthn4j.data.PublicKeyCredentialParameters> pubKeyCredParams = convertCredentialParamsToWebauthn4j(
 				creationOptions.getPubKeyCredParams());
-		RegistrationRequest webauthn4jRegistrationRequest = new RegistrationRequest(attestationObject, clientDataJSON);
+		Set<String> transports = convertTransportsToString(response);
+		RegistrationRequest webauthn4jRegistrationRequest = new RegistrationRequest(attestationObject, clientDataJSON,
+				transports);
 		RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, pubKeyCredParams,
 				userVerificationRequired, userPresenceRequired);
 		RegistrationData registrationData = this.webAuthnManager.validate(webauthn4jRegistrationRequest,
@@ -283,6 +286,17 @@ public class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOpe
 		return userCredential;
 	}
 
+	private static Set<String> convertTransportsToString(AuthenticatorAttestationResponse response) {
+		if (response.getTransports() == null) {
+			return null;
+		}
+		Set<String> transports = new HashSet<>(response.getTransports().size());
+		for (AuthenticatorTransport transport : response.getTransports()) {
+			transports.add(transport.getValue());
+		}
+		return transports;
+	}
+
 	private List<com.webauthn4j.data.PublicKeyCredentialParameters> convertCredentialParamsToWebauthn4j(
 			List<PublicKeyCredentialParameters> parameters) {
 		return parameters.stream().map(this::convertParamToWebauthn4j).collect(Collectors.toUnmodifiableList());

+ 42 - 0
web/src/test/java/org/springframework/security/web/webauthn/management/Webauthn4jRelyingPartyOperationsTests.java

@@ -45,6 +45,7 @@ import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
 import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder;
 import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
+import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
 import org.springframework.security.web.webauthn.api.Bytes;
 import org.springframework.security.web.webauthn.api.CredentialRecord;
 import org.springframework.security.web.webauthn.api.PublicKeyCredential;
@@ -224,6 +225,47 @@ class Webauthn4jRelyingPartyOperationsTests {
 		assertThatIllegalArgumentException().isThrownBy(() -> this.rpOperations.registerCredential(null));
 	}
 
+	@Test
+	void registerCredentialWhenDefaultTransportsThenSuccess() {
+		PublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions
+			.createPublicKeyCredentialCreationOptions()
+			.build();
+		PublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = TestPublicKeyCredential
+			.createPublicKeyCredential()
+			.build();
+		RelyingPartyPublicKey rpPublicKey = new RelyingPartyPublicKey(publicKeyCredential, this.label);
+
+		ImmutableRelyingPartyRegistrationRequest rpRegistrationRequest = new ImmutableRelyingPartyRegistrationRequest(
+				creationOptions, rpPublicKey);
+		CredentialRecord credentialRecord = this.rpOperations.registerCredential(rpRegistrationRequest);
+		assertThat(credentialRecord).isNotNull();
+		assertThat(credentialRecord.getCredentialId()).isNotNull();
+		assertThat(credentialRecord.getTransports()).containsExactlyInAnyOrder(AuthenticatorTransport.INTERNAL,
+				AuthenticatorTransport.HYBRID);
+	}
+
+	@Test
+	void registerCredentialWhenInternalTransportThenCredentialRecordHasTransport() {
+		PublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions
+			.createPublicKeyCredentialCreationOptions()
+			.build();
+		AuthenticatorAttestationResponse response = TestAuthenticatorAttestationResponse
+			.createAuthenticatorAttestationResponse()
+			.transports(AuthenticatorTransport.INTERNAL)
+			.build();
+		PublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = TestPublicKeyCredential
+			.createPublicKeyCredential()
+			.response(response)
+			.build();
+		RelyingPartyPublicKey rpPublicKey = new RelyingPartyPublicKey(publicKeyCredential, this.label);
+
+		ImmutableRelyingPartyRegistrationRequest rpRegistrationRequest = new ImmutableRelyingPartyRegistrationRequest(
+				creationOptions, rpPublicKey);
+		CredentialRecord credentialRecord = this.rpOperations.registerCredential(rpRegistrationRequest);
+		assertThat(credentialRecord).isNotNull();
+		assertThat(credentialRecord.getTransports()).containsExactlyInAnyOrder(AuthenticatorTransport.INTERNAL);
+	}
+
 	@Test
 	void registerCredentialWhenExistsThenException() {
 		PublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions