1. Introduction

1.1. Web Authentication

Web Authentication is a new, secure web application authentication specification standardizing under W3C. By combining local authentication, public-key authentication, per-origin key management, it provides strong authentication to web sites against authentication process attacks like phishing. Implementation is in progress in major browsers, the specification offers excellent choices for users who place importance on security and convenience. Initially, the specification was developed as "FIDO 2.0: Web API for accessing FIDO 2.0 credentials" by the FIDO Alliance, but it has been transferred to W3C.

1.2. WebAuthn4J Spring Security

WebAuthn4J Spring Security is a Spring Security extension module to provide Web Authentication specification support for your Spring web application.

1.2.1. WebAuthn4J

WebAuthn4J Spring Security uses WebAuthn4J for WebAuthn attestation and assertion verification. WebAuthn4J is a portable Java library that supports all the attestation statements, but has minimal dependencies on external libraries.

1.2.2. Spring Security WebAuthn

There is also a sister project called Spring Security WebAuthn. Spring Security WebAuthn is developed according to Spring Security project’s policy, aiming to be merged into Spring Security upstream. WebAuthn4J Spring Security is developed as a WebAuthn4J project with the aim of maximizing the functions of the WebAuthn4J library.

1.3. Requirements

1.3.1. Language & Framework

  • Java8 or later

  • Spring Framework 5 or later

  • Spring Security 5 or later

1.3.2. Environment

  • SecureContext

  • Browsers

    • Google Chrome 70 or later

    • Mozilla Firefox 60 or later

    • Microsoft Edge bundled with Windows 10 October 2018 Update or later

    • Safari 14 or later

SecureContext

Web Authentication API is only available in SecureContext, which means only HTTPS connection is permitted while a user accessing web sites. Browsers rejects Web Authentication API call if the web site is served through HTTP connection except localhost. For more details about SecureContext, please see MDN.

1.4. Source code

Source code for this project is hosted on Github.

git clone https://github.com/webauthn4j/webauthn4j-spring-security

1.5. License

WebAuthn4J Spring Security is an open source software licensed under Apache 2.0 license.

2. Quick start

WebAuthn4J Spring Security contains a sample application which demonstrates its major functionality.

It can be launched with the following command.

./gradlew samples:spa:bootRun
login view

Sample application is documented in a dedicated chapter. Please refer Sample application.

3. Configuration

3.1. Applications integration

3.1.1. Maven dependency

Please add following to pom.xml to introduce WebAuthn4J Spring Security and its dependencies.

<properties>
  ...
  <!-- Use the latest version whenever possible. -->
  <webauthn4j-spring-security.version>0.7.6.RELEASE</webauthn4j-spring-security.version>
  ...
</properties>

<dependency>
	<groupId>com.webauthn4j</groupId>
	<artifactId>webauthn4j-spring-security-core</artifactId>
	<version>${webauthn4j-spring-security.version}</version>
</dependency>

3.1.2. Java Config

WebAuthn4J Spring Security can be configured through the Spring Security Java Config DSL. Please define the SecurityFilterChain bean as follows and apply the WebAuthnLoginConfigurer to the HttpSecurity bean. Through WebAuthnLoginConfigurer, you can set various options of the WebAuthnProcessingFilter, Attestation options endpoint, and Assertion options endpoint.

@Configuration
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {

        http.authenticationManager(authenticationManager);
        // WebAuthn Login
        http.with(WebAuthnLoginConfigurer.webAuthnLogin(), (customizer) ->{
            customizer
                    .loginPage("/login")
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .credentialIdParameter("credentialId")
                    .clientDataJSONParameter("clientDataJSON")
                    .authenticatorDataParameter("authenticatorData")
                    .signatureParameter("signature")
                    .clientExtensionsJSONParameter("clientExtensionsJSON")
                    .loginProcessingUrl("/login")
                    .attestationOptionsEndpoint()
                        .rp()
                            .name("WebAuthn4J Spring Security Sample")
                        .and()
                        .pubKeyCredParams(
                                new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.RS256), // Windows Hello
                                new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256) // FIDO U2F Key, etc
                        )
                        .extensions()
                            .credProps(true)
                    .and()
                    .assertionOptionsEndpoint()
                    .and()
                    .successHandler(authenticationSuccessHandler)
                    .failureHandler(authenticationFailureHandler);
        });
    }
}
Integrating WebAuthnAuthenticationProvider

