|
27 | 27 | import com.fasterxml.jackson.databind.node.JsonNodeFactory; |
28 | 28 | import com.fasterxml.jackson.databind.node.ObjectNode; |
29 | 29 | import com.fasterxml.jackson.dataformat.cbor.CBORFactory; |
| 30 | +import com.webauthn4j.WebAuthnManager; |
30 | 31 | import com.webauthn4j.converter.AttestationObjectConverter; |
31 | 32 | import com.webauthn4j.converter.util.ObjectConverter; |
| 33 | +import com.webauthn4j.data.AuthenticationData; |
| 34 | +import com.webauthn4j.data.AuthenticationRequest; |
32 | 35 | import com.webauthn4j.data.attestation.AttestationObject; |
| 36 | +import com.webauthn4j.data.attestation.authenticator.AttestedCredentialData; |
33 | 37 | import com.webauthn4j.data.attestation.authenticator.AuthenticatorData; |
34 | 38 | import com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput; |
35 | 39 | import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; |
36 | 40 | import org.junit.jupiter.api.BeforeEach; |
37 | 41 | import org.junit.jupiter.api.Test; |
38 | 42 | import org.junit.jupiter.api.extension.ExtendWith; |
| 43 | +import org.mockito.ArgumentCaptor; |
39 | 44 | import org.mockito.Mock; |
40 | 45 | import org.mockito.junit.jupiter.MockitoExtension; |
41 | 46 |
|
|
44 | 49 | import org.springframework.security.core.authority.AuthorityUtils; |
45 | 50 | import org.springframework.security.core.userdetails.PasswordEncodedUser; |
46 | 51 | import org.springframework.security.core.userdetails.UserDetails; |
| 52 | +import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse; |
47 | 53 | import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse; |
48 | 54 | import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder; |
49 | 55 | import org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria; |
50 | 56 | import org.springframework.security.web.webauthn.api.AuthenticatorTransport; |
51 | 57 | import org.springframework.security.web.webauthn.api.Bytes; |
52 | 58 | import org.springframework.security.web.webauthn.api.CredentialRecord; |
| 59 | +import org.springframework.security.web.webauthn.api.ImmutableCredentialRecord; |
53 | 60 | import org.springframework.security.web.webauthn.api.PublicKeyCredential; |
54 | 61 | import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions; |
55 | 62 | import org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor; |
56 | 63 | import org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters; |
57 | 64 | import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions; |
58 | 65 | import org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity; |
59 | 66 | import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity; |
| 67 | +import org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses; |
60 | 68 | import org.springframework.security.web.webauthn.api.TestAuthenticatorAttestationResponses; |
61 | 69 | import org.springframework.security.web.webauthn.api.TestCredentialRecords; |
62 | 70 | import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions; |
| 71 | +import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions; |
63 | 72 | import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities; |
64 | 73 | import org.springframework.security.web.webauthn.api.TestPublicKeyCredentials; |
65 | 74 | import org.springframework.security.web.webauthn.api.UserVerificationRequirement; |
66 | 75 |
|
67 | 76 | import static org.assertj.core.api.Assertions.assertThat; |
68 | 77 | import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
69 | 78 | import static org.assertj.core.api.Assertions.assertThatRuntimeException; |
| 79 | +import static org.mockito.ArgumentMatchers.any; |
70 | 80 | import static org.mockito.BDDMockito.given; |
| 81 | +import static org.mockito.Mockito.mock; |
71 | 82 | import static org.mockito.Mockito.verifyNoInteractions; |
72 | 83 |
|
73 | 84 | @ExtendWith(MockitoExtension.class) |
@@ -587,6 +598,50 @@ void createCredentialRequestOptionsWhenAuthenticated() { |
587 | 598 | .containsExactly(credentialRecord.getCredentialId()); |
588 | 599 | } |
589 | 600 |
|
| 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 | + |
590 | 645 | private static AuthenticatorAttestationResponse setFlag(byte... flags) throws Exception { |
591 | 646 | AuthenticatorAttestationResponseBuilder authAttResponseBldr = TestAuthenticatorAttestationResponses |
592 | 647 | .createAuthenticatorAttestationResponse(); |
|
0 commit comments