diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae491df61..4a70e0e8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,9 @@ on: pull_request: branches-ignore: - 'tmp**' + schedule: + # Run once a week to check compatibility with new versions of dependencies + - cron: '0 0 * * 1' jobs: test: @@ -32,14 +35,26 @@ jobs: - name: Check out code uses: actions/checkout@v3 - - name: Set up JDK + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: ${{ matrix.distribution }} + + - name: Compile libraries and tests + run: ./gradlew clean testClasses + + - name: Build archives + run: ./gradlew assemble + + - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v3 with: java-version: ${{ matrix.java }} distribution: ${{ matrix.distribution }} - - name: Run tests - run: ./gradlew cleanTest test + - name: Run tests against JDK17-compiled code + run: ./gradlew test --exclude-task compileJava - name: Archive HTML test report if: ${{ always() }} @@ -55,8 +70,12 @@ jobs: name: test-reports-java${{ matrix.java }}-${{ matrix.distribution }}-xml path: "*/build/test-results/**/*.xml" - - name: Build JavaDoc - run: ./gradlew assembleJavadoc + - name: Check binary reproducibility + run: | + ./gradlew clean primaryPublishJar + find . -name '*.jar' | xargs sha256sum | tee checksums.sha256sum + ./gradlew clean primaryPublishJar && sha256sum -c checksums.sha256sum + ./gradlew clean primaryPublishJar && sha256sum -c checksums.sha256sum publish-test-results: name: Publish test results diff --git a/.github/workflows/release-verify-signatures.yml b/.github/workflows/release-verify-signatures.yml index f4d36bde3..0dbc92fe3 100644 --- a/.github/workflows/release-verify-signatures.yml +++ b/.github/workflows/release-verify-signatures.yml @@ -59,6 +59,14 @@ jobs: java --version ./gradlew jar + - name: Print checksums + run: | + for sumprog in md5sum sha1sum sha256sum; do + echo $sumprog + $sumprog webauthn-server-attestation/build/libs/webauthn-server-attestation-${{ github.ref_name }}.jar + $sumprog webauthn-server-core/build/libs/webauthn-server-core-${{ github.ref_name }}.jar + done + - name: Retrieve keyring and signatures uses: actions/download-artifact@v3 with: diff --git a/NEWS b/NEWS index 874fefb0f..581fc2c80 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,15 @@ +== Version 2.4.1 == + +Changes: + +* Added explicit version constraint on `jackson-bom`. + +Fixes: + +* Fixed incompatibility with Jackson version 2.15.0-rc1 and later. +* Fixed linking issue when running in Java 8. + + == Version 2.4.0 == `webauthn-server-core`: diff --git a/README b/README index 7f3c4d4d0..66ec489e6 100644 --- a/README +++ b/README @@ -64,7 +64,7 @@ Maven: com.yubico webauthn-server-core - 2.4.0 + 2.4.1 compile ---------- @@ -72,7 +72,7 @@ Maven: Gradle: ---------- -compile 'com.yubico:webauthn-server-core:2.4.0' +compile 'com.yubico:webauthn-server-core:2.4.1' ---------- 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.4.0/com/yubico/webauthn/package-summary.html[Javadoc], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/com/yubico/webauthn/package-summary.html[Javadoc] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class. 3. Use the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/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.4.1/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.4.0/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.4.1/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.4.0/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.4.1/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.4.0/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.4.1/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] methods to perform authentication ceremonies. 5. Use the outputs of `finishRegistration` and `finishAssertion` to update your database, initiate sessions, etc. @@ -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.4.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/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.4.1/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.4.0/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.4.1/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.4.0/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html[`PublicKeyCredentialCreationOptions`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/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.4.1/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.4.0/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#toJson()[`toJson()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#toJson()[`toJson()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/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.4.1/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: @@ -292,7 +292,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.4.0/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.4.1/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. @@ -300,7 +300,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.4.0/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.4.1/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. @@ -317,15 +317,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.4.0/com/yubico/webauthn/AssertionRequest.html[`AssertionRequest`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/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.4.1/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.4.0/com/yubico/webauthn/AssertionRequest.html#toJson()[`toJson()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/AssertionRequest.html#toJson()[`toJson()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/AssertionRequest.html#fromJson(java.lang.String)[`fromJson(String)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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: @@ -366,7 +366,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.4.0/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. Most importantly, you should update the signature counter. That might look something like this: [source,java] @@ -430,7 +430,7 @@ AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder() ---------- Then -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/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.4.1/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 @@ -467,14 +467,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.4.0/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.4.1/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.4.0/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.4.1/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.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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]. @@ -492,22 +492,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.4.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] creates a - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RegisteredCredential.html[`RegisteredCredential`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/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.4.1/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.4.0/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.4.1/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.4.0/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.4.1/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.4.0/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.4.0/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.4.1/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.4.1/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 @@ -641,17 +641,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.4.0/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/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.4.1/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.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#enableRevocationChecking(boolean)[`enableRevocationChecking`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/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.4.1/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/build.gradle b/build.gradle index 5de185d18..96d7be6eb 100644 --- a/build.gradle +++ b/build.gradle @@ -4,20 +4,18 @@ buildscript { } dependencies { classpath 'com.cinnober.gradle:semver-git:2.5.0' - classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.13.0' - classpath 'io.github.cosmicsilence:gradle-scalafix:0.1.13' } } plugins { id 'java-platform' - id 'maven-publish' - id 'signing' - id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' - id 'io.franzbecker.gradle-lombok' version '5.0.0' + id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' + + // The root project has no sources, but the dependency platform also needs to be published as an artifact + // See https://docs.gradle.org/current/userguide/java_platform_plugin.html + // See https://github.com/Yubico/java-webauthn-server/issues/93#issuecomment-822806951 + id 'project-convention-publish' } -import io.franzbecker.gradle.lombok.LombokPlugin -import io.franzbecker.gradle.lombok.task.DelombokTask import com.yubico.gradle.GitUtils rootProject.description = "Metadata root for the com.yubico:webauthn-server-* module family" @@ -28,34 +26,18 @@ project.ext.publishEnabled = !isCiBuild && project.hasProperty('yubicoPublish') && project.yubicoPublish && project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword') -if (publishEnabled) { - nexusPublishing { - repositories { - sonatype { - stagingProfileId = '6c61426e6529d' - - username = ossrhUsername - password = ossrhPassword - } - } - } -} - wrapper { - gradleVersion = '7.5.1' + gradleVersion = '8.1.1' } dependencies { constraints { - api('com.augustcellars.cose:cose-java:[1.0.0,2)') - api('com.fasterxml.jackson.core:jackson-databind:[2.13.2.1,3)') - api('com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:[2.13.2,3)') - api('com.fasterxml.jackson.datatype:jackson-datatype-jdk8:[2.13.2,3)') - api('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:[2.13.2,3)') - api('com.google.guava:guava:[24.1.1,32)') - api('com.upokecenter:cbor:[4.5.1,5)') - api('org.apache.httpcomponents.client5:httpclient5:[5.0.0,6)') - api('org.slf4j:slf4j-api:[1.7.25,3)') + api(constraintLibs.bundles.jackson) + api(constraintLibs.cbor) + api(constraintLibs.cose) + api(constraintLibs.guava) + api(constraintLibs.httpclient5) + api(constraintLibs.slf4j) } } @@ -66,256 +48,57 @@ allprojects { apply plugin: 'com.cinnober.gradle.semver-git' apply plugin: 'idea' - group = 'com.yubico' - idea.module { downloadJavadoc = true downloadSources = true } -} - -subprojects { - apply plugin: LombokPlugin - - lombok { - version '1.18.24' - sha256 = 'd3584bc2db03f059f984fb0a9c119aac1fa0da578a448e69fc3f68b36584c749' - } - tasks.withType(AbstractCompile) { - if (tasks.findByName('verifyLombok')) { - dependsOn tasks.verifyLombok - } - } repositories { - mavenLocal() mavenCentral() } - if (project !== project(':test-platform')) { - apply plugin: 'com.diffplug.spotless' - spotless { - java { - googleJavaFormat() - } - scala { - scalafmt('2.6.3').configFile(rootProject.file('scalafmt.conf')) - } - } - } -} - -allprojects { - evaluationDependsOnChildren() -} - -task assembleJavadoc(type: Sync) { - from("docs/index.html") { - expand project.properties - } - destinationDir = file("${rootProject.buildDir}/javadoc") -} - -task checkJavaVersionBeforeRelease { - doFirst { - if (JavaVersion.current() != JavaVersion.VERSION_17) { - throw new RuntimeException('Release must be built using JDK 17. Current JDK version: ' + JavaVersion.current()) - } - } -} - -subprojects { project -> - - if (project.plugins.hasPlugin('scala')) { - project.scalafix { - configFile = rootProject.file('scalafix.conf') - - // Work around dependency resolution issues in April 2022 - semanticdb { - autoConfigure = true - version = '4.5.5' - } - } - dependencies.scalafix('com.github.liancheng:organize-imports_2.13:0.6.0') - project.tasks.spotlessApply.dependsOn(project.tasks.scalafix) - project.tasks.spotlessCheck.dependsOn(project.tasks.checkScalafix) - project.tasks.scalafix.finalizedBy(project.tasks.spotlessApply) - project.tasks.checkScalafix.finalizedBy(project.tasks.spotlessCheck) - } - - tasks.withType(JavaCompile) { - options.compilerArgs += '-Xlint:unchecked' - options.deprecation = true - options.encoding = 'UTF-8' - } - tasks.withType(ScalaCompile) { - scalaCompileOptions.additionalParameters = ['-Wunused'] - } - - tasks.withType(AbstractArchiveTask) { - from(rootProject.file('COPYING')) - preserveFileTimestamps = false - reproducibleFileOrder = true - } - tasks.withType(AbstractTestTask) { testLogging { showStandardStreams = isCiBuild } } +} - tasks.withType(AbstractCompile) { shouldRunAfter checkJavaVersionBeforeRelease } - tasks.withType(AbstractTestTask) { shouldRunAfter checkJavaVersionBeforeRelease } - tasks.withType(Sign) { - it.dependsOn check - dependsOn checkJavaVersionBeforeRelease - } - - if (project.hasProperty('publishMe') && project.publishMe) { - task sourcesJar(type: Jar) { - archiveClassifier = 'sources' - from sourceSets.main.allSource - } +if (publishEnabled) { + nexusPublishing { + repositories { + sonatype { + stagingProfileId = '6c61426e6529d' - task delombok(type: DelombokTask, dependsOn: classes) { - ext.outputDir = file("${buildDir}/delombok") - outputs.dir outputDir - sourceSets.main.java.srcDirs.each { - if (!it.exists()) { - it.mkdirs() - } - inputs.dir it - args(it, '-d', outputDir) - } - doFirst { - outputDir.deleteDir() + username = ossrhUsername + password = ossrhPassword } } - - javadoc { - dependsOn delombok - source = delombok.outputDir - options.encoding = 'UTF-8' - options.addStringOption('charset', 'UTF-8') - } - - task javadocJar(type: Jar) { - archiveClassifier = 'javadoc' - from javadoc - } } - if (project.hasProperty('publishMe') && project.publishMe) { - - if (GitUtils.getGitCommit(projectDir) == null) { - throw new RuntimeException("Failed to get git commit ID") - } - - publishing { - publications { - jars(MavenPublication) { - from components.java - artifact javadocJar - artifact sourcesJar - - pom { - name = project.name - description = project.description - url = 'https://developers.yubico.com/java-webauthn-server/' - - developers { - developer { - id = 'emil' - name = 'Emil Lundberg' - email = 'emil@yubico.com' - } - } - - licenses { - license { - name = 'BSD-license' - comments = 'Revised 2-clause BSD license' - } - } - - scm { - url = 'scm:git:git://github.com/Yubico/java-webauthn-server.git' - connection = 'scm:git:git://github.com/Yubico/java-webauthn-server.git' - developerConnection = 'scm:git:ssh://git@github.com/Yubico/java-webauthn-server.git' - tag = 'HEAD' - } - } - } - } - } - - if (publishEnabled) { - signing { - useGpgCmd() - sign publishing.publications.jars + task checkJavaVersionBeforeRelease { + doFirst { + if (JavaVersion.current() != JavaVersion.VERSION_17) { + throw new RuntimeException('Release must be built using JDK 17. Current JDK version: ' + JavaVersion.current()) } } } -} - -// Configure cross-links from webauthn-server-attestation JavaDoc to core JavaDoc -project(':webauthn-server-attestation').tasks.javadoc { - var coreProj = project(':webauthn-server-core') - var coreJavadoc = coreProj.tasks.javadoc - inputs.files coreJavadoc.outputs.files - - // These links won't work locally, but they will work on developers.yubico.com - options.linksOffline("../../webauthn-server-core/${coreProj.version}", "${coreJavadoc.destinationDir}") - - // Use this instead for local testing - //options.linksOffline("file://${coreJavadoc.destinationDir}", "${coreJavadoc.destinationDir}") -} - -// The root project has no sources, but the dependency platform also needs to be published as an artifact -// See https://docs.gradle.org/current/userguide/java_platform_plugin.html -// See https://github.com/Yubico/java-webauthn-server/issues/93#issuecomment-822806951 -publishing { - publications { - jars(MavenPublication) { - from components.javaPlatform - - pom { - name = project.name - description = project.description - url = 'https://developers.yubico.com/java-webauthn-server/' - - developers { - developer { - id = 'emil' - name = 'Emil Lundberg' - email = 'emil@yubico.com' - } - } - - licenses { - license { - name = 'BSD-license' - comments = 'Revised 2-clause BSD license' - } - } + allprojects { + tasks.withType(AbstractCompile) { shouldRunAfter checkJavaVersionBeforeRelease } + tasks.withType(AbstractTestTask) { shouldRunAfter checkJavaVersionBeforeRelease } + tasks.withType(Sign) { + dependsOn checkJavaVersionBeforeRelease + } - scm { - url = 'scm:git:git://github.com/Yubico/java-webauthn-server.git' - connection = 'scm:git:git://github.com/Yubico/java-webauthn-server.git' - developerConnection = 'scm:git:ssh://git@github.com/Yubico/java-webauthn-server.git' - tag = 'HEAD' + tasks.withType(Jar) { + doFirst { + if (GitUtils.getGitCommit(projectDir) == null) { + throw new RuntimeException("Failed to get git commit ID") } } } } } -if (publishEnabled) { - signing { - useGpgCmd() - sign publishing.publications.jars - } -} - task pitestMerge(type: com.yubico.gradle.pitest.tasks.PitestMergeTask) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c55c2e01c..e6e5dc5e5 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,11 +1,17 @@ plugins { groovy + `groovy-gradle-plugin` + `kotlin-dsl` } repositories { + gradlePluginPortal() mavenCentral() } dependencies { + implementation("com.diffplug.spotless:spotless-plugin-gradle:6.13.0") implementation("info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.9.11") + implementation("io.franzbecker:gradle-lombok:5.0.0") + implementation("io.github.cosmicsilence:gradle-scalafix:0.1.14") } diff --git a/buildSrc/src/main/groovy/com/yubico/gradle/pitest/tasks/PitestMergeTask.groovy b/buildSrc/src/main/groovy/com/yubico/gradle/pitest/tasks/PitestMergeTask.groovy index d1ecd235c..720341d72 100644 --- a/buildSrc/src/main/groovy/com/yubico/gradle/pitest/tasks/PitestMergeTask.groovy +++ b/buildSrc/src/main/groovy/com/yubico/gradle/pitest/tasks/PitestMergeTask.groovy @@ -18,8 +18,10 @@ class PitestMergeTask extends DefaultTask { PitestMergeTask() { project.subprojects.each { subproject -> - subproject.tasks.withType(PitestTask).each { pitestTask -> - inputs.files pitestTask.outputs.files + subproject.afterEvaluate { + subproject.tasks.withType(PitestTask).each { pitestTask -> + this.inputs.files pitestTask.outputs.files + } } } } diff --git a/buildSrc/src/main/groovy/project-convention-archives.gradle b/buildSrc/src/main/groovy/project-convention-archives.gradle new file mode 100644 index 000000000..46053514e --- /dev/null +++ b/buildSrc/src/main/groovy/project-convention-archives.gradle @@ -0,0 +1,33 @@ +import com.yubico.gradle.GitUtils + +java { + withJavadocJar() + withSourcesJar() +} + +javadoc { + options.encoding = "UTF-8" + options.addStringOption("charset", "UTF-8") +} + +project.tasks.create("primaryPublishJar") { + dependsOn(project.tasks.jar) +} + +project.tasks.withType(Jar.class) { + manifest { + attributes([ + "Implementation-Id": "java-webauthn-server", + "Implementation-Version": project.version, + "Implementation-Vendor": "Yubico", + "Implementation-Source-Url": "https://github.com/Yubico/java-webauthn-server", + "Git-Commit": GitUtils.getGitCommitOrUnknown(projectDir), + ]) + } +} + +project.tasks.withType(AbstractArchiveTask.class) { + from(project.rootProject.file("COPYING")) + preserveFileTimestamps = false + reproducibleFileOrder = true +} diff --git a/buildSrc/src/main/groovy/project-convention-lombok.gradle b/buildSrc/src/main/groovy/project-convention-lombok.gradle new file mode 100644 index 000000000..13ce462d1 --- /dev/null +++ b/buildSrc/src/main/groovy/project-convention-lombok.gradle @@ -0,0 +1,36 @@ +import io.franzbecker.gradle.lombok.task.DelombokTask + +plugins { + id "java" + id "io.franzbecker.gradle-lombok" +} + +lombok { + version = "1.18.24" + sha256 = "d3584bc2db03f059f984fb0a9c119aac1fa0da578a448e69fc3f68b36584c749" +} + +tasks.withType(AbstractCompile.class) { + dependsOn(tasks.verifyLombok) +} + +task delombok(type: DelombokTask.class, dependsOn: tasks.classes) { + ext.outputDir = file("${buildDir}/delombok") + outputs.dir(outputDir) + project.sourceSets.main.java.srcDirs.forEach { + if (!it.exists()) { + it.mkdirs() + } + inputs.dir(it) + args(it, "-d", outputDir) + } + doFirst { + outputDir.deleteDir() + } +} + +javadoc { + dependsOn(delombok) + source = tasks.delombok.outputDir + inputs.dir(tasks.delombok.outputDir) +} diff --git a/buildSrc/src/main/groovy/project-convention-publish.gradle b/buildSrc/src/main/groovy/project-convention-publish.gradle new file mode 100644 index 000000000..6623e509c --- /dev/null +++ b/buildSrc/src/main/groovy/project-convention-publish.gradle @@ -0,0 +1,56 @@ +plugins { + id "maven-publish" + id "signing" +} + +project.group = "com.yubico" + +project.tasks.withType(Sign.class) { + it.dependsOn(check) +} + +// afterEvaluate needed for project.description to be set when this evaluates +project.afterEvaluate { + publishing { + publications { + jars(MavenPublication.class) { + project.components.forEach { + from(it) + } + + pom { + name = project.name + description = project.description + url = "https://developers.yubico.com/java-webauthn-server/" + + developers { + developer { + id = "emil" + name = "Emil Lundberg" + email = "emil@yubico.com" + } + } + + licenses { + license { + name = "BSD-license" + comments = "Revised 2-clause BSD license" + } + } + + scm { + url = "scm:git:git://github.com/Yubico/java-webauthn-server.git" + connection = "scm:git:git://github.com/Yubico/java-webauthn-server.git" + developerConnection = "scm:git:ssh://git@github.com/Yubico/java-webauthn-server.git" + tag = "HEAD" + } + } + } + } + } + + signing { + useGpgCmd() + sign(publishing.publications.jars) + } +} diff --git a/buildSrc/src/main/kotlin/project-convention-code-formatting.gradle.kts b/buildSrc/src/main/kotlin/project-convention-code-formatting.gradle.kts new file mode 100644 index 000000000..2147b795f --- /dev/null +++ b/buildSrc/src/main/kotlin/project-convention-code-formatting.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id("com.diffplug.spotless") + id("io.github.cosmicsilence.scalafix") +} + +spotless { + java { + googleJavaFormat() + } + scala { + scalafmt("2.6.3").configFile(project.rootProject.file("scalafmt.conf")) + } +} + +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") +} + +project.dependencies.scalafix("com.github.liancheng:organize-imports_2.13:0.6.0") + +project.tasks.spotlessApply.configure { dependsOn(project.tasks["scalafix"]) } +project.tasks.spotlessCheck.configure { dependsOn(project.tasks["checkScalafix"]) } + +// Scalafix adds tasks in afterEvaluate, so their configuration must be deferred +project.afterEvaluate { + project.tasks["scalafix"].finalizedBy(project.tasks.spotlessApply) + project.tasks["checkScalafix"].finalizedBy(project.tasks.spotlessCheck) +} diff --git a/buildSrc/src/main/kotlin/project-convention-java.gradle.kts b/buildSrc/src/main/kotlin/project-convention-java.gradle.kts new file mode 100644 index 000000000..100a52a62 --- /dev/null +++ b/buildSrc/src/main/kotlin/project-convention-java.gradle.kts @@ -0,0 +1,23 @@ +plugins { + java +} + +java { + toolchain { + // Java 8 binaries are not reproducible + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + +tasks.withType(JavaCompile::class) { + options.compilerArgs.add("-Xlint:deprecation") + options.compilerArgs.add("-Xlint:unchecked") + options.encoding = "UTF-8" + options.release.set(8) +} + +tasks.withType(Test::class) { + javaLauncher.set(javaToolchains.launcherFor { + languageVersion.set(JavaLanguageVersion.of(8)) + }) +} diff --git a/buildSrc/src/main/kotlin/project-convention-pitest.gradle.kts b/buildSrc/src/main/kotlin/project-convention-pitest.gradle.kts new file mode 100644 index 000000000..4bd829dfb --- /dev/null +++ b/buildSrc/src/main/kotlin/project-convention-pitest.gradle.kts @@ -0,0 +1,19 @@ +plugins { + java + id("info.solidsoft.pitest") +} + +pitest { + pitestVersion.set("1.9.5") + timestampedReports.set(false) + + outputFormats.set(listOf("XML", "HTML")) + + avoidCallsTo.set(listOf( + "java.util.logging", + "org.apache.log4j", + "org.slf4j", + "org.apache.commons.logging", + "com.google.common.io.Closeables", + )) +} diff --git a/buildSrc/src/main/kotlin/project-convention-scala.gradle.kts b/buildSrc/src/main/kotlin/project-convention-scala.gradle.kts new file mode 100644 index 000000000..16f2f34b8 --- /dev/null +++ b/buildSrc/src/main/kotlin/project-convention-scala.gradle.kts @@ -0,0 +1,14 @@ +plugins { + scala +} + +tasks.withType(ScalaCompile::class) { + // listOf doesn't work at the moment + // See: https://github.com/gradle/gradle/issues/23193 + // See: https://github.com/gradle/gradle/pull/23198 + // See: https://github.com/gradle/gradle/pull/23751 + scalaCompileOptions.additionalParameters = mutableListOf("-Wunused") + javaLauncher.set(javaToolchains.launcherFor { + languageVersion.set(JavaLanguageVersion.of(8)) + }) +} diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 8799f43b4..000000000 --- a/docs/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - -Javadoc index - java-webauthn-server ${version} - - - -

