Initialize a passkey relying party

You’re ready to begin building your relying party. Take these first steps to initialize your application

In this section we will be discussing how to initialize your relying party with the necessary configurations required in order to facilitate passkey registration, authentication, and credential management ceremonies.

As noted in the prerequisites section, this guide will be utilizing the Yubico’s java-webauthn-server library, but the core concepts can still be used regardless of the library being utilized.

Before you begin, ensure that you have included the following dependencies in your Java application

<dependency>
    <groupId>com.yubico</groupId>
    <artifactId>webauthn-server-core</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>com.yubico</groupId>
    <artifactId>webauthn-server-attestation</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>com.yubico</groupId>
    <artifactId>yubico-util</artifactId>
    <version>2.0.0</version>
</dependency>

Identity provider connections

First, ensure that your identity provider is able to send requests to your application. This can be done through APIs, webhooks, or custom triggers offered by your relying party. This will vary greatly depending on your application stack. For the rest of this guide we will assume that your application can receive requests from your identity provider.

Initialize repositories

Next we will declare our repositories for credential storage, along with storage for authentication and registration requests.

Credential repository

We’ll start by initializing the repository that will store user credentials.

First let’s define the schema that is used for the database table. The code sample below contains a schema that can be utilized for this database.

Figure 1 contains sample code that defines the database table schema

CREATE TABLE credentialRegistrations (
   username TEXT,
   userHandle TEXT,
   credentialId NVARCHAR(1023),
   registration TEXT,
   creationDate DATETIME DEFAULT CURRENT_TIMESTAMP,
   lastUsedDate DATETIME,
   lastUpdatedDate DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   active BOOL DEFAULT TRUE
);

Figure 1

Next you will implement your Java class that is capable of interacting with the database through an SDK, API, or library. While this class is out of scope of this guide, you can find an example here that we use to connect our application to our AWS Aurora database. Please see this sample class for a sample object that is used for the values in this repository.

Make sure that your custom class above implements the interface CredentialRepository.

Once your class has been implemented, declare it in your main application code. For our example our class is called RegistrationStorage.

Figure 2 contains sample code used to define your new credential repository in your application.

private final RegistrationStorage userStorage = new RDSRegistrationStorage();

Figure 2

Registration request repository

Next you will want a way to store the registration requests that have been issued by the relying party. This is important to implement as you will want a mechanism to ensure only valid registration requests are sent to your application. This will allow you to:

  • Block unprompted registrations

  • Block registrations that send a challenge that does not match what was issued

  • Invalidate registration requests that have been used or have exceeded a timeout period

Figure 3 provides sample code demonstrating the table schema used to store registration requests.

CREATE TABLE registrationRequests (
   _key TEXT,
   _value TEXT,
   creationDate DATETIME DEFAULT CURRENT_TIMESTAMP,
   lastUpdatedDate DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Figure 3

The registration request object will be stored as a string in the value field. Once your table is created you can create a class to interface to the repository. This interface is not provided by the java-webauthn-server library. Please refer to this sample code for an overview of the methods that will need to be used in order to support the use of registration request repository. Please see this sample class for a sample object that is used for the values in this repository.

Once your class is declared, you can initialize it in your application. In our example this class is referred to as RegistrationRequestStorage.

Figure 4 contains sample code used to define your new registration request repository in your application

private final RegistrationRequestStorage registerRequestStorage = new RegistrationRequestStorage();

Figure 4

Assertion request repository

The assertion request repository is similar to the registration request repository mentioned above. Assertion is another way to reference authentication requests. You will want a mechanism to store and refer to authentication requests that have been sent. This will enable you to:

  • Block unprompted authentications

  • Block authentications that utilized a challenge that does not match what was issued

  • Refer to the original challenge that was issued in order to verify signed challenges

  • Invalidate authentication requests that have been used or have exceeded a timeout period

Figure 5 provides sample code demonstrating the table schema used to store assertion requests.

CREATE TABLE assertionRequests (
   _key TEXT,
   _value TEXT,
   creationDate DATETIME DEFAULT CURRENT_TIMESTAMP,
   lastUpdatedDate DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Figure 5

The assertion request object will be stored as a string in the value field. Once your table is created you can create a class to interface to the repository. This interface is not provided by the java-webauthn-server library. Please refer to this sample code for an overview of the methods that will need to be used in order to support the use of assertion request repository. Please see this sample class for a sample object that is used for the values in this repository.

Once your class is declared, you can initialize it in your application. In our example this class is referred to as AssertionRequestStorage.

Figure 6 contains sample code used to define your new assertion request repository in your application

private final AssertionRequestStorage assertRequestStorage = new AssertionRequestStorage();

Figure 6

Initialize relying party object

The Yubico java-webauthn-server library has the notion of a RelyingParty class. This class will help support the registration and authentication ceremonies by providing configurations such as the relying party ID, allowed origins, and much more. Below we are going to provide a walkthrough of a standard configuration, and some additional options that you can include based on your applications requirements.

Let’s start with declaring a RelyingParty object

Figure 7 demonstrates sample code to initialize a RelyingParty object.

RelyingPartyIdentity rpID = RelyingPartyIdentity.builder()
           .id("passkey.app.com")
           .name("Passkey App")
           .build()

Set<Strings> allowedOrigins = new HashSet<String>;
allowedOrigins.add("https://passkey.app.com");

private final RelyingParty rp = RelyingParty.builder()
           .identity(rpID)
           .credentialRepository(this.userStorage)
           .origins(allowedOrigins)
           .attestationConveyancePreference(Optional.of(AttestationConveyancePreference.DIRECT))
           .allowUntrustedAttestation(true)
           .validateSignatureCounter(true)
           .build();

Figure 7

The first step is to declare an ID that will be used by the relying party. This ID is used to bind credentials to a specific domain and origin, belonging to your application. This means that the credential can only be utilized within the context of the application that it was created on.

This ID will be used in the authentication and registration requests sent by the relying party under the form of an rpID. In Figure 7, the ID is declared by providing an ID that correlates to the origin of your application, along with a descriptive name of the application.

Next we will declare the credential repository to be used by the relying party. This is going to refer to the credential repository that we declared in Figure 2.

Next we will list the origins that are allowed to register with this application. This essentially means that the credential needs to have been created on one of the origins in this list.

The next two options refer to attestation. We will cover this concept in further detail later in this guide. For now what you need to understand is that during registration, a credential can include information that can be used to identify the make and model of the authenticator that it was created on.

The settings that we provide above will allow an authenticator to send attestation data, and will still allowing registrations to be completed if attestation is not provided.

Lastly we note to the relying party that we want to check the signature counter during authentication. This will help to prevent replay attacks, or mitigate risks in the case that a private key is compromised.

Now that we have initialized our application, let’s go over how to create a method to invoke and process registration requests.