Skip to content

Commit

Permalink
webauthn registerCredential returns transports
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rwinch committed Dec 4, 2024
1 parent cc2506b commit 9c3b119
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand Down Expand Up @@ -254,7 +255,9 @@ public CredentialRecord registerCredential(RelyingPartyRegistrationRequest rpReg
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,
Expand Down Expand Up @@ -283,6 +286,17 @@ public CredentialRecord registerCredential(RelyingPartyRegistrationRequest rpReg
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
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;
Expand Down Expand Up @@ -224,6 +225,47 @@ void registerCredentialWhenRpRegistrationRequestNullThenIllegalArgumentException
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
Expand Down

0 comments on commit 9c3b119

Please sign in to comment.