Skip to content

Commit 26991bb

Browse files
committed
AuthenticationRequest uses rawId.getBytes()
Previously id.getBytes() was used which was problemantic because the id is base64 encoded and this did not match the expected ids. Closes gh-18158
1 parent e4106ec commit 26991bb

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

webauthn/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ public PublicKeyCredentialUserEntity authenticate(RelyingPartyAuthenticationRequ
399399
.getUserVerification() == UserVerificationRequirement.REQUIRED;
400400

401401
com.webauthn4j.data.AuthenticationRequest authenticationRequest = new com.webauthn4j.data.AuthenticationRequest(
402-
request.getPublicKey().getId().getBytes(), assertionResponse.getAuthenticatorData().getBytes(),
402+
request.getPublicKey().getRawId().getBytes(), assertionResponse.getAuthenticatorData().getBytes(),
403403
assertionResponse.getClientDataJSON().getBytes(), assertionResponse.getSignature().getBytes());
404404

405405
// CollectedClientData and ExtensionsClientOutputs is registration data, and can

webauthn/src/test/java/org/springframework/security/web/webauthn/management/Webauthn4jRelyingPartyOperationsTests.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,20 @@
2727
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
2828
import com.fasterxml.jackson.databind.node.ObjectNode;
2929
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
30+
import com.webauthn4j.WebAuthnManager;
3031
import com.webauthn4j.converter.AttestationObjectConverter;
3132
import com.webauthn4j.converter.util.ObjectConverter;
33+
import com.webauthn4j.data.AuthenticationData;
34+
import com.webauthn4j.data.AuthenticationRequest;
3235
import com.webauthn4j.data.attestation.AttestationObject;
36+
import com.webauthn4j.data.attestation.authenticator.AttestedCredentialData;
3337
import com.webauthn4j.data.attestation.authenticator.AuthenticatorData;
3438
import com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput;
3539
import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
3640
import org.junit.jupiter.api.BeforeEach;
3741
import org.junit.jupiter.api.Test;
3842
import org.junit.jupiter.api.extension.ExtendWith;
43+
import org.mockito.ArgumentCaptor;
3944
import org.mockito.Mock;
4045
import org.mockito.junit.jupiter.MockitoExtension;
4146

@@ -44,30 +49,36 @@
4449
import org.springframework.security.core.authority.AuthorityUtils;
4550
import org.springframework.security.core.userdetails.PasswordEncodedUser;
4651
import org.springframework.security.core.userdetails.UserDetails;
52+
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
4753
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
4854
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder;
4955
import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;
5056
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
5157
import org.springframework.security.web.webauthn.api.Bytes;
5258
import org.springframework.security.web.webauthn.api.CredentialRecord;
59+
import org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;
5360
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
5461
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
5562
import org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;
5663
import org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters;
5764
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
5865
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;
5966
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
67+
import org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses;
6068
import org.springframework.security.web.webauthn.api.TestAuthenticatorAttestationResponses;
6169
import org.springframework.security.web.webauthn.api.TestCredentialRecords;
6270
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
71+
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;
6372
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;
6473
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentials;
6574
import org.springframework.security.web.webauthn.api.UserVerificationRequirement;
6675

6776
import static org.assertj.core.api.Assertions.assertThat;
6877
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
6978
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
79+
import static org.mockito.ArgumentMatchers.any;
7080
import static org.mockito.BDDMockito.given;
81+
import static org.mockito.Mockito.mock;
7182
import static org.mockito.Mockito.verifyNoInteractions;
7283

7384
@ExtendWith(MockitoExtension.class)
@@ -587,6 +598,50 @@ void createCredentialRequestOptionsWhenAuthenticated() {
587598
.containsExactly(credentialRecord.getCredentialId());
588599
}
589600

601+
// gh-18158
602+
@Test
603+
void authenticateThenWa4jRequestCredentialIdIsRawIdBytes() throws Exception {
604+
PublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions.create().build();
605+
AuthenticatorAssertionResponse response = TestAuthenticationAssertionResponses
606+
.createAuthenticatorAssertionResponse()
607+
.build();
608+
PublicKeyCredential<AuthenticatorAssertionResponse> credentials = TestPublicKeyCredentials
609+
.createPublicKeyCredential(response)
610+
.build();
611+
RelyingPartyAuthenticationRequest request = new RelyingPartyAuthenticationRequest(options, credentials);
612+
PublicKeyCredential<AuthenticatorAssertionResponse> publicKey = request.getPublicKey();
613+
614+
ImmutableCredentialRecord credentialRecord = TestCredentialRecords.fullUserCredential().build();
615+
given(this.userCredentials.findByCredentialId(publicKey.getRawId())).willReturn(credentialRecord);
616+
ObjectMapper json = mock(ObjectMapper.class);
617+
ObjectMapper cbor = mock(ObjectMapper.class);
618+
given(cbor.getFactory()).willReturn(mock(CBORFactory.class));
619+
AttestationObject attestationObject = mock(AttestationObject.class);
620+
AuthenticatorData wa4jAuthData = mock(AuthenticatorData.class);
621+
given(attestationObject.getAuthenticatorData()).willReturn(wa4jAuthData);
622+
given(wa4jAuthData.getAttestedCredentialData()).willReturn(mock(AttestedCredentialData.class));
623+
given(cbor.readValue(credentialRecord.getAttestationObject().getBytes(), AttestationObject.class))
624+
.willReturn(attestationObject);
625+
this.rpOperations.setObjectConverter(new ObjectConverter(json, cbor));
626+
627+
WebAuthnManager manager = mock(WebAuthnManager.class);
628+
ArgumentCaptor<AuthenticationRequest> wa4jRequest = ArgumentCaptor.forClass(AuthenticationRequest.class);
629+
AuthenticationData wa4jData = mock(AuthenticationData.class);
630+
given(wa4jData.getAuthenticatorData()).willReturn(mock(AuthenticatorData.class));
631+
given(manager.verify(wa4jRequest.capture(), any())).willReturn(wa4jData);
632+
given(this.userEntities.findById(any())).willReturn(TestPublicKeyCredentialUserEntities.userEntity().build());
633+
this.rpOperations.setWebAuthnManager(manager);
634+
635+
this.rpOperations.authenticate(request);
636+
637+
// this ensures that our next assertion is valid (we want the rawId bytes, not the
638+
// id bytes to be used)
639+
assertThat(publicKey.getRawId().getBytes()).isNotEqualTo(publicKey.getId().getBytes());
640+
// ensure that the raw id bytes are passed into webauthn4j (not the id bytes which
641+
// are base64 encoded)
642+
assertThat(wa4jRequest.getValue().getCredentialId()).isEqualTo(publicKey.getRawId().getBytes());
643+
}
644+
590645
private static AuthenticatorAttestationResponse setFlag(byte... flags) throws Exception {
591646
AuthenticatorAttestationResponseBuilder authAttResponseBldr = TestAuthenticatorAttestationResponses
592647
.createAuthenticatorAttestationResponse();

0 commit comments

Comments
 (0)