WebAuthnAuthenticationProvider, an AuthenticationProvider for Web Authentication, need to be defined as a Bean. If you set up two-step authentication combined with password authentication, you also need a Bean definition for DaoAuthenticationProvider.

    @Bean
    public WebAuthnAuthenticationProvider webAuthnAuthenticationProvider(WebAuthnAuthenticatorService authenticatorService, WebAuthnManager webAuthnManager){
        return new WebAuthnAuthenticationProvider(authenticatorService, webAuthnManager);
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
        return daoAuthenticationProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(List<AuthenticationProvider> providers){
        return new ProviderManager(providers);
    }

3.1.3. Persistence layer integration

WebAuthn4J Spring Security looks up an authenticator through the WebAuthnCredentialRecordService interface. Please set a class implementing WebAuthnCredentialRecordService to the WebAuthnAuthenticationProvider. Speaking of Java Config, it can be set through a constructor of WebAuthnAuthenticationProviderConfigurer.

3.2. Client interface

W3C Web Authentication specification defines web browser JavaScript APIs only. It is up to implementation how to send a generated credential.

3.2.1. WebAuthn authentication request processing

Regarding WebAuthn4J Spring Security, WebAuthnProcessingFilter retrieves credentialId, clientData, authenticatorData, signature, and clientExtensionsJSON from the request sent to login processing url. credentialId, clientData, authenticatorData and signature are binary data, please send them as Base64 strings.

3.2.2. WebAuthn registration request processing

In contrast to authentication request processing, Servlet filter is not provided for registration request processing because in most cases, data other than WebAuthn like user’s first name, last name, or email address are sent at the same time.

While it is basically application’s responsibility to handle an authenticator registration process, WebAuthn4J Spring Security provides converters and validators to examine the received credential. Base64StringToCollectedClientDataConverter converts Base64 string to a CollectedClientData. Base64StringToAttestationObjectConverter converts Base64 string to a AttestationObject.

WebAuthnRegistrationRequestValidator validates an authenticator registration request.

3.2.3. Options endpoints

Web Authentication needs to obtain a challenge from the server prior to registration and authentication. When using the FIDO-U2F token as an authentication device, the CredentialIds associated with the user identified by the first authentication factor also need to be obtained from the server. To retrieve these data, WebAuthn4J Spring Security offers AttestationOptionsEndpointFilter and AssertionOptionsEndpointFilter.

3.3. Customization

3.3.1. WebAuthnProcessingFilter

WebAuthnProcessingFilter retrieves credentialId, clientData, authenticatorData, signature, and clientExtensionsJSON from the request and build WebAuthnAssertionAuthenticationToken. If credentialId does not exist, it retrieves username and password to build UsernamePasswordAuthenticationToken. To change request parameter names, configure properties of WebAuthnProcessingFilter or corresponding Java Config method of WebAuthnLoginConfigurer.

3.3.2. WebAuthnAuthenticationProvider

WebAuthnAuthenticationProvider is an AuthenticationProvider implementation to process a WebAuthnAssertionAuthenticationToken. For WebAuthn assertion verification, WebAuthnManager is used. See WebAuthn4J reference for more details of WebAuthnManager.

3.3.3. Attestation options endpoint, Assertion options endpoint

WebAuthn4J Spring Security provides AttestationOptionsEndpointFilter for WebAuthn JS Credential Creation API parameters serving, and AssertionOptionsEndpointFilter for WebAuthn JS Credential Get API parameter serving. As these Parameters generation are delegated through AttestationOptionsProvider and AssertionOptionsProvider interfaces, they can be customized by implementing these interfaces.

These can be customized through Java Config. Method chains from WebAuthnLoginConfigurer 's attestationOptionsEndpoint method or assertionOptionsEndpoint method are configuration point for that.

@Configuration
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
        http.authenticationManager(authenticationManager);
        // WebAuthn Login
        http.with(WebAuthnLoginConfigurer.webAuthnLogin(), (customizer) ->{
            customizer
            .rpId("example.com")
            .attestationOptionsEndpoint()
                .attestationOptionsProvider(attestationOptionsProvider)
                .processingUrl("/webauthn/attestation/options")
                .rp()
                    .name("example")
                    .and()
                .pubKeyCredParams(
                        new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256),
                        new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.RS1)
                )
                .authenticatorSelection()
                    .authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)
                    .residentKey(ResidentKeyRequirement.PREFERRED)
                    .userVerification(UserVerificationRequirement.PREFERRED)
                    .and()
                .attestation(AttestationConveyancePreference.DIRECT)
                .extensions()
                    .credProps(true)
                    .uvm(true)
                .and()
            .assertionOptionsEndpoint()
                .assertionOptionsProvider(assertionOptionsProvider)
                .processingUrl("/webauthn/assertion/options")
                .rpId("example.com")
                .userVerification(UserVerificationRequirement.PREFERRED)
            .and();
        });
    }
}
Dynamic generation of PublicKeyCredentialUserEntity

