Giter Club home page Giter Club logo

java-webauthn-server's Introduction

java-webauthn-server

Build Status Mutation test coverage Binary reproducibility

Server-side Web Authentication library for Java. Provides implementations of the Relying Party operations required for a server to support Web Authentication, including passkey authentication.

Warning
Psychic signatures in Java

In April 2022, 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 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.

Table of contents

Features

  • 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 an "attestation trust source" to verify authenticator attestations

  • Reproducible builds: release signatures match fresh builds from source. See 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.

Dependency configuration

Maven:

<dependency>
  <groupId>com.yubico</groupId>
  <artifactId>webauthn-server-core</artifactId>
  <version>2.5.2</version>
  <scope>compile</scope>
</dependency>

Gradle:

implementation("com.yubico:webauthn-server-core:2.5.2")
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 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 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 major version increase.

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:

Documentation

See the Javadoc for in-depth API documentation.

Getting started

Using this library comes in two parts: the server side and the client side. The server side involves:

  1. Implement the CredentialRepository interface with your database access logic.

  2. Instantiate the RelyingParty class.

  3. Use the RelyingParty.startRegistration(...) and RelyingParty.finishRegistration(...) methods to perform registration ceremonies.

  4. Use the RelyingParty.startAssertion(...) and RelyingParty.finishAssertion(...) methods to perform authentication ceremonies.

  5. Optionally use additional features: passkeys, passwordless multi-factor authentication, credential backup state.

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 webauthn-json library.

Example code is given below. For more detailed example usage, see webauthn-server-demo for a complete demo server.

1. Implement a CredentialRepository

The CredentialRepository interface abstracts your database in a database-agnostic way. The concrete implementation will be different for every project, but you can use InMemoryRegistrationStorage as a simple example.

2. Instantiate a RelyingParty

The RelyingParty class is the main entry point to the library. You can instantiate it using its builder methods, passing in your CredentialRepository implementation (called MyCredentialRepository here) as an argument:

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 RelyingParty.startRegistration(...).

  2. Send registration parameters to the client and call navigator.credentials.create().

  3. With cred as the result of the successfully resolved promise, call cred.getClientExtensionResults() and cred.response.getTransports() and return their results along with cred to the server.

  4. Validate the response using RelyingParty.finishRegistration(...).

  5. Update your database using the finishRegistration output.

This example uses GitHub’s webauthn-json library to do both (2) and (3) in one function call.

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

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 PublicKeyCredentialCreationOptions object in temporary storage so you can also pass it into RelyingParty.finishRegistration(...) later. If needed, you can use the toJson() and fromJson(String) methods to serialize and deserialize the value for storage.

Now call the WebAuthn API on the client side:

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:

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:

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.getSignatureCount(), // Initial signature counter value
  result.isDiscoverable(),    // Is this a passkey?
  result.isBackupEligible(),  // Can this credential be backed up (synced)?
  result.isBackedUp(),        // Is this credential currently backed up?
  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 RelyingParty.startAssertion(...).

  2. Send authentication parameters to the client, call navigator.credentials.get() and return the response.

  3. With cred as the result of the successfully resolved promise, call cred.getClientExtensionResults() and return the result along with cred to the server.

  4. Validate the response using 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 webauthn-json library to do both (2) and (3) in one function call.

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

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 AssertionRequest object in temporary storage so you can also pass it into RelyingParty.finishAssertion(...) later. If needed, you can use the toJson() and fromJson(String) methods to serialize and deserialize the value for storage.

Now call the WebAuthn API on the client side:

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:

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 AssertionResult. Most importantly, you should update the signature counter. That might look something like this:

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.getSignatureCount(),  // Set new signature counter value
  result.isBackedUp(),         // Set new backup state flag
  Clock.systemUTC().instant()  // Set time of last use (now)
);

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

5. Optional features: passkeys, multi-factor, backup state

WebAuthn supports a number of additional features beyond the basics:

Passkeys: passwordless, username-less authentication

A passkey is a WebAuthn credential that can simultaneously both identify and authenticate the user. This is also called a discoverable credential. By default, credentials are created non-discoverable, which means the server must list them in the allowCredentials parameter before the user can use them to authenticate. This is typically because the credential private key is not stored within the authenticator, but instead encoded into one of the credential IDs in allowCredentials. This way even a small hardware authenticator can have an unlimited credential capacity, but with the drawback that the user must first identify themself to the server so the server can retrieve the correct allowCredentials list.

Passkeys are instead stored within the authenticator, and also include the user’s user handle in addition to the credential ID. This way the user can be both identified and authenticated simultaneously. Many passkey-capable authenticators also offer a credential sync mechanism to allow one passkey to be used on multiple devices.

Passkeys can be created by setting the authenticatorSelection.residentKey option to REQUIRED:

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:

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

Some authenticators might create passkeys even if not required, and setting the residentKey option to PREFERRED will create a passkey if the authenticator supports it. The RegistrationResult.isDiscoverable() method can be used to determine whether the created credential is a passkey. This requires the credProps extension to be enabled, which it is by default.

User verification: passwordless multi-factor authentication

User verification can be enforced independently per authentication ceremony:

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

Then 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 authenticatorSelection.userVerification option set:

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

You can also request that user verification be used if possible, but is not required:

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

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

In this case RelyingParty.finishRegistration(...) and RelyingParty.finishAssertion(...) will NOT enforce user verification, but instead the isUserVerified() method of RegistrationResult and AssertionResult will tell whether user verification was used.

For example, you could prompt for a password as the second factor if isUserVerified() returns false:

AssertionResult result = rp.finishAssertion(/* ... */);

if (result.isSuccess()) {
    if (result.isUserVerified()) {
        return successfulLogin(result.getUsername());
    } else {
        return passwordRequired(result.getUsername());
    }
}

User verification can be used with both discoverable credentials (passkeys) and non-discoverable credentials.

Using passkeys with autofill UI

Passkeys on platform authenticators may also support the WebAuthn autofill UI, also known as "conditional mediation". This can help onboard users who are unfamiliar with a fully username-less login flow, allowing a familiar username input field to opportunistically offer a shortcut using a passkey if the user has one on their device.

This library is compatible with the autofill UI but provides no server-side options for it, because the steps to enable it are taken on the front-end side. Using autofill UI does not affect the response verification procedure.

See the guide on passkeys.dev for complete instructions on how to enable the autofill UI. In particular you need to:

  • Add the credential request option mediation: "conditional" alongside the publicKey option generated by RelyingParty.startAssertion(...),

  • Add autocomplete="username webauthn" to a username input field on the page, and

  • Call navigator.credentials.get() in the background.

If the Promise resolves, handle it like any other assertion response as described in 4. Authentication above.

Because of technical limitations, autofill UI is as of May 2023 only supported for platform credentials, i.e., passkeys stored on the user’s computing devices. Autofill UI might support passkeys on external security keys in the future.

Credential backup state

Some authenticators may allow credentials to be backed up and/or synced between devices. This capability and its current state is signaled via the Credential Backup State flags, which are available via the isBackedUp() and isBackupEligible() methods of RegistrationResult and AssertionResult. These can be used as a hint about how vulnerable a user is to authenticator loss. In particular, a user with only one credential which is not backed up may risk getting locked out if they lose their authenticator.

Migrating from version 1.x

Migrating from U2F