Modules:

- - - diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832f..c1962a79e 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661ee..37aef8d3f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb6c..aeb74cbb4 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -143,12 +140,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/gradlew.bat b/gradlew.bat index f127cfd49..93e3f59f1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/settings.gradle.kts b/settings.gradle.kts index 9c076aa98..4c4394666 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,3 +10,29 @@ include(":test-dependent-projects:java-dep-webauthn-server-core") include(":test-dependent-projects:java-dep-webauthn-server-core-and-bouncycastle") include(":test-dependent-projects:java-dep-yubico-util") include(":test-platform") + +dependencyResolutionManagement { + versionCatalogs { + create("constraintLibs") { + library("cbor", "com.upokecenter:cbor:[4.5.1,5)") + library("cose", "com.augustcellars.cose:cose-java:[1.0.0,2)") + library("guava", "com.google.guava:guava:[24.1.1,32)") + library("httpclient5", "org.apache.httpcomponents.client5:httpclient5:[5.0.0,6)") + library("slf4j", "org.slf4j:slf4j-api:[1.7.25,3)") + + val jacksonVer = version("jackson", "[2.13.2.1,3)") + library("jackson-bom", "com.fasterxml.jackson", "jackson-bom").versionRef(jacksonVer) + library("jackson-databind", "com.fasterxml.jackson.core", "jackson-databind").versionRef(jacksonVer) + library("jackson-dataformat-cbor", "com.fasterxml.jackson.dataformat", "jackson-dataformat-cbor").versionRef(jacksonVer) + library("jackson-datatype-jdk8", "com.fasterxml.jackson.datatype", "jackson-datatype-jdk8").versionRef(jacksonVer) + library("jackson-datatype-jsr310", "com.fasterxml.jackson.datatype", "jackson-datatype-jsr310").versionRef(jacksonVer) + bundle("jackson", listOf( + "jackson-bom", + "jackson-databind", + "jackson-dataformat-cbor", + "jackson-datatype-jdk8", + "jackson-datatype-jsr310", + )) + } + } +} diff --git a/test-dependent-projects/build.gradle.kts b/test-dependent-projects/build.gradle.kts deleted file mode 100644 index 2221c1110..000000000 --- a/test-dependent-projects/build.gradle.kts +++ /dev/null @@ -1,4 +0,0 @@ -plugins { - // This is needed because the root project needs the sourceCompatibility property to exist. - `java-library` -} diff --git a/webauthn-server-attestation/README.adoc b/webauthn-server-attestation/README.adoc index 131ff7f3e..11eb29a4c 100644 --- a/webauthn-server-attestation/README.adoc +++ b/webauthn-server-attestation/README.adoc @@ -21,7 +21,7 @@ This module does four things: - Re-download the metadata BLOB when out of date or invalid. - Provide utilities for selecting trusted metadata entries and authenticators. - Integrate with the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class in the base library, to provide trust root certificates for verifying attestation statements during credential registrations. @@ -30,18 +30,18 @@ Notable *non-features* include: - *Scheduled BLOB downloads.* + The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] class will attempt to download a new BLOB only when its -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] or -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] method is executed. As the names suggest, `loadCachedBlob()` downloads a new BLOB only if the cache is empty or the cached BLOB is invalid or out of date, while `refreshBlob()` always downloads a new BLOB and falls back to the cached BLOB only when the new BLOB is invalid in some way. -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] will never re-download a new BLOB once instantiated. + You should use some external scheduling mechanism to re-run `loadCachedBlob()` @@ -54,12 +54,12 @@ classes keep no internal mutable state. + The FIDO Metadata Service may from time to time report security issues with particular authenticator models. The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] class can be configured with a filter for which authenticators to trust, and untrusted authenticators can be rejected during registration by setting -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] on -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], but this will not affect any credentials already registered. @@ -94,7 +94,7 @@ Maven: com.yubico webauthn-server-attestation - 2.4.0 + 2.4.1 compile ---------- @@ -102,7 +102,7 @@ Maven: Gradle: ---------- -compile 'com.yubico:webauthn-server-attestation:2.4.0' +compile 'com.yubico:webauthn-server-attestation:2.4.1' ---------- @@ -111,7 +111,7 @@ compile 'com.yubico:webauthn-server-attestation:2.4.0' This library uses link:https://semver.org/[semantic versioning]. The public API consists of all public classes, methods and fields in the `com.yubico.fido.metadata` package and its subpackages, i.e., everything covered by the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/package-summary.html[Javadoc]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/package-summary.html[Javadoc]. Package-private classes and methods are NOT part of the public API. The `com.yubico:yubico-util` module is NOT part of the public API. @@ -120,26 +120,26 @@ Breaking changes to these will NOT be reflected in version numbers. == Getting started -Using this module consists of 4 major steps: +Using this module consists of 5 major steps: 1. Create a - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] instance to download and cache metadata BLOBs, and a - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] instance to make use of the downloaded BLOB. See the JavaDoc for these classes for details on how to construct them. + [WARNING] ===== Unlike other classes in this module and the core library, -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] is NOT THREAD SAFE since its -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] methods read and write caches. -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`], on the other hand, is thread safe, and `FidoMetadataDownloader` instances can be reused for subsequent `loadCachedBlob()` and `refreshBlob()` calls @@ -162,18 +162,18 @@ FidoMetadataService mds = FidoMetadataService.builder() ---------- 2. Set the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] as the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/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.4.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] on your - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] instance, and set - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationConveyancePreference(com.yubico.webauthn.data.AttestationConveyancePreference)[`attestationConveyancePreference(AttestationConveyancePreference.DIRECT)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationConveyancePreference(com.yubico.webauthn.data.AttestationConveyancePreference)[`attestationConveyancePreference(AttestationConveyancePreference.DIRECT)`] on `RelyingParty` to request an attestation statement for new registrations. Optionally also set - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] on `RelyingParty` to require trusted attestation for new registrations. + [source,java] @@ -188,9 +188,9 @@ RelyingParty rp = RelyingParty.builder() ---------- 3. After performing registrations, inspect the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`isAttestationTrusted()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`isAttestationTrusted()`] result in - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] to determine whether the authenticator presented an attestation statement that could be verified by any of the trusted attestation certificates in the FIDO Metadata Service. + @@ -207,7 +207,7 @@ if (result.isAttestationTrusted()) { ---------- 4. If needed, use the `findEntries` methods of - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] to retrieve additional authenticator metadata for new registrations. + [source,java] @@ -218,31 +218,34 @@ RegistrationResult result = rp.finishRegistration(/* ... */); Set metadata = mds.findEntries(result); ---------- -By default, -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] -will probably use the SUN provider for the `PKIX` certificate path validation algorithm. -This requires the `com.sun.security.enableCRLDP` system property set to `true` in order to verify the BLOB signature. + 5. If you use the SUN provider for the `PKIX` certificate path validation algorithm, which many deployments do by default: + set the `com.sun.security.enableCRLDP` system property to `true`. + This is required for the SUN `PKIX` provider to support the CRL Distribution Points extension, + which is needed in order to verify the BLOB signature. ++ For example, this can be done on the JVM command line using a `-Dcom.sun.security.enableCRLDP=true` option. -See the https://docs.oracle.com/javase/9/security/java-pki-programmers-guide.htm#JSSEC-GUID-EB250086-0AC1-4D60-AE2A-FC7461374746[Java PKI Programmers Guide] +See the https://docs.oracle.com/javase/9/security/java-pki-programmers-guide.htm#GUID-EB250086-0AC1-4D60-AE2A-FC7461374746__SECTION-139-623E860E[Java PKI Programmers Guide] for details. ++ +This step may not be necessary if you use a different provider for the `PKIX` certificate path validation algorithm. == Selecting trusted authenticators The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] class can be configured with filters for which authenticators to trust. When the `FidoMetadataService` is used as the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/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.4.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], this will be reflected in the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`.isAttestationTrusted()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`.isAttestationTrusted()`] result in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`]. Any authenticators not trusted will also be rejected for new registrations if you set -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] on `RelyingParty`. The filter has two stages: a "prefilter" which selects metadata entries to include in the data source, @@ -305,17 +308,17 @@ entry, and the default registration-time filter excludes any authenticator with a matching `ATTESTATION_KEY_COMPROMISE` status report entry. To customize the filters, configure the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#prefilter(java.util.function.Predicate)[`.prefilter(Predicate)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#prefilter(java.util.function.Predicate)[`.prefilter(Predicate)`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#filter(java.util.function.Predicate)[`.filter(Predicate)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#filter(java.util.function.Predicate)[`.filter(Predicate)`] settings in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`]. The filters are predicate functions; each metadata entry will be included in the data source if and only if the prefilter predicate returns `true` for that entry. Similarly during registration or metadata lookup, the authenticator will be matched with each metadata entry only if the registration-time filter returns `true` for that pair of authenticator and metadata entry. You can also use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] combinator to merge several predicates into one. [NOTE] @@ -325,10 +328,10 @@ This is true for both the prefilter and the registration-time filter. If you want to maintain the default filter in addition to the new behaviour, you must include the default condition in the new filter. For example, you can use -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] to combine a predefined filter with a custom one. The default filters are available via static functions in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.Filters.html[`FidoMetadataService.Filters`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.Filters.html[`FidoMetadataService.Filters`]. ===== @@ -349,9 +352,9 @@ This is why any enforceable attestation policy must disallow unknown trust roots Note that unknown and untrusted attestation is allowed by default, but can be disallowed by explicitly configuring -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] with -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`]. == Alignment with FIDO MDS spec @@ -361,17 +364,17 @@ link:https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.h The library implements these as closely as possible, but with some slight departures from the spec: * Processing rules steps 1-7 are implemented as specified, by the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] class. All "SHOULD" clauses are also respected, with some caveats: ** Step 3 states "The `nextUpdate` field of the Metadata BLOB specifies a date when the download SHOULD occur at latest". `FidoMetadataDownloader` does not automatically re-download the BLOB. Instead, each time the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] method is executed it checks whether a new BLOB should be downloaded. The - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] method always attempts to download a new BLOB when executed, but also does not trigger re-downloads automatically. + @@ -383,7 +386,7 @@ until the next execution of `.loadCachedBlob()` or `.refreshBlob()`. * Metadata entries are not stored or cached individually, instead the BLOB is cached as a whole. In processing rules step 8, neither `FidoMetadataDownloader` nor - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] performs any comparison between versions of a metadata entry. Policy for ignoring metadata entries can be configured via the filter settings in `FidoMetadataService`. See above for details. @@ -395,7 +398,7 @@ There are also some other requirements throughout the spec, which may not be obv states that "The Relying party MUST reject the Metadata Statement if the `authenticatorVersion` has not increased" in an `UPDATE_AVAILABLE` status report. Thus, - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] silently ignores any `MetadataBLOBPayloadEntry` whose `metadataStatement.authenticatorVersion` is present and not greater than or equal to the `authenticatorVersion` in the respective status report. @@ -405,16 +408,16 @@ There are also some other requirements throughout the spec, which may not be obv link:https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#info-statuses[AuthenticatorStatus section] states that "FIDO Servers MUST silently ignore all unknown AuthenticatorStatus values". Thus any unknown status values will be parsed as - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/AuthenticatorStatus.html#UNKNOWN[`AuthenticatorStatus.UNKNOWN`], + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/AuthenticatorStatus.html#UNKNOWN[`AuthenticatorStatus.UNKNOWN`], and - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/MetadataBLOBPayloadEntry.html[`MetadataBLOBPayloadEntry`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/MetadataBLOBPayloadEntry.html[`MetadataBLOBPayloadEntry`] will silently ignore any status report with that status. == Overriding certificate path validation The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.4.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] class uses `CertPathValidator.getInstance("PKIX")` to retrieve a `CertPathValidator` instance. If you need to override any aspect of certificate path validation, such as CRL retrieval or OCSP, you may provide a custom `CertPathValidator` provider for the `"PKIX"` algorithm. diff --git a/webauthn-server-attestation/build.gradle.kts b/webauthn-server-attestation/build.gradle.kts index fd4af93dc..fdcbb8e4c 100644 --- a/webauthn-server-attestation/build.gradle.kts +++ b/webauthn-server-attestation/build.gradle.kts @@ -1,23 +1,16 @@ -import com.yubico.gradle.GitUtils - plugins { `java-library` - scala - `maven-publish` - signing - id("info.solidsoft.pitest") - id("io.github.cosmicsilence.scalafix") + `project-convention-java` + `project-convention-scala` + `project-convention-lombok` + `project-convention-code-formatting` + `project-convention-archives` + `project-convention-publish` + `project-convention-pitest` } description = "Yubico WebAuthn attestation subsystem" -val publishMe by extra(true) - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - sourceSets { create("integrationTest") { compileClasspath += sourceSets.main.get().output @@ -81,24 +74,19 @@ tasks.jar { attributes(mapOf( "Implementation-Id" to "java-webauthn-server-attestation", "Implementation-Title" to project.description, - "Implementation-Version" to project.version, - "Implementation-Vendor" to "Yubico", - "Git-Commit" to GitUtils.getGitCommitOrUnknown(projectDir), )) } } -pitest { - pitestVersion.set("1.9.5") - timestampedReports.set(false) +// Configure cross-links from webauthn-server-attestation JavaDoc to core JavaDoc +tasks.javadoc.configure { + val coreProj = project(":webauthn-server-core") + val coreJavadoc = coreProj.tasks.javadoc.get() + inputs.files(coreJavadoc.outputs.files) - outputFormats.set(listOf("XML", "HTML")) + // These links won't work locally, but they will work on developers.yubico.com + (options as StandardJavadocDocletOptions).linksOffline("../../webauthn-server-core/${coreProj.version}", "${coreJavadoc.destinationDir}") - avoidCallsTo.set(listOf( - "java.util.logging", - "org.apache.log4j", - "org.slf4j", - "org.apache.commons.logging", - "com.google.common.io.Closeables", - )) + // Use this instead for local testing + //(options as StandardJavadocDocletOptions).linksOffline("file://${coreJavadoc.destinationDir}", "${coreJavadoc.destinationDir}") } diff --git a/webauthn-server-core/build.gradle.kts b/webauthn-server-core/build.gradle.kts index 8816b72ff..69cf488b8 100644 --- a/webauthn-server-core/build.gradle.kts +++ b/webauthn-server-core/build.gradle.kts @@ -1,24 +1,17 @@ -import com.yubico.gradle.GitUtils - plugins { `java-library` - scala - `maven-publish` - signing - id("info.solidsoft.pitest") - id("io.github.cosmicsilence.scalafix") id("me.champeau.jmh") version "0.6.8" + `project-convention-java` + `project-convention-scala` + `project-convention-lombok` + `project-convention-code-formatting` + `project-convention-archives` + `project-convention-publish` + `project-convention-pitest` } description = "Yubico WebAuthn server core API" -val publishMe by extra(true) - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - dependencies { api(platform(rootProject)) @@ -59,39 +52,18 @@ configurations.jmhRuntimeClasspath { exclude(module = "slf4j-test") } -tasks.jar { +tasks.withType(Jar::class) { manifest { attributes(mapOf( + "Implementation-Title" to "Yubico Web Authentication server library", + "Specification-Title" to "Web Authentication: An API for accessing Public Key Credentials", "Specification-Version" to "Level 2 Proposed Recommendation 2021-04-08", "Specification-Vendor" to "World Wide Web Consortium", - "Specification-Url" to "https://www.w3.org/TR/2021/REC-webauthn-2-20210408/", "Specification-Url-Latest" to "https://www.w3.org/TR/webauthn-2/", "Specification-W3c-Status" to "recommendation", "Specification-Release-Date" to "2021-04-08", - - "Implementation-Id" to "java-webauthn-server", - "Implementation-Title" to "Yubico Web Authentication server library", - "Implementation-Version" to project.version, - "Implementation-Vendor" to "Yubico", - "Implementation-Source-Url" to "https://github.com/Yubico/java-webauthn-server", - "Git-Commit" to GitUtils.getGitCommitOrUnknown(projectDir), )) } } - -pitest { - pitestVersion.set("1.9.5") - timestampedReports.set(false) - - outputFormats.set(listOf("XML", "HTML")) - - avoidCallsTo.set(listOf( - "java.util.logging", - "org.apache.log4j", - "org.slf4j", - "org.apache.commons.logging", - "com.google.common.io.Closeables", - )) -} diff --git a/webauthn-server-demo/README b/webauthn-server-demo/README index 12b1f2a45..f1e50c32f 100644 --- a/webauthn-server-demo/README +++ b/webauthn-server-demo/README @@ -15,10 +15,8 @@ class which provides the REST API on top of it. == Quick start -``` -$ ./gradlew run -$ $BROWSER https://localhost:8443/ -``` + $ ./gradlew run + $ $BROWSER https://localhost:8443/ == Architecture @@ -40,7 +38,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.4.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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 @@ -54,7 +52,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.4.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class. + This layer implements the Web Authentication @@ -65,11 +63,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.4.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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.4.0/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.4.1/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`] diff --git a/webauthn-server-demo/build.gradle.kts b/webauthn-server-demo/build.gradle.kts index 99f5b84b6..7a77b2347 100644 --- a/webauthn-server-demo/build.gradle.kts +++ b/webauthn-server-demo/build.gradle.kts @@ -1,9 +1,10 @@ plugins { - java war application - scala - id("io.github.cosmicsilence.scalafix") + `project-convention-java` + `project-convention-scala` + `project-convention-lombok` + `project-convention-code-formatting` } description = "WebAuthn demo" diff --git a/yubico-util-scala/build.gradle.kts b/yubico-util-scala/build.gradle.kts index 8e5ee2718..75ec36b00 100644 --- a/yubico-util-scala/build.gradle.kts +++ b/yubico-util-scala/build.gradle.kts @@ -1,14 +1,11 @@ plugins { - scala - id("io.github.cosmicsilence.scalafix") + `project-convention-java` + `project-convention-scala` + `project-convention-code-formatting` } description = "Yubico internal Scala utilities" -java { - targetCompatibility = JavaVersion.VERSION_1_8 -} - dependencies { implementation(platform(rootProject)) implementation(platform(project(":test-platform"))) diff --git a/yubico-util/build.gradle.kts b/yubico-util/build.gradle.kts index ba6974f9e..c5849fdfa 100644 --- a/yubico-util/build.gradle.kts +++ b/yubico-util/build.gradle.kts @@ -1,22 +1,17 @@ plugins { `java-library` - scala - `maven-publish` - signing - id("info.solidsoft.pitest") - id("io.github.cosmicsilence.scalafix") id("me.champeau.jmh") version "0.6.8" + `project-convention-java` + `project-convention-scala` + `project-convention-lombok` + `project-convention-code-formatting` + `project-convention-archives` + `project-convention-publish` + `project-convention-pitest` } description = "Yubico internal utilities" -val publishMe by extra(true) - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - dependencies { api(platform(rootProject)) @@ -45,29 +40,11 @@ configurations.jmhRuntimeClasspath { exclude(module = "slf4j-test") } - tasks.jar { manifest { attributes(mapOf( "Implementation-Id" to "yubico-util", "Implementation-Title" to project.description, - "Implementation-Version" to project.version, - "Implementation-Vendor" to "Yubico", )) } } - -pitest { - pitestVersion.set("1.9.5") - timestampedReports.set(false) - - outputFormats.set(listOf("XML", "HTML")) - - avoidCallsTo.set(listOf( - "java.util.logging", - "org.apache.log4j", - "org.slf4j", - "org.apache.commons.logging", - "com.google.common.io.Closeables", - )) -} diff --git a/yubico-util/src/main/java/com/yubico/internal/util/JacksonCodecs.java b/yubico-util/src/main/java/com/yubico/internal/util/JacksonCodecs.java index f9c1027c9..1c4e1a960 100644 --- a/yubico-util/src/main/java/com/yubico/internal/util/JacksonCodecs.java +++ b/yubico-util/src/main/java/com/yubico/internal/util/JacksonCodecs.java @@ -3,8 +3,10 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.Base64Variants; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.dataformat.cbor.CBORFactory; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; @@ -19,13 +21,15 @@ public static ObjectMapper cbor() { } public static ObjectMapper json() { - return new ObjectMapper() + return JsonMapper.builder() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) - .setSerializationInclusion(Include.NON_ABSENT) - .setBase64Variant(Base64Variants.MODIFIED_FOR_URL) - .registerModule(new Jdk8Module()) - .registerModule(new JavaTimeModule()); + .configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true) + .serializationInclusion(Include.NON_ABSENT) + .defaultBase64Variant(Base64Variants.MODIFIED_FOR_URL) + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .build(); } public static CBORObject deepCopy(CBORObject a) {