Attestation options endpoint can generate PublicKeyCredentialUserEntity to be returned dynamically based on the Authentication object associated with logged-in user. To generate PublicKeyCredentialUserEntity, PublicKeyCredentialUserEntityProvider is provided.

Speaking of Java Config, it can be set in this way:

@Configuration
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {

        // WebAuthn Login
        http.with(WebAuthnLoginConfigurer.webAuthnLogin(), (customizer) ->{
            customizer
            .attestationOptionsEndpoint()
                .attestationOptionsProvider(attestationOptionsProvider)
                .processingUrl("/webauthn/attestation/options")
                .processingUrl("/webauthn/attestation/options")
                .user(new MyPublicKeyCredentialUserEntityProvider()); // put your PublicKeyCredentialUserEntityProvider implementation
        });
    }
}

If PublicKeyCredentialUserEntityProvider is not set explicitly, WebAuthn4J Spring Security Java Config looks it up from Spring Application Context. Registering its bean to the application context is another way to set it.

3.3.4. Selecting authentication method

WebAuthn4J Spring Security supports "Password-less multi-factor authentication with a user-verifying authenticator", "Multi-factor authentication with password and authenticator" and "Single-factor authentication like password". If you put value on adoption, you may allow password authentication in your web system, or if you give greater importance to security, you may restrict password authentication.

How to realize password authentication

To realize "Multi-factor authentication with password and authenticator" and "Single-factor authentication like password", configure not only WebAuthnAuthenticationProvider but also DaoAuthenticationProvider to process UsernamePasswordAuthenticationToken. "Multi-factor authentication with password and authenticator" can be realized by including additional authorization requirement to check a user is authenticated by WebAuthn.

Whether it is authenticated by WebAuthn can be checked with the WebAuthnSecurityExpression#isWebAuthnAuthenticated method. Register a bean of WebAuthnSecurityExpression instance and call it from JavaConfig. WebAuthn4J Spring Security Sample MPA is a good example for it.

3.4. Advanced topics

3.4.1. Distinction of a user in the middle of multi-factor authentication

In the case where it is needed to show a different view based on authentication level, one way is to switch the view based on the type of the current Authentication instance.

@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authenticationTrustResolver.isAnonymous(authentication)) {
        return VIEW_LOGIN_LOGIN;
    } else {
        return VIEW_LOGIN_AUTHENTICATOR_LOGIN;
    }
}

3.4.2. Configuring a credential scope (rpId)

In Web Authentication specification, the scope of a creating credential can be configured through the parameter named "rpId" while creating the credential i.e. registering authenticator. "rpId" accepts effective domain. For example, in the case where the domain of the site is webauthn.example.com, and webauthn.example.com is set to rpId, the credential is only available in webauthn.example.com and its sub-domain, but if example.com is set to rpId, the scope of the credential is relaxed to example.com and its sub-domain.

