java-webauthn-server icon indicating copy to clipboard operation
java-webauthn-server copied to clipboard

Server-side Web Authentication library for Java https://www.w3.org/TR/webauthn/#rp-operations

java-webauthn-server

:toc: :toc-placement: macro :toc-title:

image:https://github.com/Yubico/java-webauthn-server/workflows/build/badge.svg["Build Status", link="https://github.com/Yubico/java-webauthn-server/actions"] image:https://img.shields.io/endpoint?url=https%3A%2F%2FYubico.github.io%2Fjava-webauthn-server%2Fcoverage-badge.json["Mutation test coverage", link="https://Yubico.github.io/java-webauthn-server/"] image:https://github.com/Yubico/java-webauthn-server/actions/workflows/release-verify-signatures.yml/badge.svg["Binary reproducibility", link="https://github.com/Yubico/java-webauthn-server/actions/workflows/release-verify-signatures.yml"]

Server-side https://www.w3.org/TR/webauthn/[Web Authentication] library for Java. Provides implementations of the https://www.w3.org/TR/webauthn/#sctn-rp-operations[Relying Party operations] required for a server to support Web Authentication. This includes registering authenticators and authenticating registered authenticators.

[WARNING] .Psychic signatures in Java

In April 2022, link:https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/[CVE-2022-21449] was disclosed in Oracle's OpenJDK (and other JVMs derived from it) which can impact applications using java-webauthn-server. The impact is that for the most common type of WebAuthn credential, invalid signatures are accepted as valid, allowing authentication bypass for users with such a credential. Please read link:https://openjdk.java.net/groups/vulnerability/advisories/2022-04-19[Oracle's advisory] and make sure you are not using one of the impacted OpenJDK versions. If you are, we urge you to upgrade your Java deployment to a version that is safe.

toc::[]

== Dependency configuration

Maven:


com.yubico webauthn-server-core 2.0.0 compile ----------

Gradle:


compile 'com.yubico:webauthn-server-core:2.0.0'

NOTE: You may need additional dependencies with JCA providers to support some signature algorithms. In particular, OpenJDK 14 and earlier does not include providers for the EdDSA family of algorithms. The library will log warnings if you try to configure it for algorithms with no JCA provider available.

=== Semantic versioning

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.0.0/com/yubico/webauthn/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. Breaking changes to these will NOT be reflected in version numbers.

=== Additional modules

In addition to the main webauthn-server-core module, there is also:

  • link:webauthn-server-attestation[webauthn-server-attestation]: Integration with the https://fidoalliance.org/metadata/[FIDO Metadata Service] for retrieving and selecting trust roots to use for verifying https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation[attestation statements].

== Features

  • Generates request objects suitable as parameters to navigator.credentials.create() and .get()
  • Performs all necessary https://www.w3.org/TR/webauthn/#sctn-rp-operations[validation logic] on the response from the client
  • No mutable state or side effects - everything (except builders) is thread safe
  • Optionally integrates with an "attestation trust source" to verify https://www.w3.org/TR/webauthn/#sctn-attestation[authenticator attestations]
  • Reproducible builds: release signatures match fresh builds from source. See link:#reproducible-builds[Reproducible builds] below.

=== Non-features

This library has no concept of accounts, sessions, permissions or identity federation, and it is not an authentication framework; it only deals with executing the WebAuthn authentication mechanism. Sessions, account management and other higher level concepts can make use of this authentication mechanism, but the authentication mechanism alone does not make a security system.

== Documentation

See the link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/com/yubico/webauthn/package-summary.html[Javadoc] for in-depth API documentation.

== Migrating from version 1.x

See link:doc/Migrating_from_v1.adoc[the migration guide].

== Getting started

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.0.0/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.0.0/com/yubico/webauthn/RelyingParty.html[RelyingParty] class.
  3. Use the link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/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.0.0/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[RelyingParty.fininshRegistration(...)] methods to perform registration ceremonies.
  4. Use the link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/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.0.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[RelyingParty.fininshAssertion(...)] methods to perform authentication ceremonies.
  5. Use the outputs of finishRegistration and finishAssertion to update your database, initiate sessions, etc.

The client side involves:

  1. Call navigator.credentials.create() or .get(), passing the result from RelyingParty.startRegistration(...) or .startAssertion(...) as the argument.
  2. Encode the result of the successfully resolved promise and return it to the server. For this you need some way to encode Uint8Array values; this guide will use GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library.

Example code is given below. For more detailed example usage, see 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.0.0/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] as a simple example.

=== 2. Instantiate a RelyingParty

The link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/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.0.0/com/yubico/webauthn/CredentialRepository.html[CredentialRepository] implementation (called MyCredentialRepository here) as an argument:

[source,java]

