diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 000000000..430fcaae1 --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -0,0 +1,52 @@ +# This name is shown in the status badge in the README +name: integration-test + +on: + push: + branches: + - main + - 'release-*' + schedule: + # Run once a week to check compatibility with new FIDO MDS blob contents + - cron: '0 0 * * 1' + +jobs: + test: + name: JDK ${{ matrix.java }} ${{ matrix.distribution }} + + runs-on: ubuntu-latest + strategy: + matrix: + java: [17] + distribution: [temurin] + + outputs: + report-java: 17 + report-dist: temurin + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + distribution: ${{ matrix.distribution }} + + - name: Run integration tests + run: ./gradlew integrationTest + + - name: Archive HTML test report + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: test-reports-java${{ matrix.java }}-${{ matrix.distribution }}-html + path: "*/build/reports/**" + + - name: Archive JUnit test report + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: test-reports-java${{ matrix.java }}-${{ matrix.distribution }}-xml + path: "*/build/test-results/**/*.xml" diff --git a/NEWS b/NEWS index f502f25ba..61ff613e2 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,13 @@ +== Version 2.5.3 == + +`webauthn-server-attestation`: + +Fixes: + +* `FidoMetadataDownloader` no longer rejects FIDO MDS metadata BLOBs with + unknown properties. + + == Version 2.5.2 == Fixes: diff --git a/README b/README index 176303870..628701ed1 100644 --- a/README +++ b/README @@ -64,7 +64,7 @@ Maven: com.yubico webauthn-server-core - 2.5.2 + 2.5.3 compile ---------- @@ -72,7 +72,7 @@ Maven: Gradle: ---------- -implementation("com.yubico:webauthn-server-core:2.5.2") +implementation("com.yubico:webauthn-server-core:2.5.3") ---------- NOTE: You may need additional dependencies with JCA providers to support some signature algorithms. @@ -85,7 +85,7 @@ The library will log warnings if you try to configure it for algorithms with no This library uses link:https://semver.org/[semantic versioning]. The public API consists of all public classes, methods and fields in the `com.yubico.webauthn` package and its subpackages, i.e., everything covered by the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/package-summary.html[Javadoc], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/package-summary.html[Javadoc], *with the exception* of features annotated with a `@Deprecated` annotation and a `@deprecated EXPERIMENTAL:` tag in JavaDoc. Such features are considered unstable and may receive breaking changes without a @@ -108,7 +108,7 @@ In addition to the main `webauthn-server-core` module, there is also: == Documentation See the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/package-summary.html[Javadoc] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/package-summary.html[Javadoc] for in-depth API documentation. @@ -118,20 +118,20 @@ Using this library comes in two parts: the server side and the client side. The server side involves: 1. Implement the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] interface with your database access logic. 2. Instantiate the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class. 3. Use the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`] and - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] methods to perform registration ceremonies. 4. Use the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`] and - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] methods to perform authentication ceremonies. 5. Optionally use additional features: passkeys, passwordless multi-factor authentication, credential backup state. @@ -151,7 +151,7 @@ link:webauthn-server-demo[`webauthn-server-demo`] for a complete demo server. === 1. Implement a `CredentialRepository` The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] interface abstracts your database in a database-agnostic way. The concrete implementation will be different for every project, but you can use link:https://github.com/Yubico/java-webauthn-server/blob/main/webauthn-server-demo/src/main/java/demo/webauthn/InMemoryRegistrationStorage.java[`InMemoryRegistrationStorage`] @@ -160,11 +160,11 @@ as a simple example. === 2. Instantiate a `RelyingParty` The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class is the main entry point to the library. You can instantiate it using its builder methods, passing in your -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] implementation (called `MyCredentialRepository` here) as an argument: [source,java] @@ -186,7 +186,7 @@ RelyingParty rp = RelyingParty.builder() A registration ceremony consists of 5 main steps: 1. Generate registration parameters using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`]. 2. Send registration parameters to the client and call https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create[`navigator.credentials.create()`]. 3. With `cred` as the result of the successfully resolved promise, @@ -194,7 +194,7 @@ A registration ceremony consists of 5 main steps: and https://www.w3.org/TR/webauthn-2/#ref-for-dom-authenticatorattestationresponse-gettransports[`cred.response.getTransports()`] and return their results along with `cred` to the server. 4. Validate the response using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`]. 5. Update your database using the `finishRegistration` output. This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call. @@ -226,15 +226,15 @@ return credentialCreateJson; // Send to client ---------- You will need to keep this -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html[`PublicKeyCredentialCreationOptions`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html[`PublicKeyCredentialCreationOptions`] object in temporary storage so you can also pass it into -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] later. If needed, you can use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#toJson()[`toJson()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#toJson()[`toJson()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#fromJson(java.lang.String)[`fromJson(String)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#fromJson(java.lang.String)[`fromJson(String)`] methods to serialize and deserialize the value for storage. Now call the WebAuthn API on the client side: @@ -295,7 +295,7 @@ storeCredential( // Some database access method of your own design Like registration ceremonies, an authentication ceremony consists of 5 main steps: 1. Generate authentication parameters using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`]. 2. Send authentication parameters to the client, call https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get[`navigator.credentials.get()`] and return the response. @@ -303,7 +303,7 @@ Like registration ceremonies, an authentication ceremony consists of 5 main step https://www.w3.org/TR/webauthn-2/#ref-for-dom-publickeycredential-getclientextensionresults[`cred.getClientExtensionResults()`] and return the result along with `cred` to the server. 4. Validate the response using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`]. 5. Update your database using the `finishAssertion` output, and act upon the result (for example, grant login access). This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call. @@ -320,15 +320,15 @@ return credentialGetJson; // Send to client ---------- Again, you will need to keep this -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/AssertionRequest.html[`AssertionRequest`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/AssertionRequest.html[`AssertionRequest`] object in temporary storage so you can also pass it into -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] later. If needed, you can use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/AssertionRequest.html#toJson()[`toJson()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/AssertionRequest.html#toJson()[`toJson()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/AssertionRequest.html#fromJson(java.lang.String)[`fromJson(String)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/AssertionRequest.html#fromJson(java.lang.String)[`fromJson(String)`] methods to serialize and deserialize the value for storage. Now call the WebAuthn API on the client side: @@ -369,7 +369,7 @@ throw new RuntimeException("Authentication failed"); ---------- Finally, if the previous step was successful, update your database using the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. Most importantly, you should update the signature counter. That might look something like this: [source,java] @@ -420,9 +420,9 @@ Many passkey-capable authenticators also offer a credential sync mechanism to allow one passkey to be used on multiple devices. Passkeys can be created by setting the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/StartRegistrationOptions.StartRegistrationOptionsBuilder.html#authenticatorSelection(com.yubico.webauthn.data.AuthenticatorSelectionCriteria)[`authenticatorSelection`].link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/StartRegistrationOptions.StartRegistrationOptionsBuilder.html#authenticatorSelection(com.yubico.webauthn.data.AuthenticatorSelectionCriteria)[`authenticatorSelection`].link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] option to -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/ResidentKeyRequirement.html#REQUIRED[`REQUIRED`]: +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/ResidentKeyRequirement.html#REQUIRED[`REQUIRED`]: [source,java] ---------- @@ -444,15 +444,15 @@ AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder().bui Some authenticators might create passkeys even if not required, and setting the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] option to -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/ResidentKeyRequirement.html#PREFERRED[`PREFERRED`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/ResidentKeyRequirement.html#PREFERRED[`PREFERRED`] will create a passkey if the authenticator supports it. The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RegistrationResult.html#isDiscoverable()[`RegistrationResult.isDiscoverable()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RegistrationResult.html#isDiscoverable()[`RegistrationResult.isDiscoverable()`] method can be used to determine whether the created credential is a passkey. This requires the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/RegistrationExtensionInputs.RegistrationExtensionInputsBuilder.html#credProps()[`credProps` extension] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/RegistrationExtensionInputs.RegistrationExtensionInputsBuilder.html#credProps()[`credProps` extension] to be enabled, which it is by default. @@ -471,7 +471,7 @@ AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder() ---------- Then -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] will enforce that user verification was performed. However, there is no guarantee that the user's authenticator will support this unless the user has some credential created with the @@ -508,14 +508,14 @@ AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder() ---------- In this case -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] will NOT enforce user verification, but instead the `isUserVerified()` method of -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/AssertionResult.html[`AssertionResult`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/AssertionResult.html[`AssertionResult`] will tell whether user verification was used. For example, you could prompt for a password as the second factor if `isUserVerified()` returns `false`: @@ -555,7 +555,7 @@ In particular you need to: - Add the credential request option `mediation: "conditional"` alongside the `publicKey` option generated by -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`], - Add `autocomplete="username webauthn"` to a username input field on the page, and - Call `navigator.credentials.get()` in the background. @@ -574,9 +574,9 @@ Some authenticators may allow credentials to be backed up and/or synced between This capability and its current state is signaled via the link:https://w3c.github.io/webauthn/#sctn-credential-backup[Credential Backup State] flags, which are available via the `isBackedUp()` and `isBackupEligible()` methods of -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. These can be used as a hint about how vulnerable a user is to authenticator loss. In particular, a user with only one credential which is not backed up may risk getting locked out if they lose their authenticator. @@ -600,14 +600,14 @@ To migrate to using the WebAuthn API, you need to do the following: 1. Follow the link:#getting-started[Getting started] guide above to set up WebAuthn support in general. + -Note that unlike a U2F AppID, the WebAuthn link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/RelyingPartyIdentity.RelyingPartyIdentityBuilder.html#id(java.lang.String)[RP ID] +Note that unlike a U2F AppID, the WebAuthn link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/RelyingPartyIdentity.RelyingPartyIdentityBuilder.html#id(java.lang.String)[RP ID] consists of only the domain name of the AppID. WebAuthn does not support link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-appid-and-facets-v1.2-ps-20170411.html[U2F Trusted Facet Lists]. 2. Set the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#appId(com.yubico.webauthn.extension.appid.AppId)[`appId()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#appId(com.yubico.webauthn.extension.appid.AppId)[`appId()`] setting on your - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] instance. The argument to the `appid()` setting should be the same as you used for the `appId` argument to the link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[U2F `register` and `sign` functions]. @@ -625,22 +625,22 @@ extensions and configure the `RelyingParty` to accept the given AppId when verif privacy consideration. 4. When your - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] creates a - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RegisteredCredential.html[`RegisteredCredential`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RegisteredCredential.html[`RegisteredCredential`] for a U2F credential, use the U2F key handle as the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#credentialId(com.yubico.webauthn.data.ByteArray)[credential ID]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#credentialId(com.yubico.webauthn.data.ByteArray)[credential ID]. If you store key handles base64 encoded, you should decode them using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/ByteArray.html#fromBase64(java.lang.String)[`ByteArray.fromBase64`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/ByteArray.html#fromBase64(java.lang.String)[`ByteArray.fromBase64`] or - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/data/ByteArray.html#fromBase64Url(java.lang.String)[`ByteArray.fromBase64Url`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/data/ByteArray.html#fromBase64Url(java.lang.String)[`ByteArray.fromBase64Url`] as appropriate before passing them to the `RegisteredCredential`. 5. When your `CredentialRepository` creates a `RegisteredCredential` for a U2F credential, use the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyEs256Raw(com.yubico.webauthn.data.ByteArray)[`publicKeyEs256Raw()`] - method instead of link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyCose(com.yubico.webauthn.data.ByteArray)[`publicKeyCose()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyEs256Raw(com.yubico.webauthn.data.ByteArray)[`publicKeyEs256Raw()`] + method instead of link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyCose(com.yubico.webauthn.data.ByteArray)[`publicKeyCose()`] to set the credential public key. 6. Replace calls to the U2F @@ -774,17 +774,17 @@ provides optional additional features for working with attestation. See the module documentation for more details. Alternatively, you can use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] interface to implement your own source of attestation root certificates and set it as the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] for your -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] instance. Note that depending on your JCA provider configuration, you may need to set the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#enableRevocationChecking(boolean)[`enableRevocationChecking`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#enableRevocationChecking(boolean)[`enableRevocationChecking`] and/or -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#policyTreeValidator(java.util.function.Predicate)[`policyTreeValidator`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#policyTreeValidator(java.util.function.Predicate)[`policyTreeValidator`] settings for compatibility with some authenticators' attestation certificates. See the JavaDoc for these settings for more information. diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 8e2ebbf9b..2efaa001c 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,12 +10,12 @@ repositories { } dependencies { - implementation("info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.9.11") + implementation("info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.15.0") implementation("io.franzbecker:gradle-lombok:5.0.0") // Spotless dropped Java 8 support in version 2.33.0 if (JavaVersion.current().isJava11Compatible) { - implementation("com.diffplug.spotless:spotless-plugin-gradle:6.19.0") - implementation("io.github.cosmicsilence:gradle-scalafix:0.1.14") + implementation("com.diffplug.spotless:spotless-plugin-gradle:6.25.0") + implementation("io.github.cosmicsilence:gradle-scalafix:0.2.2") } } diff --git a/buildSrc/src/main/groovy/project-convention-code-formatting-internal.gradle b/buildSrc/src/main/groovy/project-convention-code-formatting-internal.gradle index 29e7800f9..d468bf0c7 100644 --- a/buildSrc/src/main/groovy/project-convention-code-formatting-internal.gradle +++ b/buildSrc/src/main/groovy/project-convention-code-formatting-internal.gradle @@ -13,9 +13,10 @@ spotless { scalafix { configFile.set(project.rootProject.file("scalafix.conf")) - // Work around dependency resolution issues in April 2022 - semanticdb.autoConfigure.set(true) - semanticdb.version.set("4.5.5") + if (project.name != "yubico-util-scala") { + // yubico-util-scala is the only subproject with Scala sources in the "main" source set + ignoreSourceSets.add("main") + } } project.dependencies.scalafix("com.github.liancheng:organize-imports_2.13:0.6.0") diff --git a/buildSrc/src/main/kotlin/project-convention-pitest.gradle.kts b/buildSrc/src/main/kotlin/project-convention-pitest.gradle.kts index 4bd829dfb..0bd7a5104 100644 --- a/buildSrc/src/main/kotlin/project-convention-pitest.gradle.kts +++ b/buildSrc/src/main/kotlin/project-convention-pitest.gradle.kts @@ -4,7 +4,7 @@ plugins { } pitest { - pitestVersion.set("1.9.5") + pitestVersion.set("1.15.0") timestampedReports.set(false) outputFormats.set(listOf("XML", "HTML")) diff --git a/test-platform/build.gradle.kts b/test-platform/build.gradle.kts index c3ee96679..763737ab2 100644 --- a/test-platform/build.gradle.kts +++ b/test-platform/build.gradle.kts @@ -9,12 +9,12 @@ dependencies { api("junit:junit:4.13.2") api("org.bouncycastle:bcpkix-jdk18on:[1.62,2)") api("org.bouncycastle:bcprov-jdk18on:[1.62,2)") - api("org.mockito:mockito-core:4.7.0") - api("org.scalacheck:scalacheck_2.13:1.16.0") - api("org.scalatest:scalatest_2.13:3.2.13") - api("org.scalatestplus:junit-4-13_2.13:3.2.13.0") - api("org.scalatestplus:scalacheck-1-16_2.13:3.2.13.0") - api("org.slf4j:slf4j-nop:2.0.3") + api("org.mockito:mockito-core:4.11.0") + api("org.scalacheck:scalacheck_2.13:1.18.0") + api("org.scalatest:scalatest_2.13:3.2.18") + api("org.scalatestplus:junit-4-13_2.13:3.2.18.0") + api("org.scalatestplus:scalacheck-1-16_2.13:3.2.14.0") + api("org.slf4j:slf4j-nop:2.0.13") api("uk.org.lidalia:slf4j-test:1.2.0") } } diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java index 15fc7ae08..cf3dfd5cd 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java @@ -25,7 +25,6 @@ package com.yubico.fido.metadata; import com.fasterxml.jackson.core.Base64Variants; -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.yubico.fido.metadata.FidoMetadataDownloaderException.Reason; import com.yubico.internal.util.BinaryUtil; @@ -1172,9 +1171,7 @@ private static ParseResult parseBlob(ByteArray jwt) throws IOException, Base64Ur final ByteArray jwtSignature = ByteArray.fromBase64Url(s.next()); final ObjectMapper headerJsonMapper = - com.yubico.internal.util.JacksonCodecs.json() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) - .setBase64Variant(Base64Variants.MIME_NO_LINEFEEDS); + JacksonCodecs.json().setBase64Variant(Base64Variants.MIME_NO_LINEFEEDS); return new ParseResult( new MetadataBLOB( diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/JacksonCodecs.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/JacksonCodecs.java index 2d16b1820..0fd5d84b6 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/JacksonCodecs.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/JacksonCodecs.java @@ -5,8 +5,13 @@ class JacksonCodecs { - static ObjectMapper jsonWithDefaultEnums() { + static ObjectMapper json() { return com.yubico.internal.util.JacksonCodecs.json() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + static ObjectMapper jsonWithDefaultEnums() { + return json() .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true); } } diff --git a/webauthn-server-demo/README b/webauthn-server-demo/README index 6f667388b..7b8714411 100644 --- a/webauthn-server-demo/README +++ b/webauthn-server-demo/README @@ -44,7 +44,7 @@ layer. This layer manages the general architecture of the system, and is where most business logic and integration code would go. The demo server implements the "persistent" storage of users and credential registrations - the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] integration point - as the link:src/main/java/demo/webauthn/InMemoryRegistrationStorage.java[`InMemoryRegistrationStorage`] class, which simply keeps them stored in memory for a limited time. The @@ -58,7 +58,7 @@ would be specific to a particular Relying Party (RP) would go in this layer. - The server layer in turn calls the *library layer*, which is where the link:../webauthn-server-core/[`webauthn-server-core`] library gets involved. The entry point into the library is the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class. + This layer implements the Web Authentication @@ -69,11 +69,11 @@ and exposes integration points for storage of challenges and credentials. Some notable integration points are: + ** The library user must provide an implementation of the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] interface to use for looking up stored public keys, user handles and signature counters. ** The library user can optionally provide an instance of the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.2/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.3/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] interface to enable identification and validation of authenticator models. This instance is then used to look up trusted attestation root certificates. The link:../webauthn-server-attestation/[`webauthn-server-attestation`] @@ -158,7 +158,7 @@ correct environment. Authentication demo'` - `YUBICO_WEBAUTHN_USE_FIDO_MDS`: If set to `true` (case-insensitive), use - https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.2/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.3/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] from the link:../webauthn-server-attestation[`webauthn-server-attestation`] module as a source of attestation data in addition to the static JSON file bundled with the demo. This will write cache files to the