This section is only relevant for applications that have user credentials registered via the 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 Getting started guide above to set up WebAuthn support in general.

    Note that unlike a U2F AppID, the WebAuthn RP ID consists of only the domain name of the AppID. WebAuthn does not support U2F Trusted Facet Lists.

  2. Set the appId() setting on your RelyingParty instance. The argument to the appid() setting should be the same as you used for the appId argument to the U2F register and sign functions.

    This will enable the appid and appidExclude extensions and configure the RelyingParty to accept the given AppId when verifying authenticator signatures.

  3. Generate a 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 User Handle Contents privacy consideration.

  4. When your CredentialRepository creates a RegisteredCredential for a U2F credential, use the U2F key handle as the credential ID. If you store key handles base64 encoded, you should decode them using ByteArray.fromBase64 or ByteArray.fromBase64Url as appropriate before passing them to the RegisteredCredential.

  5. When your CredentialRepository creates a RegisteredCredential for a U2F credential, use the publicKeyEs256Raw() method instead of publicKeyCose() to set the credential public key.

  6. Replace calls to the U2F register method with calls to navigator.credentials.create() as described in Getting started.

  7. Replace calls to the U2F sign method with calls to navigator.credentials.get() as described in 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.

Example application architecture

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.

WebAuthn ceremony sequence diagram

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.

  1. 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.

  2. If the user does not already have a user handle, the application creates one in some application-specific way.

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

  4. 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.

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

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

  7. The returned list of credential IDs is used to populate the excludeCredentials or allowCredentials parameter.

  8. 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 PublicKeyCredentialCreationOptions, and for authentication ceremonies it will be a PublicKeyCredentialRequestOptions. The application stores the request in temporary storage.

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

  10. The user confirms the operation and the client returns a PublicKeyCredential object response to the application.

  11. 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.

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

  13. 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.

  14. The database adapter returns the public key.

  15. The library verifies the authentication signature.

  16. 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.

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

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

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

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

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

Using attestation

WebAuthn supports 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 webauthn-server-attestation module provides optional additional features for working with attestation. See the module documentation for more details.

Alternatively, you can use the AttestationTrustSource interface to implement your own source of attestation root certificates and set it as the attestationTrustSource for your RelyingParty instance. Note that depending on your JCA provider configuration, you may need to set the enableRevocationChecking and/or policyTreeValidator settings for compatibility with some authenticators' attestation certificates. See the JavaDoc for these settings for more information.

Building

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.

To run the tests:

$ ./gradlew check

To run the PIT mutation tests (this may take upwards of 30 minutes):

$ ./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:

$ 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 Yubico Developers site.

java-webauthn-server's People

Contributors

adamberryhuff avatar asaph avatar ashok2ashok avatar cmujurizi avatar coltnz avatar dagheyman avatar dainnilsson avatar dependabot[bot] avatar elukewalker avatar emlun avatar iaik-jheher avatar jdhoek avatar johnnyjayjay avatar jokkon avatar luisgoncalves avatar marissanishimoto avatar minisu avatar mmoayyed avatar ngtlin avatar nwilson avatar ozzi- avatar ryantenney avatar syntaxcase avatar travisspencer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

java-webauthn-server's Issues

PublicKeyCredentialCreationOptions and AssertionRequest are not serializable

For both the registration and the authentication ceremonies, the options used to to initiate them are kept server-side for the verification step. It would be very helpful if PublicKeyCredentialCreationOptions and AssertionRequest could be made to implement Serializable; that would allow developers to store instances of these classes in sessions, database, or cache without having to serialize them themselves.

Failed to parse extension data

I am using to communicate with my yubico security key and then register at the demo website on my local machine. I am using fido_cred_set_extensions(cred, 0); and is getting the below error. When I try to set extension flag to False by using fido_cred_set_extensions(cred, FIDO_OPT_FALSE), I get error fido_makecred: FIDO_ERR_UNSUPPORTED_OPTION (0x2b) . Can someone help me find the source of this issue?

 Task :webauthn-server-demo:run