RelyingPartyIdentity rpIdentity = RelyingPartyIdentity.builder() .id("example.com") // Set this to a parent domain that covers all subdomains // where users' credentials should be valid .name("Example Application") .build();

RelyingParty rp = RelyingParty.builder() .identity(rpIdentity) .credentialRepository(new MyCredentialRepository()) .build();

=== 3. Registration

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.0.0/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, call https://www.w3.org/TR/webauthn-2/#ref-for-dom-publickeycredential-getclientextensionresults[cred.getClientExtensionResults()] 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.0.0/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[RelyingParty.fininshRegistration(...)].
  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.

First, generate registration parameters and send them to the client:

[source,java]

Optional<UserIdentity> findExistingUser(String username) { /* ... */ }

PublicKeyCredentialCreationOptions request = rp.startRegistration( StartRegistrationOptions.builder() .user( findExistingUser("alice") .orElseGet(() -> { byte[] userHandle = new byte[64]; random.nextBytes(userHandle); return UserIdentity.builder() .name("alice") .displayName("Alice Hypothetical") .id(new ByteArray(userHandle)) .build(); }) ) .build());

String credentialCreateJson = request.toCredentialsCreateJson(); return credentialCreateJson; // Send to client

You will need to keep this link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/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.0.0/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[RelyingParty.fininshRegistration(...)] later.

Now call the WebAuthn API on the client side:

[source,javascript]

import * as webauthnJson from "@github/webauthn-json";

// Make the call that returns the credentialCreateJson above const credentialCreateOptions = await fetch(/* ... */).then(resp => resp.json());

// Call WebAuthn ceremony using webauthn-json wrapper const publicKeyCredential = await webauthnJson.create(credentialCreateOptions);

// Return encoded PublicKeyCredential to server fetch(/* ... */, { body: JSON.stringify(publicKeyCredential) });

Validate the response on the server side:

[source,java]

String publicKeyCredentialJson = /* ... */; // publicKeyCredential from client PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> pkc = PublicKeyCredential.parseRegistrationResponseJson(publicKeyCredentialJson);

