Server-side Web Authentication library for Java. Provides implementations of the Relying Party operations required for a server to support Web Authentication. This includes registering authenticators and authenticating registered authenticators.
Maven:
<dependency> <groupId>com.yubico</groupId> <artifactId>webauthn-server-core</artifactId> <version>1.3.0</version> <scope>compile</scope> </dependency>
Gradle:
compile 'com.yubico:webauthn-server-core:1.3.0'
-
Generates request objects suitable as parameters to
navigator.credentials.create()
and.get()
-
Performs all necessary validation logic on the response from the client
-
No mutable state or side effects - everything (except builders) is thread safe
-
Optionally integrates with a "metadata service" to verify authenticator attestations and annotate responses with additional authenticator metadata
This library has no concept of accounts, sessions, permissions or identity federation, and it’s 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.
-
In the example app, authentication does not work in Chrome as of version 70. This is due to a bug in Chrome which will not be worked around here. To work around this in application code, you can omit the
userHandle
when constructing anAuthenticatorAssertionResponse
value if theuserHandle
is empty. See Yubico#12 .
See the Javadoc for in-depth API documentation.
Implement the
CredentialRepository
interface with your database access logic. See
InMemoryRegistrationStorage
for an example.
Instantiate the
RelyingParty
class:
RelyingPartyIdentity rpIdentity = RelyingPartyIdentity.builder()
.id("example.com")
.name("Example Application")
.build();
RelyingParty rp = RelyingParty.builder()
.identity(rpIdentity)
.credentialRepository(new MyCredentialRepository())
.build();
Initiate a registration ceremony:
byte[] userHandle = new byte[64];
random.nextBytes(userHandle);
PublicKeyCredentialCreationOptions request = rp.startRegistration(StartRegistrationOptions.builder()
.user(UserIdentity.builder()
.name("alice")
.displayName("Alice Hypothetical")
.id(new ByteArray(userHandle))
.build())
.build());
Serialize request
to JSON and send it to the client:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper jsonMapper = new ObjectMapper()
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.setSerializationInclusion(Include.NON_ABSENT)
.registerModule(new Jdk8Module());
String json = jsonMapper.writeValueAsString(request);
return json;
Get the response from the client:
String responseJson = /* ... */;
PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> pkc =
PublicKeyCredential.parseRegistrationResponseJson(responseJson);
Validate the response:
try {
RegistrationResult result = rp.finishRegistration(FinishRegistrationOptions.builder()
.request(request)
.response(pkc)
.build());
} catch (RegistrationFailedException e) { /* ... */ }
Update your database:
storeCredential("alice", result.getKeyId(), result.getPublicKeyCose());
Initiate an authentication ceremony:
AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder()
.username(Optional.of("alice"))
.build());
String json = jsonMapper.writeValueAsString(request);
return json;
Validate the response:
String responseJson = /* ... */;
PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> pkc =
PublicKeyCredential.parseAssertionResponseJson(responseJson);
try {
AssertionResult result = rp.finishAssertion(FinishAssertionOptions.builder()
.request(request)
.response(pkc)
.build());
if (result.isSuccess()) {
return result.getUsername();
}
} catch (AssertionFailedException e) { /* ... */ }
throw new RuntimeException("Authentication failed");
For more detailed example usage, see
webauthn-server-demo
for a complete demo server.
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.
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.
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 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 credential IDs is used to populate the
excludeCredentials
orallowCredentials
parameter. -
The library returns a
request
object which can be serialized to JSON and passed as thepublicKey
argument tonavigator.credentials.create()
or.get()
. For registration ceremonies this will be aPublicKeyCredentialCreationOptions
, and for authentication ceremonies it will be aPublicKeyCredentialRequestOptions
. The application stores therequest
in temporary storage. -
The application’s client-side script runs
navigator.credentials.create()
or.get()
withrequest
as thepublicKey
argument. -
The user confirms the operation and the client returns a
PublicKeyCredential
objectresponse
to the application. -
The application retrieves the
request
from temporary storage and passesrequest
andresponse
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 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.
Use the included
Gradle wrapper to
build the .jar
artifact:
$ ./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.
Starting in version 1.4.0
, artifacts are built reproducibly. Fresh builds from
tagged commits should therefore be verifiable by signatures from Maven Central:
$ git checkout 1.4.0
$ ./gradlew :webauthn-server-core:jar
$ wget https://repo1.maven.org/maven2/com/yubico/webauthn-server-core/1.4.0/webauthn-server-core-1.4.0.jar.asc
$ gpg --verify webauthn-server-core-1.4.0.jar.asc webauthn-server-core/build/libs/webauthn-server-core-1.4.0.jar
Official Yubico software signing keys are listed on the Yubico Developers site.
To run the tests:
$ ./gradlew check
To run the PIT mutation tests:
$ ./gradlew pitest