10:05:59.263-0600 [main] DEBUG demo.webauthn.Config - YUBICO_WEBAUTHN_ALLOWED_ORIGINS: null
10:05:59.264-0600 [main] INFO  demo.webauthn.Config - Origins: [https://localhost:8443]
10:05:59.265-0600 [main] DEBUG demo.webauthn.Config - RP name: null
10:05:59.265-0600 [main] DEBUG demo.webauthn.Config - RP ID: null
10:05:59.265-0600 [main] DEBUG demo.webauthn.Config - RP icon: null
10:05:59.265-0600 [main] DEBUG demo.webauthn.Config - RP name not given - using default.
10:05:59.265-0600 [main] DEBUG demo.webauthn.Config - RP ID not given - using default.
10:05:59.265-0600 [main] DEBUG demo.webauthn.Config - RP icon not given - using none.
10:05:59.265-0600 [main] INFO  demo.webauthn.Config - RP identity: RelyingPartyIdentity(name=Yubico WebAuthn demo, id=localhost, icon=Optional.empty)
10:05:59.265-0600 [main] DEBUG demo.webauthn.Config - YUBICO_WEBAUTHN_U2F_APPID: null
10:05:59.267-0600 [main] DEBUG demo.webauthn.Config - U2F AppId: https://localhost:8443
10:05:59.706-0600 [main] INFO  org.eclipse.jetty.util.log - Logging initialized @581ms to org.eclipse.jetty.util.log.Slf4jLog
10:05:59.750-0600 [main] INFO  org.eclipse.jetty.server.Server - jetty-9.4.9.v20180320; built: 2018-03-20T06:21:10-06:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 1.8.0_232-8u232-b09-0ubuntu1~18.04.1-b09
Jun 05, 2020 10:05:59 AM org.glassfish.jersey.internal.inject.Providers checkProviderRuntime
WARNING: A provider demo.webauthn.WebAuthnRestResource registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider demo.webauthn.WebAuthnRestResource will be ignored. 
10:06:00.069-0600 [main] INFO  o.e.j.server.handler.ContextHandler - Started o.e.j.s.ServletContextHandler@407cf41{/,file:///home/tarun/Desktop/U2Fproject/java-webauthn-server/webauthn-server-demo/src/main/webapp/,AVAILABLE}
10:06:00.079-0600 [main] INFO  o.e.jetty.util.ssl.SslContextFactory - x509=X509@37cd92d6(serverkey,h=[],w=[]) for SslContextFactory@5922ae77[provider=null,keyStore=file:///home/tarun/Desktop/U2Fproject/java-webauthn-server/webauthn-server-demo/keystore.jks,trustStore=null]
10:06:00.106-0600 [main] INFO  o.e.jetty.server.AbstractConnector - Started ServerConnector@2d72f75e{SSL,[ssl, http/1.1]}{127.0.0.1:8443}
10:06:00.107-0600 [main] INFO  org.eclipse.jetty.server.Server - Started @982ms
10:07:00.688-0600 [qtp1561063579-12] DEBUG demo.webauthn.WebAuthnRestResource - startRegistration JSON response: {"success":true,"request":{"username":"a2","credentialNickname":"a2","requestId":"aCSTdchg8Aio40iNc7PHjqRtBVP0Zva7we3vngoc-v4","publicKeyCredentialCreationOptions":{"rp":{"name":"Yubico WebAuthn demo","id":"localhost"},"user":{"name":"a2","displayName":"a2","id":"zPJ66TIw5KXDizs3DIv24pmu5g7oRTgAVPcIP6ehACY"},"challenge":"Zd2TLJ1pkVEPimqxg0Pzgv6zLNT5n_gTNsc5QyzUIqE","pubKeyCredParams":[{"alg":-7,"type":"public-key"},{"alg":-8,"type":"public-key"},{"alg":-257,"type":"public-key"}],"excludeCredentials":[],"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},"attestation":"direct","extensions":{}},"sessionToken":"-S9B9RXwWf5JyLMXC3RjHnJ3dXsZzu1-YC2g2izLbV8"},"actions":{"finish":"https://localhost:8443/api/v1/register/finish","finishU2f":"https://localhost:8443/api/v1/register/finish-u2f"}}
10:07:02.555-0600 [qtp1561063579-27] ERROR demo.webauthn.WebAuthnServer - JSON error in finishRegistration; responseJson: {"requestId":"aCSTdchg8Aio40iNc7PHjqRtBVP0Zva7we3vngoc-v4","credential":{"type":"public-key","id":"ZdXr6LLJmSy1z8JANc_q2_PqsgRDP74l3tuDbZny1F2OmGeSO1N4zVvDXh0pB45vtK6uPc7A4_ib7-H7PVfBiw","response":{"attestationObject":"o2NmbXRmcGFja2VkaGF1dGhEYXRhWMZYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEBl1evossmZLLXPwkA1z-rb8-qyBEM_viXe24NtmfLUXY6YZ5I7U3jNW8NeHSkHjm-0rq49zsDj-Jvv4fs9V8GLpQECAyYgASFYIEfu_XVdZ4hoPuA3T483n4NM3MJw_GK75tEI_FAqdr41Ilgg8shTi12jh_uy5Jjz9UEZbIcMwvVV7CQKrCPsevlpS7BnYXR0U3RtdKNjYWxnJmNzaWdYRzBFAiEAxEAmO0Jl3beBHUMdy1zu4tvfOGV8tCrpsZpIj5FzNa4CIBpVNxOqa3QwjL7prUGMcFc6KCS6MKcTI4kK0tlW75e1Y3g1Y4FZAlMwggJPMIIBN6ADAgECAgQq2WrzMA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNVBAMTI1l1YmljbyBVMkYgUm9vdCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgwMTAwMDAwMFoYDzIwNTAwOTA0MDAwMDAwWjAxMS8wLQYDVQQDDCZZdWJpY28gVTJGIEVFIFNlcmlhbCAyMzkyNTczNDUxNjU1MDM4NzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABC_hoj6_pVs-Rh1ZpDUi15dImBy6bSiamPG9ff9lZoDbu-28K65gfm73cvV2sE1UxOXzL1lvJuYRFccnLPbKdZSjOzA5MCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4yMBMGCysGAQQBguUcAgEBBAQDAgQwMA0GCSqGSIb3DQEBCwUAA4IBAQCFavqLz08_Yl8pG8EVjjx-vSVSvPdXB1P1Eh2mpU0kzM-uJ87WqzESjCl-W1uJBd2gIBeTHx9fWSWTWVH8AEvL4grdfY0FL5VDs0lsFbgxDhDL2bsFOCdPWD6tH0USiMPqdtBwrUTlOv6o8i0fc2Jf8tWJ_jDfJmLLfLt8mWGArc-pik0BLPMTRs0RdGpYSOi-7fPjDMvZwd0iFnGyg4hh9lpFNiO1GNVWf6jwo84QXfTxOVPhFOpZ4Kfy_maIZ0MuUv1qL2T3PEjNmzjy37oseks7ESjfJtZqJPiV3aC2EYD0FE9rcHXDGKSa4ItY02rbHjBTZysXxaGffwoi8Q6U","clientDataJSON":"ewoJImNoYWxsZW5nZSIgOiAiWmQyVExKMXBrVkVQaW1xeGcwUHpndjZ6TE5UNW5fZ1ROc2M1UXl6VUlxRSIsCgkib3JpZ2luIiA6ICJodHRwczovL2xvY2FsaG9zdDo4NDQzIiwKCSJ0eXBlIiA6ICJ3ZWJhdXRobi5jcmVhdGUiCn0"},"clientExtensionResults":{}},"sessionToken":"-S9B9RXwWf5JyLMXC3RjHnJ3dXsZzu1-YC2g2izLbV8"}
com.upokecenter.cbor.CBORException: Too many bytes
        at com.upokecenter.cbor.CBORObject.CheckCBORLength(CBORObject.java:5644)
        at com.upokecenter.cbor.CBORObject.DecodeFromBytes(CBORObject.java:890)
        at com.upokecenter.cbor.CBORObject.DecodeFromBytes(CBORObject.java:766)
        at com.yubico.webauthn.data.AuthenticatorData.parseExtensions(AuthenticatorData.java:231)
        ... 84 common frames omitted
Wrapped by: java.lang.IllegalArgumentException: Failed to parse extension data
        at com.yubico.webauthn.data.AuthenticatorData.parseExtensions(AuthenticatorData.java:233)
        at com.yubico.webauthn.data.AuthenticatorData.<init>(AuthenticatorData.java:133)
        at com.yubico.webauthn.data.AttestationObject.<init>(AttestationObject.java:167)
        at com.yubico.webauthn.data.AuthenticatorAttestationResponse.<init>(AuthenticatorAttestationResponse.java:96)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:124)
        at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:283)
        ... 75 common frames omitted
Wrapped by: com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `com.yubico.webauthn.data.AuthenticatorAttestationResponse`, problem: Failed to parse extension data
 at [Source: (String)"{"requestId":"aCSTdchg8Aio40iNc7PHjqRtBVP0Zva7we3vngoc-v4","credential":{"type":"public-key","id":"ZdXr6LLJmSy1z8JANc_q2_PqsgRDP74l3tuDbZny1F2OmGeSO1N4zVvDXh0pB45vtK6uPc7A4_ib7-H7PVfBiw","response":{"attestationObject":"o2NmbXRmcGFja2VkaGF1dGhEYXRhWMZYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEBl1evossmZLLXPwkA1z-rb8-qyBEM_viXe24NtmfLUXY6YZ5I7U3jNW8NeHSkHjm-0rq49zsDj-Jvv4fs9V8GLpQECAyYgASFYIEfu_XVdZ4hoPuA3T483n4NM3MJw_GK75tEI_FAqdr41Ilgg8shTi12jh_uy5Jjz9UEZbIcMwvVV"[truncated 1228 chars]; line: 1, column: 1637] (through reference chain: demo.webauthn.data.RegistrationResponse["credential"]->com.yubico.webauthn.data.PublicKeyCredential["response"])
        at com.fasterxml.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:47)
        at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1754)
        at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.wrapAsJsonMappingException(StdValueInstantiator.java:491)
        at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:514)
        at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:285)
        at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:229)
        at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:202)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:424)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)
        at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:535)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:419)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)
        at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:535)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:419)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3434)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3402)
        at demo.webauthn.WebAuthnServer.finishRegistration(WebAuthnServer.java:325)
        at demo.webauthn.WebAuthnRestResource.finishRegistration(WebAuthnRestResource.java:187)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)
        at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)
        at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:268)
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)
        at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)
        at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)
        at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)
        at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:864)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:535)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
        at org.eclipse.jetty.server.Server.handle(Server.java:531)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:291)
        at org.eclipse.jetty.io.ssl.SslConnection$3.succeeded(SslConnection.java:151)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:319)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:175)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:133)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)
        at java.lang.Thread.run(Thread.java:748)