try { RegistrationResult result = rp.finishRegistration(FinishRegistrationOptions.builder() .request(request) // The PublicKeyCredentialCreationOptions from startRegistration above // NOTE: Must be stored in server memory or otherwise protected against tampering .response(pkc) .build()); } catch (RegistrationFailedException e) { /* ... */ }

Finally, if the previous step was successful, store the new credential in your database. Here is an example of things you will likely want to store:

[source,java]

storeCredential( // Some database access method of your own design "alice", // Username or other appropriate user identifier result.getKeyId(), // Credential ID and transports for allowCredentials result.getPublicKeyCose(), // Public key for verifying authentication signatures result.isDiscoverable(), // Can this key be used for username-less auth? result.signatureCount(), // Initial signature counter value pkc.getResponse().getAttestationObject(), // Store attestation object for future reference pkc.getResponse().getClientDataJSON() // Store client data for re-verifying signature if needed );

=== 4. Authentication

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.0.0/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.
  3. With cred as the result of the successfully resolved promise, call 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.0.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[RelyingParty.fininshAssertion(...)].
  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.

First, generate authentication parameters and send them to the client:

[source,java]

AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder() .username("alice") // Or .userHandle(ByteArray) if preferred .build()); String credentialGetJson = request.toCredentialsGetJson(); 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.0.0/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.0.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[RelyingParty.fininshAssertion(...)] later.

Now call the WebAuthn API on the client side:

[source,javascript]

import * as webauthnJson from "@github/webauthn-json";

// Make the call that returns the credentialGetJson above const credentialGetOptions = await fetch(/* ... */).then(resp => resp.json());

// Call WebAuthn ceremony using webauthn-json wrapper const publicKeyCredential = await webauthnJson.get(credentialGetOptions);

// Return encoded PublicKeyCredential to server fetch(/* ... */, { body: JSON.stringify(publicKeyCredential) });

Validate the response on the server side:

[source,java]

String publicKeyCredentialJson = /* ... */; // publicKeyCredential from client PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> pkc = PublicKeyCredential.parseAssertionResponseJson(publicKeyCredentialJson);

try { AssertionResult result = rp.finishAssertion(FinishAssertionOptions.builder() .request(request) // The PublicKeyCredentialRequestOptions from startAssertion above .response(pkc) .build());

if (result.isSuccess()) {
    return result.getUsername();
}

} catch (AssertionFailedException e) { /* ... */ } 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.0.0/com/yubico/webauthn/AssertionResult.html[AssertionResult]. Most importantly, you should update the signature counter. That might look something like this:

[source,java]

updateCredential( // Some database access method of your own design "alice", // Query by username or other appropriate user identifier result.getCredentialId(), // Query by credential ID of the credential used result.signatureCount(), // Set new signature counter value Clock.systemUTC().instant() // Set time of last use (now) );

Then do whatever else you need - for example, initiate a user session.

=== 5. Passwordless, username-less authentication

WebAuthn supports passwordless multi-factor authentication via on-authenticator https://www.w3.org/TR/webauthn-2/#user-verification[user verification], and username-less authentication via https://www.w3.org/TR/webauthn-2/#discoverable-credential[discoverable credentials] (sometimes the term "passwordless" is used to mean the combination of both, but here the two are treated separately).

Discoverable credentials must be enabled at registration time by setting the link:https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialcreationoptions-authenticatorselection[authenticatorSelection].link:https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-residentkey[residentKey] option:

[source,java]

PublicKeyCredentialCreationOptions request = rp.startRegistration( StartRegistrationOptions.builder() .user(/* ... */) .authenticatorSelection(AuthenticatorSelectionCriteria.builder() .residentKey(ResidentKeyRequirement.REQUIRED) .build()) .build());

The username can then be omitted when starting an authentication ceremony:

[source,java]

AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder().build());

Some authenticators might enable this feature even if not required, and setting the residentKey option to ResidentKeyRequirement.PREFERRED will enable it if the authenticator supports it. The https://www.w3.org/TR/webauthn-2/#sctn-authenticator-credential-properties-extension[credProps extension] can be used to determine whether the created credential is discoverable, and is enabled by default.

User verification can be enforced independently per authentication ceremony:

[source,java]

AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder() .username("alice") .userVerification(UserVerificationRequirement.REQUIRED) .build());

Then link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[RelyingParty.fininshAssertion(...)] 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 link:https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialcreationoptions-authenticatorselection[authenticatorSelection].link:https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-userverification[userVerification] option set:

[source,java]

PublicKeyCredentialCreationOptions request = rp.startRegistration( StartRegistrationOptions.builder() .user(/* ... */) .authenticatorSelection(AuthenticatorSelectionCriteria.builder() .userVerification(UserVerificationRequirement.REQUIRED) .build()) .build());

== Migrating from U2F

This section is only relevant for applications that have user credentials registered via the link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html[U2F JavaScript API]. New WebAuthn deployments can skip this section.

The WebAuthn API is backwards-compatible with U2F authenticators, and credentials registered via the U2F API will continue to work with the WebAuthn API with the right settings.

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.0.0/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].

  1. Set the link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/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.0.0/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].

This will enable the link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension[appid] and link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension[appidExclude] extensions and configure the RelyingParty to accept the given AppId when verifying authenticator signatures.

  1. Generate a link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle[user handle] for each existing user and store it in their account, or decide on a method for deriving one deterministically from existing user attributes. For example, if your user records are assigned UUIDs, you can use that UUID as the user handle. You SHOULD NOT use a plain username or e-mail address, or hash of either, as the user handle - for more on this, see the link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-user-handle-privacy[User Handle Contents] privacy consideration.

  2. When your link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/com/yubico/webauthn/CredentialRepository.html[CredentialRepository] creates a link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.0.0/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.0.0/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.0.0/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.0.0/com/yubico/webauthn/data/ByteArray.html#fromBase64Url(java.lang.String)[ByteArray.fromBase64Url] as appropriate before passing them to the RegisteredCredential.

  3. 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.0.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.0.0/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyCose(com.yubico.webauthn.data.ByteArray)[publicKeyCose()] to set the credential public key.

  4. Replace calls to the U2F link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[register] method with calls to navigator.credentials.create() as described in link:#getting-started[Getting started].

  5. Replace calls to the U2F link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[sign] method with calls to navigator.credentials.get() as described in link:#getting-started[Getting started].

Existing U2F credentials should now work with the WebAuthn API.

Note that new credentials registered on U2F authenticators via the WebAuthn API are NOT backwards compatible with the U2F JavaScript API.

== Architecture

The library tries to place as few requirements on the overall application architecture as possible. For this reason it is stateless and free from side effects, and does not directly interact with any database. This means it is database agnostic and thread safe. The following diagram illustrates an example architecture for an application using the library.

image::https://raw.githubusercontent.com/Yubico/java-webauthn-server/main/docs/img/demo-architecture.svg?sanitize=true["Example application architecture",align="center"]

The application manages all state and database access, and communicates with the library via POJO representations of requests and responses. The following diagram illustrates the data flow during a WebAuthn registration or authentication ceremony.

image::https://raw.githubusercontent.com/Yubico/java-webauthn-server/main/docs/img/demo-sequence-diagram.svg?sanitize=true["WebAuthn ceremony sequence diagram",align="center"]

In this diagram, the Client is the user's browser and the application's client-side scripts. The Server is the application and its business logic, the Library is this library, and the Users database stores registered WebAuthn credentials.

. The client requests to start the ceremony, for example by submitting a form. The username may or may not be known at this point. For example, the user might be requesting to create a new account, or we might be using username-less authentication.

. If the user does not already have a https://www.w3.org/TR/webauthn/#user-handle[user handle], the application creates one in some application-specific way.

. The application may choose to authenticate the user with a password or the like before proceeding.

. The application calls one of the library's "start" methods to generate a parameter object to be passed to navigator.credentials.create() or .get() on the client.

. The library generates a random challenge and an assortment of other arguments depending on configuration set by the application.

. If the username is known, the library uses a read-only database adapter provided by the application to look up the user's credentials.

. The returned list of https://www.w3.org/TR/webauthn/#credential-id[credential IDs] is used to populate the https://www.w3.org/TR/webauthn/#dom-publickeycredentialcreationoptions-excludecredentials[excludeCredentials] or https://www.w3.org/TR/webauthn/#dom-publickeycredentialrequestoptions-allowcredentials[allowCredentials] parameter.

. The library returns a request object which can be serialized to JSON and passed as the publicKey argument to navigator.credentials.create() or .get(). For registration ceremonies this will be a https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialcreationoptions[PublicKeyCredentialCreationOptions], and for authentication ceremonies it will be a https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrequestoptions[PublicKeyCredentialRequestOptions]. The application stores the request in temporary storage.

. The application's client-side script runs navigator.credentials.create() or .get() with request as the publicKey argument.

. The user confirms the operation and the client returns a https://www.w3.org/TR/webauthn/#public-key-credential[PublicKeyCredential] object response to the application.

. The application retrieves the request from temporary storage and passes request and response to one of the library's "finish" methods to run the response validation logic.

. The library verifies that the response contents - challenge, origin, etc. - are valid.

. If this is an authentication ceremony, the library uses the database adapter to look up the public key for the credential named in response.id.

. The database adapter returns the public key.

. The library verifies the authentication signature.

. The library returns a POJO representation of the result of the ceremony. For registration ceremonies, this will include the credential ID and public key of the new credential. The application may opt in to also getting information about the authenticator model and whether the authenticator attestation is trusted. For authentication ceremonies, this will include the username and user handle, the credential ID of the credential used, and the new https://www.w3.org/TR/webauthn/#signature-counter[signature counter] value for the credential.

. The application inspects the result object and takes any appropriate actions as defined by its business logic.

. If the result is not satisfactory, the application reports failure to the client.

. If the result is satisfactory, the application proceeds with storing the new credential if this is a registration ceremony.

. If this is an authentication ceremony, the application updates the signature counter stored in the database for the credential.

. Finally, the application reports success and resumes its business logic.

== Using attestation

WebAuthn supports link:https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation[authenticator attestation], which provides a way for the web service to request cryptographic proof of what authenticator the user is using. Most services do not need this, and it is disabled by default.

The link:webauthn-server-attestation[webauthn-server-attestation module] provides optional additional features for working with attestation. See the module documentation for more details.

== Building

Use the included https://docs.gradle.org/current/userguide/gradle_wrapper.html[Gradle wrapper] to build the .jar artifact:

[source,shell]

$ ./gradlew :webauthn-server-core:jar

The output is built in the webauthn-server-core/build/libs/ directory, and the version is derived from the most recent Git tag. Builds done on a tagged commit will have a plain x.y.z version number, while a build on any other commit will result in a version number containing the abbreviated commit hash.

To run the tests:

[source,shell]

$ ./gradlew check

To run the http://pitest.org/[PIT mutation tests] (this may take upwards of 30 minutes):

[source,shell]

$ ./gradlew pitest

=== Reproducible builds Starting in version 1.4.0-RC2, artifacts are built reproducibly. Fresh builds from tagged commits should therefore be verifiable by signatures from Maven Central and GitHub releases:

[source,shell]

$ git checkout 1.4.0-RC2 $ ./gradlew :webauthn-server-core:jar

$ wget https://repo1.maven.org/maven2/com/yubico/webauthn-server-core/1.4.0-RC2/webauthn-server-core-1.4.0-RC2.jar.asc $ gpg --verify webauthn-server-core-1.4.0-RC2.jar.asc webauthn-server-core/build/libs/webauthn-server-core-1.4.0-RC2.jar

$ wget https://github.com/Yubico/java-webauthn-server/releases/download/1.4.0-RC2/webauthn-server-core-1.4.0-RC2.jar.asc $ gpg --verify webauthn-server-core-1.4.0-RC2.jar.asc webauthn-server-core/build/libs/webauthn-server-core-1.4.0-RC2.jar

Note that building with a different JDK may produce a different artifact. To ensure binary reproducibility, please build with the same JDK as specified in the release notes. Reproducible builds also require building from a Git repository, since the build embeds version number and Git commit ID into the built artifacts.

Official Yubico software signing keys are listed on the https://developers.yubico.com/Software_Projects/Software_Signing.html[Yubico Developers site].