WebAuthn4J Spring Security supports rpId configuration through the rpId property of ServerPropertyProviderImpl, which can be configured through WebAuthnConfigurer in JavaConfig. If you would like to change rpId dynamically based on request, set RpIdProvider.

3.4.3. Attestation statement verification

Web Authentication specification allows the relying party to retrieve an attestation statement from an authenticator if it is requested during authenticator registration. By verifying attestation statement, the relying party can exclude authenticators not conforming its security requirements. It’s to be noted that the attestation statement contains information that can be used to track user across web sites, it is discouraged to request an attestation statement unnecessarily. It is also to be noted that the browser shows an additional dialog to confirm the user consent, lowering usability. Except for enterprise applications that require strict verification of authenticators, most sites should not request attestation statements.

WebAuthnRegistrationContextValidator from WebAuthn4J validates an authenticator registration request, and it delegates attestation statement signature and trustworthiness validation to WebAuthnManager and CertPathTrustworthinessValidator interface implementation respectively.

WebAuthnRegistrationContextValidator.createNonStrictRegistrationContextValidator factory method can create the WebAuthnRegistrationContextValidator instance that contains AttestationStatementValidator and CertPathTrustworthinessValidator configured for web sites not requiring strict attestation verification.

3.4.4. TrustAnchorProvider using Spring Resource

While validating an authenticator attestation certificate path on registration, TrustAnchorCertPathTrustworthinessValidator class uses TrustAnchor retrieved through TrustAnchorProvider interface implementation. WebAuthn4J Spring Security offers KeyStoreResourceTrustAnchorProvider class, which retrieves a TrustAnchor from a Java Key Store file loaded as Spring Resource.

4. Spring Boot

Spring Boot Auto-Configuration for WebAuthn4J Spring Security is planned to be provided in the future version.

5. Sample application

WebAuthn4J Spring Security contains a sample application demonstrating its major functionalities. Sample SPA is a demo of Single Page Application. Sample MPA is a demo of traditional Multi Page Application. Sample SPA is explained below.

5.1. Sample application execution

Sample application can be executed by following command.

./gradlew samples:spa:bootRun

5.2. User and authenticator registration

signup view

The signup page can be accessed with http://localhost:8080/angular/signup. Please fill user information and register user and authentication device.

authenticator request popup

Click the "Add" button in the "Authenticators" table, it will show a pop-up asking you to give a gesture to the authenticator to acknowledge the registration. In case of an accident or device loss, Multiple authenticators can be registered for backup. If you would like to allow single-factor authentication, Please check "Allow password authentication".

5.3. User authentication

Login page can be accessed with http://localhost:8080/angular/login. Sample application supports three authentication flow.

  • Multi-factor authentication with password and authenticator

  • Password-less multi-factor authentication with a user-verifying authenticator

  • Single-factor authentication only with a password

Each of three authentication flows are explained below.

5.3.1. Multi-factor authentication with password and authenticator

If you register a non user-verifying authenticator like FIDO-U2F token, you can login with multi-factor authentication with password and authenticator.

login view

Please fill the username (email address) and password on the login view and click the login button.

authenticator login view

If the password authentication succeeds, an authenticator is asked in the next authenticator login view. When the pop-up opens, please give a gesture to the authenticator to finish the authentication. You will be automatically redirected to the dashboard.

5.3.2. Password-less multi-factor authentication with a user-verifying authenticator

If you register a user-verifying authenticator like that supports FIDO-CTAP2, you can login without password.

login view (password-less login)

Click the "Password-less login" button on the login view and the pop-up asking an authenticator will be opened. Please give a gesture to the authenticator to finish the authentication. You will be automatically redirected to the dashboard.

5.3.3. Password authentication

If you checked "Allow password authentication" at user registration, standard password authentication is available.

login view

Please fill the username (email address) and password on the login view and click the login button. If the authentication succeeds, you will be automatically redirected to the dashboard.