10:07:02.555-0600 [qtp1561063579-27] DEBUG demo.webauthn.WebAuthnRestResource - fail finishRegistration responseJson: {"requestId":"aCSTdchg8Aio40iNc7PHjqRtBVP0Zva7we3vngoc-v4","credential":{"type":"public-key","id":"ZdXr6LLJmSy1z8JANc_q2_PqsgRDP74l3tuDbZny1F2OmGeSO1N4zVvDXh0pB45vtK6uPc7A4_ib7-H7PVfBiw","response":{"attestationObject":"o2NmbXRmcGFja2VkaGF1dGhEYXRhWMZYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEBl1evossmZLLXPwkA1z-rb8-qyBEM_viXe24NtmfLUXY6YZ5I7U3jNW8NeHSkHjm-0rq49zsDj-Jvv4fs9V8GLpQECAyYgASFYIEfu_XVdZ4hoPuA3T483n4NM3MJw_GK75tEI_FAqdr41Ilgg8shTi12jh_uy5Jjz9UEZbIcMwvVV7CQKrCPsevlpS7BnYXR0U3RtdKNjYWxnJmNzaWdYRzBFAiEAxEAmO0Jl3beBHUMdy1zu4tvfOGV8tCrpsZpIj5FzNa4CIBpVNxOqa3QwjL7prUGMcFc6KCS6MKcTI4kK0tlW75e1Y3g1Y4FZAlMwggJPMIIBN6ADAgECAgQq2WrzMA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNVBAMTI1l1YmljbyBVMkYgUm9vdCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgwMTAwMDAwMFoYDzIwNTAwOTA0MDAwMDAwWjAxMS8wLQYDVQQDDCZZdWJpY28gVTJGIEVFIFNlcmlhbCAyMzkyNTczNDUxNjU1MDM4NzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABC_hoj6_pVs-Rh1ZpDUi15dImBy6bSiamPG9ff9lZoDbu-28K65gfm73cvV2sE1UxOXzL1lvJuYRFccnLPbKdZSjOzA5MCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4yMBMGCysGAQQBguUcAgEBBAQDAgQwMA0GCSqGSIb3DQEBCwUAA4IBAQCFavqLz08_Yl8pG8EVjjx-vSVSvPdXB1P1Eh2mpU0kzM-uJ87WqzESjCl-W1uJBd2gIBeTHx9fWSWTWVH8AEvL4grdfY0FL5VDs0lsFbgxDhDL2bsFOCdPWD6tH0USiMPqdtBwrUTlOv6o8i0fc2Jf8tWJ_jDfJmLLfLt8mWGArc-pik0BLPMTRs0RdGpYSOi-7fPjDMvZwd0iFnGyg4hh9lpFNiO1GNVWf6jwo84QXfTxOVPhFOpZ4Kfy_maIZ0MuUv1qL2T3PEjNmzjy37oseks7ESjfJtZqJPiV3aC2EYD0FE9rcHXDGKSa4ItY02rbHjBTZysXxaGffwoi8Q6U","clientDataJSON":"ewoJImNoYWxsZW5nZSIgOiAiWmQyVExKMXBrVkVQaW1xeGcwUHpndjZ6TE5UNW5fZ1ROc2M1UXl6VUlxRSIsCgkib3JpZ2luIiA6ICJodHRwczovL2xvY2FsaG9zdDo4NDQzIiwKCSJ0eXBlIiA6ICJ3ZWJhdXRobi5jcmVhdGUiCn0"},"clientExtensionResults":{}},"sessionToken":"-S9B9RXwWf5JyLMXC3RjHnJ3dXsZzu1-YC2g2izLbV8"}
10:07:02.555-0600 [qtp1561063579-27] DEBUG demo.webauthn.WebAuthnRestResource - Encoding messages as JSON: [Registration failed!, Failed to decode response object., Cannot construct instance of `com.yubico.webauthn.data.AuthenticatorAttestationResponse`, problem: Failed to parse extension data
 at [Source: (String)"{"requestId":"aCSTdchg8Aio40iNc7PHjqRtBVP0Zva7we3vngoc-v4","credential":{"type":"public-key","id":"ZdXr6LLJmSy1z8JANc_q2_PqsgRDP74l3tuDbZny1F2OmGeSO1N4zVvDXh0pB45vtK6uPc7A4_ib7-H7PVfBiw","response":{"attestationObject":"o2NmbXRmcGFja2VkaGF1dGhEYXRhWMZYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEBl1evossmZLLXPwkA1z-rb8-qyBEM_viXe24NtmfLUXY6YZ5I7U3jNW8NeHSkHjm-0rq49zsDj-Jvv4fs9V8GLpQECAyYgASFYIEfu_XVdZ4hoPuA3T483n4NM3MJw_GK75tEI_FAqdr41Ilgg8shTi12jh_uy5Jjz9UEZbIcMwvVV"[truncated 1228 chars]; line: 1, column: 1637] (through reference chain: demo.webauthn.data.RegistrationResponse["credential"]->com.yubico.webauthn.data.PublicKeyCredential["response"])]

NullPointerException at finishRegistration

Hi,

in my finishRegistration method I run following to build the FinishRegistrationOptions to put it in finishRegistration Method of the RelyingParty. But in the debugger the value of callerTokenBindingId of the FinishRegistrationOptions is null.

FinishRegistrationOptions finishRegistrationOptions = FinishRegistrationOptions.builder()
        .request(startResponse.getPublicKeyCredentialCreationOptions())
        .response(finishRequest.getCredential())
        .build();

When I call finishRegistration of RelyingParty with the finishRegistrationOptions above I get following exception:

java.lang.NullPointerException: null
	at com.yubico.webauthn.FinishRegistrationSteps$Step17.validate(FinishRegistrationSteps.java:622) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.next(FinishRegistrationSteps.java:115) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.FinishRegistrationSteps.run(FinishRegistrationSteps.java:89) ~[webauthn-server-core-1.6.2.jar:1.6.2]
	at com.yubico.webauthn.RelyingParty.finishRegistration(RelyingParty.java:444) ~[webauthn-server-core-1.6.2.jar:1.6.2]

My Problem is, I can't find in the documentation if I have to set this value for callerTokenBindingId and where I have to set it.

Response of startRegistration I get in client
{
  "status": "OK",
  "registrationId": "pshpETxYtI8HNlqMTB+7fw==",
  "publicKeyCredentialCreationOptions": {
    "rp": {
      "name": "Example Application",
      "id": "localhost"
    },
    "user": {
      "name": "Testname",
      "displayName": "TestDisplayName",
      "id": "AAAAAAAAADQ"
    },
    "challenge": "9XiroqI145r5KeeSy8RJBvPQ-41ALbQClQwDBQAYQJk",
    "pubKeyCredParams": [
      {
        "alg": -7,
        "type": "public-key"
      },
      {
        "alg": -8,
        "type": "public-key"
      },
      {
        "alg": -257,
        "type": "public-key"
      }
    ],
    "excludeCredentials": [],
    "attestation": "none",
    "extensions": {}
  }
}
Credential after calling create in client
{
  "type": "public-key",
  "id": "AdBKv_pk2sbNIwH3odb5i7ENY3dUvPIoSt1Z5_ZX1WB4DxQz9nlLW...",
  "rawId": "AdBKv_pk2sbNIwH3odb5i7ENY3dUvPIoSt1Z5_ZX1WB4DxQz9nlLW...",
  "response": {
    "clientDataJSON": "eyJjaGFsbGVuZ2UiOiI5WGlyb3FJMTQ1cjVLZWVTeThSSkJ2UFEtNDFB...",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjG..."
  },
  "clientExtensionResults": {}
}
Credential Response I send back to server for finishRegistration
{
  "registrationId": "pshpETxYtI8HNlqMTB+7fw==",
  "credential": {
    "type": "public-key",
    "id": "AdBKv_pk2sbNIwH3odb5i7ENY3dUvPIoSt1Z5_ZX1WB4DxQz9nlLW...",
    "rawId": "AdBKv_pk2sbNIwH3odb5i7ENY3dUvPIoSt1Z5_ZX1WB4DxQz9nlLW...",
    "response": {
      "clientDataJSON": "eyJjaGFsbGVuZ2UiOiI5WGlyb3FJMTQ1cjVLZWVTeThSSkJ2UFEtNDFB...",
      "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjG..."
    },
    "clientExtensionResults": {}
  }
}

NoClassDefFoundError Exception in UserIdentity

I have the following code to start the registration:

byte[] userIdByteArray = Longs.toByteArray(userId);

    PublicKeyCredentialCreationOptions credentialCreation = this.relyingParty
        .startRegistration(
            StartRegistrationOptions
                .builder()
                .user(
                    UserIdentity
                        .builder()
                        .name(name)
                        .displayName(displayName)
                        .id(
                            new ByteArray(userIdByteArray)
                        )
                        .build()
                )
                .build());

With the debugger I get in credentialCreation in the line with UserIdentity.builder().id(): Method threw 'java.lang.NoClassDefFoundError' exception. Cannot evaluate com.yubico.webauthn.data.UserIdentity.toString().

For the challenge in credentialCreation I get in the debugger the same error Method threw 'java.lang.NoClassDefFoundError' exception. Cannot evaluate com.yubico.webauthn.data.ByteArray.toString().

Maybe there is something wrong in my code but I can't see any differences in the documentation.

finishAssertion fails for some authenticatorData parameters

I am using the FIDO2 Conformance Tools for a server and using this library as a dependency.
When I want to make the final assertion, I call the following function :

AssertionResult result = rp.finishAssertion(FinishAssertionOptions.builder()
			.request(request)
			.response(response)
			.build());

But this does not verify some of the tests for FIDO2 Conformance Tools, they all return an error and they shouldn't :

Server-ServerAuthenticatorAssertionResponse-Resp-3 Test server processing authenticatorData

P-4 Send a valid ServerAuthenticatorAssertionResponse both authenticatorData.flags.UV and authenticatorData.flags.UP are not set, for userVerification set to "preferred", and check that server succeeds
P-6 Send a valid ServerAuthenticatorAssertionResponse with authenticatorData.flags.UV is set, despite requested userVerification set to "discouraged", and check that server succeeds
P-7 Send a valid ServerAuthenticatorAssertionResponse both authenticatorData.flags.UV and authenticatorData.flags.UP are not set, for userVerification set to "discouraged", and check that server succeeds

So, I am making this check myself on my own server, but I am not sure if it should be fixed on my side or if this is an issue of this project.

Regards,

[discuss]: refactor components of the example webapp into a new module & release

Background

As indicated here, I am in the process of adding WebAuthN support to Apereo CAS. CAS does already support MFA providers such as YubiKey and FIDO U2F with typical flows that account for device registration and authentication, and I'd like be able to use the library provided here to continue the effort for WebAuthN support.

Progress

Much of the work is already done and is based on the example web application that is available in this repository, which was extremely useful (thank you!). We have started off with v1.6.0 and hope to keep up and/or even contribute to further development of the codebase here by releasing the integration between CAS and WebAuthn and collecting feedback. In doing the work so far, here is what I have had to do:

  • Move relevant components of the example web application to a webauthn module in the CAS codebase. Abstractions such as RegistrationStorage, WebAuthnServer, SessionManager, U2fVerifier, etc and more and wired them up appropriately as Spring Beans (CAS at its core is a Spring Boot/Cloud type of application)
  • Wired in additional flows for registration and authentication as they related to CAS handling MFA flows in general, much like what it done for YubiKey, etc.
  • Moved client-side code over to the CAS webapp to handle webauthn-related matters between CAS and the browser.

All and all, this has been quite instructive and interesting.

Suggestion

My main concern is the components that are copied off of the example web application to CAS. I realize the intention for these might be to just live their life as examples and demonstrations, and one should really start with their version of the same based on available documentation, but I also think they can be quite re-useable and useful to serve as a baseline for implementations and integrations like the one I have. (i.e. I did not want to create yet again another RegistrationStorage) I would rather not maintain a separate copy of such files, keep it in sync, patch it, match and fix differences in code styles, etc. Rather, it would be much more applicable if such components were available in binary form in jar that one could, optionally, use and wire into an application.

So my suggestions are:

  • Could we extract the core components of the example webapp out into a separate module?
  • Could we, then, release that module as a separate jar along with other modules here for maximum reuse?

This way, applications such as CAS would not be forced to start from scratch (if they don't want to) and could re-use what is already worked out here and build on top of those, specially for things like support for different types of RegistrationStorage components.

If this does make sense, I am happy to start a modest refactoring process to move things into a new module and submit a pull request for review.

Thank you for your time!

Support for Face/TouchID on iOS14 - Safari

Hi,

I have a fido2-server built on this library (and this is great!), and I would like to know if there is a plan to support the apple attestation format in the next versions, or how to handle it. Here is the error I get when trying to use Face/TouchID with my server.

Failed to resolve attestation type; unknown attestation statement format: apple

This error is returned here:

throw new IllegalArgumentException("Failed to resolve attestation type; unknown attestation statement format: " + attestation.getFormat());

I don't have this issue when using the https://demo.yubico.com/playground, so I wonder how you did resolve this!

Thanks
Jules

415 error

I am trying to run the demo app. I've done ./gradlew run as well as compiling the WAR and hosting that in the newest version of Jetty. Both produce a 415 error:

HTTP/2.0 415 Unsupported Media Type
Server: Jetty(9.4.32.v20200930)
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1

for this request:

POST https://localhost:8555/api/v1/authenticate HTTP/2.0
content-length: 15
pragma: no-cache
cache-control: no-cache
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
content-type: application/x-www-form-urlencoded;charset=UTF-8
accept: */*
origin: https://localhost:8555
sec-fetch-site: same-origin
sec-fetch-mode: cors
sec-fetch-dest: empty
referer: https://localhost:8555/
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
cookie: ssm=ssm
cookie: username=eyJ1biI6InRlZGRpZSJ9###98b3a02c2ff78fc9d1e48d2e6277a1b7175ae40e569cf89da9e1cd06481931a1
host: localhost:8555

It sounds very much like this SO post where the Jackson data binding is causing the content type to be incorrectly set.

Is this only happening for me?

Does this support JDK 7, 6 and below?

Seems like couple of people mentioned about compiling on JDK 8 and above environment, but I am considering using this library on below JDK 7 (probably 6).

I know it's not optimal to use it on such legacy settings and I understand it can be exhausting to check compatibilities, but it would be awesome to know if it will work or not.

Thanks.

Missing dependencies in Maven POM v1.6.1

Hi,
Dependencies definitions are missing in the java-webauthn-server Maven POM since v1.6.1.
The previous version (1.6.0) pom was correctly defining this project's dependencies.

Evidences : compare the left block of the following pages :

The same problem seems to affect yubico-util projet.

Could you correct the project's pom ASAP ?
Thanks

Maven dependency convergence issue

Maven Enforcer will flag the yubico:webauthn package because of conflicting versions of its dependencies. This will make many pipelines out there fail without a patchwork fix.

[WARNING] 
Dependency convergence error for com.github.peteroupc:numbers:0.2.1 paths to dependency are:
 +-com.yubico:webauthn-server-core:1.3.0
   +-com.augustcellars.cose:cose-java:0.9.4
     +-com.upokecenter:cbor:2.4.1
       +-com.github.peteroupc:numbers:0.2.1
and
 +-com.yubico:webauthn-server-core:1.3.0
   +-com.augustcellars.cose:cose-java:0.9.4
     +-com.github.peteroupc:numbers:0.3.0

A potential fix would be excluding the peteroupc:numbers package from one of the modules, but I don't know much about maven, so maybe there's a better solution?

Poll: Add support for wildcard subdomain matching?

As noted in #14, the domain validation logic currently supports only a whitelist of exact origins allowed. This should cover most use cases, assuming the set of origins doesn't change often, while maintaining strict control of the permissible domains, but there may be cases where some kind of wildcard subdomain matching is preferable . We'll use this issue to track demand for such a feature.

Please add a 👍 to this post if your use case motivates preferring wildcard subdomain matching over a whitelist of exact origins.

remove class name duplication

various classes like AssertionRequest are duplicated both in the demo application and thewebauthn-server-core library. This is rather confusing. Would be great if they could be named differently and specify their purpose, or better, be removed in favor of reusable ones from the library to limit the effort to implement such a relay (currently rather high in terms of lines of code).

Add more abstraction

Current implementation has been designed for single Relying Party without Serialisation support for security purposes. As a result we cannot use it in modern clustered (which rely on serialisation for distributed processing and caching) and multi-tenant (multiple RP) environments

In order to solve that, I propose to open the library in following the open closed principle in introducing some abstraction in the overall design. Ideally a library API should expect a contract object (interface) in lieu of a final class.

Authenticator extensions {credProtect} are not a subset of requested extensions {}

Hello,
I have the following error while trying to register a resident key:

com.yubico.webauthn.exception.RegistrationFailedException: java.lang.IllegalArgumentException: Authenticator extensions {credProtect} are not a subset of requested extensions {}.
	at com.yubico.webauthn.RelyingParty.finishRegistration(RelyingParty.java:446)
...
Caused by: java.lang.IllegalArgumentException: Authenticator extensions {credProtect} are not a subset of requested extensions {}.
	at com.yubico.webauthn.ExtensionsValidation.validate(ExtensionsValidation.java:61)
	at com.yubico.webauthn.FinishRegistrationSteps$Step12.validate(FinishRegistrationSteps.java:371)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.next(FinishRegistrationSteps.java:115)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.run(FinishRegistrationSteps.java:123)
	at com.yubico.webauthn.FinishRegistrationSteps.run(FinishRegistrationSteps.java:89)
	at com.yubico.webauthn.RelyingParty.finishRegistration(RelyingParty.java:444)
	... 40 more

I have the problem with Yubikey 5.2.6 and Windows 10 2004.
I don't have the problem with neither Yubikey 5.1.2 nor Windows 10 1909.

The creation options are

{
	"attestation": "none",
	"authenticatorSelection": {
		"authenticatorAttachment": "cross-platform",
		"requireResidentKey": true,
		"userVerification": "required",
	},
	"challenge": [8, 185],
	"extensions": {},
	"pubKeyCredParams": [
		{"alg": -7, "type": "public-key"}
	],
	"rp": {
		"id": "localhost",
		"name": "xxx",
	},
	"user": {
		"displayName": "xxx",
		"id": [149, 41],
		"name": "xxx"
	}
}

The data sent back contains an extension:
image
image

Do you have any idea how to fix this behavior?

Can not deserialize com.yubico.webauthn.RegisteredCredential with jackson

I want to store CredentialRegistration of each user in database. Serializer is working as expected.
I serialize that with this code which works fine:

Collection<CredentialRegistration> registrations = ....;
JacksonCodecs.json().writeValueAsString(registrations)

It stores something like this:

[
    {
        "signatureCount": 0,
        "credential": {
            "credentialId": "Qh5671PvhA_YYyUE1rZ_oh45e2KiHXm7uQj6MthdfHj3D3DoHNNy1IEBbw7eDLhSRaf440gYeU-mW9xauPAXww",
            "userHandle": "lA_444PjkzZWA2PiTafgdfsuty2jKv44wedfgev8tPc",
            "publicKeyCose": "pQMmAQIsdfv-tZ3fkpkfsdfsdfsdfdfsdf36dgpTVqKiUAxIhjgvbn8jWv9bdsdfsdfsdfc_fND3WGvbnbvnzRpL3U",
            "signatureCount": 0
        },
        "userIdentity": {
            "name": "1",
            "displayName": "admin",
            "id": "lA_gWtPwerbnnvbnvbnrhtryhwetgefg2jvbnbn7ev8tPc"
        },
        "credentialNickname": "default",
        "username": "1",
        "registrationTime": "2020-03-14T23:15:51.444Z"
    }
]

Deserialization error

But when I attempt to deserialize it back, using code bellow:

JacksonCodecs.json().readValue(temp, new TypeReference<Collection<CredentialRegistration>>(){});

It produces this error:

Cannot construct instance of com.yubico.webauthn.RegisteredCredential (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

RegisteredCredential class is annotated using lombok but why jackson can not deserialize it?
Is there a workaround?

Yubico-util has outdated dependency

Another dependency convergence error, like in #36. This is on 1.5.0-RC1. Need to bump the version for yubico-util to 4.0.1

  [WARNING] 
  Dependency convergence error for com.upokecenter:cbor:3.6.0 paths to dependency are:
  +-MY.PROJECT
    +-com.yubico:webauthn-server-core:1.5.0-RC1
      +-com.yubico:yubico-util:1.5.0-RC1
        +-com.upokecenter:cbor:3.6.0
  and
  +-MY.PROJECT
    +-com.yubico:webauthn-server-core:1.5.0-RC1
      +-com.augustcellars.cose:cose-java:1.0.0
        +-com.upokecenter:cbor:4.0.1
  and
  +-MY.PROJECT
    +-com.yubico:webauthn-server-core:1.5.0-RC1
      +-com.upokecenter:cbor:4.0.1

A problem occurred starting process 'command 'gpg2.exe''

While checking the state of this repository, I checked out this repo and tried to execute the given command ./gradlew :webauthn-server-demo:appRun, but it failed. Even when calling ./gradlew build it fails because the gpg2.exe can't be found:

PS C:\develop\tmp\java-webauthn-server> .\gradlew build
Starting a Gradle Daemon, 2 incompatible Daemons could not be reused, use --status for details

> Task :buildSrc:compileGroovy
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/Me/.gradle/wrapper/dists/gradle-4.8-bin/divx0s2uj4thofgytb7gf9fsi/gradle-4.8/lib/groovy-all-2.4.12.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

> Task :signArchives FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':signArchives'.
> A problem occurred starting process 'command 'gpg2.exe''

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.
See https://docs.gradle.org/4.8/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 14s
2 actionable tasks: 2 executed

I'm myself publishing to Sonatype OSS, running on Windows 10, which might confuse the configured buildscript.

After changing project.ext.publishEnabled = System.env.CI != 'true' && project.hasProperty('ossrhUsername') to project.ext.publishEnabled = System.env.CI != 'true' && project.hasProperty('ossrhUsername') && false I do get a bit further.

How can I build webauthn in an offline?

Is there a way to configure and run the java-webauthn-server environment where no Internet connection is available?

I would like to know more about how to build a build environment without using gradle or maven.

Unit-Testing in Spring Boot 1

I wonder if there's any kind of testing/mock/unit-test related documentation to cover this library.
specifically in java spring boot 1

Unknown credential

Hi,

I am trying to implement a FIDO2 server and I have an issue in finishAuthentication : I am using the finishAssertion method to validate the request :

AssertionResult result = rp.finishAssertion(FinishAssertionOptions.builder().request(request).response(response).build());

but I am experimenting this error :

java.lang.IllegalArgumentException : Unknown credential: ByteArray(...)

This error seems to come from FinishAssertionSteps line 202 and this is maybe because my userHandle is empty. However I thought userHandle could be empty. How can I resolve this issue ?

Thank you for your help.
DeenOub

Gradle dependencies

Hello lovely people.

When using this library in a kotlin project managed by gradle I had to add the following dependencies to be able to call methods like ByteArray::getBytes:

    implementation("com.yubico:webauthn-server-core:1.6.1")
    implementation("com.yubico:yubico-util:1.6.1")

If this is the expected behaviour can you please mention this in the README?

InMemoryRegistrationStorage: querying by user-handle uses user-identity instead

The InMemoryRegistrationStorage component has a method to look up registrations by user handles:

    @Override
    public Collection<CredentialRegistration> getRegistrationsByUserHandle(ByteArray userHandle) {
        return storage.asMap().values().stream()
            .flatMap(Collection::stream)
            .filter(credentialRegistration ->
                userHandle.equals(credentialRegistration.getUserIdentity().getId())
            )
            .collect(Collectors.toList());
    }

What's strange here is that even though the method does imply a lookup by user-handles, leading one to suppose the query would be based on credentialRegistration.getCredential().getUserHandle(), the lookup is in fact based on credentialRegistration.getUserIdentity().getId() as the method shows.

Is this a bug where comparisons should be instead based on credentialRegistration.getUserIdentity().getId() or should the method name be changed to reflect something like getRegistrationsByUserIdentity, or am I completely off?

Authenticate doesn't work on Chrome

Steps to reproduce:

  1. Register a new account
  2. Try to authenticate

Server response:

{
  "messages": [
    "Assertion failed!",
    "java.lang.IllegalArgumentException: User handle ByteArray() does not own credential ByteArray(3dGcBiyN73KXMGzM+d26w2B4wifi5pH0qqwQp7pGhRHD1Mn8f4oJuPrigPR7akpAGnrpNiGUkjKOJ1ZzSXJ78Q==)"
  ]
}

I am using Chrome 70.0.3538.77 and verified that the issue doesn't exist on Firefox. The root cause for this is that the userHandle returned by the authenticator is an empty ArrayBuffer and Chrome evaluates this as true whereas on Firefox it is null which is evaluated as false.

The fix would be to either handle empty array buffers for userHandle in the backend or add more checks in webauthn.js to prevent sending empty array buffers in the first place.

Conformity of java-webauthn-server to FIDO2

Hi,

I am comparing this lib to other libraries specifically webauthn4j which says they are conformant to all the tests set by fido specification. Also, it says it supports all the attestation format.

Can someone please confirm if this lib is fido conformant and supports all the attestation formats?

Thanks

allowUntrustedAttestation = true doesn't seem to do anything

My rp instantiation is as follows...

        rp = RelyingParty.builder()
            .identity(rpId)
            .credentialRepository(this.userStorage)
            .origins(Sets.newHashSet(baseUrl))
            .attestationConveyancePreference(Optional.of(AttestationConveyancePreference.DIRECT))
            .metadataService(Optional.empty())
            .allowUnrequestedExtensions(true)
            .allowUntrustedAttestation(true)
            .validateSignatureCounter(true)
            .build();

When I attempt rp.FinishRegistration() I get the following exception which I wouldn't expect since it should allow untrusted attestation. I also get failures with TPM attestation (which I know isn't supported yet but would equally expect it to ignore as well).

com.yubico.webauthn.exception.RegistrationFailedException: java.lang.IllegalArgumentException: Failed to obtain attestation trust anchors.

Invalid attestation signature

Hi,

I'm working on integrating WebAuthn to our system and I've hit an issue with incorrect "Invalid attestation signature" step 14 of [1] . Debugging gives me a weird value of clientDataJsonHash

The raw value of clientDataJSON is
eyJjaGFsbGVuZ2UiOiJYdDhuajNqQkJid1gtaWlsUGZpTEx5UFc4Z2NneEZZcU1xS0plME9mQTR3Iiwib3JpZ2luIjoibG9jYWxob3N0IiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9

but the hash result is WL6RneVupE03jwO5j8AZiGlj_scPVJ-b34C0b9pOVks

which leads to invalid signature verification. I tried to compute a SHA-256 of the raw value [2], then base64 encode, and I saw a different value.

[1]


[2] https://emn178.github.io/online-tools/sha256.html

Can you please explain that to me? Thanks

clientExtensionResults mandatory?

The method PublicKeyCredential.parseRegistrationResponseJson(responseJson) fails if the responseJson doesn't contain clientExtensionResults with error:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.yubico.webauthn.data.PublicKeyCredential, problem: clientExtensionResults is marked @NonNull but is null

Is this desired behaviour? My understanding from this documentation is that clientExtensionResults should be optional.

Thank you

[U2F] YubiKey 5 NFC - metadata not resolvable

Hi

Problem Description:

When using a YubiKey 5 NFC token the ExtensionMapper is not able to match any of the certificate extensions.

attestation-certificate:

Version: 3 (0x2)
Serial Number: 1573932769 (0x5dd04ee1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Yubico U2F Root CA Serial 457200631
Validity
Not Before: 2014-08-01T00:00:00
Not After: 2050-09-04T00:00:00
Subject: C=SE,O=Yubico AB,OU=Authenticator Attestation,CN=Yubico U2F EE Serial 1573932769
Subject Public Key Info:
Public-Key: (256 bit)
pub:

04:23:69:e0:0b:3e:51:19:d6:a9:b4:d1:87:be:b4:41:80:cd:
ac:71:ea:52:98:81:e3:e7:7d:1c:f5:81:a1:3b:67:31:2b:a9:
32:86:35:9d:c3:48:0d:81:13:d6:5e:14:14:ec:e6:d1:0e:bb:
2d:1e:c8:dd:9a:3f:02:fd:5c:80:d4

Curve: secp256r1
X509v3 extensions:
1.3.6.1.4.1.41482.2 (U2F Device identifier):
Hex value: 312e332e362e312e342e312e34313438322e312e37
1.3.6.1.4.1.41482.1.7 (YubiKey 5)
1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid):
AAGUID: 2fc0579f811347eab116bb5a8db9202a
1.3.6.1.4.1.45724.2.1.1 (fidoU2FTransports):
Hex value: 03020430
Transports: USB NFC
X509v3 Basic Constraints:
CA: FALSE
Signature Algorithm: sha256WithRSAEncryption

Possible Solution:

update metadata.json to add the following selector to the YubiKey 5 NFC entry

{
  "type": "x509Extension",
  "parameters": {
    "key": "1.3.6.1.4.1.45724.1.1.4",
    "value": {
      "type": "hex",
      "value": "2fc0579f811347eab116bb5a8db9202a"
    }
  }
}

Problems with origin matching.

I was building a simple app to see how the library works and I ran into an issue with the library refusing to allow through a request with the origin of https://localhost:8080 when the rpID was set to localhost. Not sure if this is intended behaviour.

I had to add the exact origin to RelyingParty.origins, which doesn't seem like a good solution, especially considering that this has to always be set for local development.

Perhaps a more lax matching system for the origin URIs?

I'd put this under #15, but this feels like a separate issue.

Let me know if I'm missing something obvious here.

Wrong userHandle using multiple registrations on the same username

Hi,

I am using java-webauthn-server library for website login.
(browser - webserver - java-webauthn-server - my-registration-store)
My implementation of the registration store saves the username, crentialid and userhandle in a database table.

Everything works fine as long as the username is unique.
However, I need to register the same username with multiple yubico tokens to have a backup in case one of the tokens is lost or damaged.

Having multiple entries for the same username in the registration store, authentication finish may fail:

java.lang.IllegalArgumentException : Unknown credential: ByteArray(...)

or

java.lang.IllegalArgumentException: User handle ByteArray(H1L9...=) does not own credential ByteArray(7Uur...=)"

In fact, this message reveals the problem: during validation, the credential from registration 1 is combined with userhandle of registration 2 which must fail.

In FinishAssertion.step0, the userHandle() is fetched from the request:

private Optional<ByteArray> userHandle() { return response.getResponse().getUserHandle() .map(Optional::of) .orElseGet(() -> credentialRepository.getUserHandleForUsername(request.getUsername().get())); }

Since the userHandle in the response is empty, the methods tries to fetch the userhandle for the username from the repository - and picking the wrong one (2 or mory entries for the same username)

The root cause maybe the userHandle is empty in the request. However, although a filled/valid userHandle was created (and sent to the brower) during registration, this userhandle is never sent via authentication...

Is it safe to fetch the userHandle using the credentialId which is unique in constrast to the username?
If yes, how to change the bevavior of the library?

Kind regards

Does not compile using JDK 10 or above

Same as the older u2f-project (Yubico/java-u2flib-server#38), this webauthn-server does not compile using JDK 10 or JDK 11.

This is mostly because the used Scala fragments are not yet supporting these JDK-versions:
scala/scala-dev#559
scala/scala#7218

This is even documented on their website: https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html#jdk-9--up-compatibility-notes

As Webauthn is in Rec-state (https://www.w3.org/TR/webauthn/), upcoming developers might want to use a more up2date JDK, mostly because Java 8 support will get dropped in 2019 from Oracle (https://www.oracle.com/technetwork/java/eol-135779.html), and 2023 from Redhat (https://access.redhat.com/articles/1299013).

Maybe somewhere should be stated, why some parts are made in Scala and other parts are not, just to state the design decision a bit more (or releasing some plan which parts might get reworked on).

I do know that this project says it's work in progress, but just wanted to have this issue addressed so it can get referenced (and for other developers being stated to now work with these JDKs).

License?

Any chance for a more permissive license? Like MIT?

WebAuthN server demo: display-name for registration is not optional

I am trying out the webauthn-server-demo from the latest tip of the master branch as of today, (no local changes made; simply ran gradlew run) and I'm trying to test out the registration flow using the "Resident Credential" option with a YubiKey 5.

Here is what I see:

  1. Provide a username in the UI
  2. Click "Register new account with resident credential"
  3. Browser prompts to set up a PIN
  4. I provide a PIN and confirmation PIN and click next.
  5. See the error "DOMException: The operation either timed out or was not allowed. See: https://w3c.github.io/webauthn/#sec-assertion-privacy." in the console.

(The link also appears to point to a non-existing anchor tag and/or section)

At this point and once the pin is set up, I always see the error message above when I attempt register using that option. Tried the same scenario with Chrome, Firefox and Brave, all of which lead to the same error. The same error also pops up after resetting/removing the pin. The "Resident Credential" option does seem to work fine on webauthn.io

I suspect I might be missing something pretty obvious. How best can I troubleshoot this?

PS The registration/authentication flow without a resident credential option works fine, on Chrome, Firefox and Brave.

Use of EC Keys produces - Point does not match field size

I am using EC keys to register and I am getting the following error intermittently
This is not happening every time

Failed to parse public key from attestation data
at com.yubico.internal.util.ExceptionUtil.wrapAndLog(ExceptionUtil.java:34)
	at com.yubico.webauthn.PackedAttestationStatementVerifier.verifySelfAttestationSignature(PackedAttestationStatementVerifier.java:105)
	at com.yubico.webauthn.PackedAttestationStatementVerifier.verifyAttestationSignature(PackedAttestationStatementVerifier.java:90)
	at com.yubico.webauthn.FinishRegistrationSteps$Step14.lambda$validate$0(FinishRegistrationSteps.java:436)
	at java.base/java.util.Optional.ifPresent(Optional.java:183)
	at com.yubico.webauthn.FinishRegistrationSteps$Step14.validate(FinishRegistrationSteps.java:434)
	at com.yubico.webauthn.FinishRegistrationSteps$Step.next(FinishRegistrationSteps.java:115)

....


Caused by: COSE.CoseException: Internal error on SPKI
	at COSE.OneKey.CheckECKey(OneKey.java:412)
	at COSE.OneKey.CheckKeyState(OneKey.java:307)
	at COSE.OneKey.<init>(OneKey.java:59)
	at com.yubico.webauthn.WebAuthnCodecs.importCoseP256PublicKey(WebAuthnCodecs.java:93)
	at com.yubico.webauthn.WebAuthnCodecs.importCosePublicKey(WebAuthnCodecs.java:77)
	at com.yubico.webauthn.PackedAttestationStatementVerifier.verifySelfAttestationSignature(PackedAttestationStatementVerifier.java:101)
	... 86 more
Caused by: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Invalid EC key
	at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePublic(ECKeyFactory.java:157)
	at java.base/java.security.KeyFactory.generatePublic(KeyFactory.java:352)
	at COSE.OneKey.CheckECKey(OneKey.java:406)
	... 91 more
Caused by: java.security.InvalidKeyException: Invalid EC key
	at jdk.crypto.ec/sun.security.ec.ECPublicKeyImpl.parseKeyBits(ECPublicKeyImpl.java:111)
	at java.base/sun.security.x509.X509Key.decode(X509Key.java:390)
	at java.base/sun.security.x509.X509Key.decode(X509Key.java:401)
	at jdk.crypto.ec/sun.security.ec.ECPublicKeyImpl.<init>(ECPublicKeyImpl.java:71)
	at jdk.crypto.ec/sun.security.ec.ECKeyFactory.implGeneratePublic(ECKeyFactory.java:219)
	at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePublic(ECKeyFactory.java:153)
	... 93 more
Caused by: java.io.IOException: Point does not match field size
	at java.base/sun.security.util.ECUtil.decodePoint(ECUtil.java:48)
	at jdk.crypto.ec/sun.security.ec.ECPublicKeyImpl.parseKeyBits(ECPublicKeyImpl.java:109)
	... 98 more


Invalid attestation signature.

Hello.

I'm trying to register a security key NFC from yubico in a demo application I'm implementing, but I'm having some trouble while trying to register it.
In my client side I construct the object to send to the server for registration like this (publicKeyCredential is the object I get from executing navigator.credentials.create):

const decodedClientDataJSON=utf8Decoder.decode(publicKeyCredential.response.clientDataJSON);
const parsedClientDataJSON=JSON.parse(decodedClientDataJSON);
parsedClientDataJSON.challenge=atob(parsedClientDataJSON.challenge);

var newClientDataJSON=utf8Encoder.encode(JSON.stringify(parsedClientDataJSON));

var newResponse={
            attestationObject: this.mimeBase64ToUrl(base64js.fromByteArray(this.ensureUint8Array(publicKeyCredential.response.attestationObject))),
            clientDataJSON: this.mimeBase64ToUrl(base64js.fromByteArray(this.ensureUint8Array(newClientDataJSON)))
        };

let bodyJSON=JSON.stringify({
            requestId: this.state.requestId,
            credential: {
                type: publicKeyCredential.type,
                id: publicKeyCredential.id,
                response: newResponse,
                clientExtensionResults: publicKeyCredential.getClientExtensionResults()
            }
        });

The function mimeBase64ToUrl is the one present in the demo of this repo.
I send this to the server for registration but I'm getting
Invalid attestation signature.

I see in https://github.com/Yubico/java-webauthn-server/blob/master/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishRegistrationSteps.java that the verifier.verifyAttestationSignature(attestation, clientDataJsonHash) fails, but is this caused by the way I encode clientDataJSON here on the client side?

I also saw #6 but I'm enconding clientDataJSON in Base64.

Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.