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.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
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
The signup page can be accessed with http://localhost:8080/angular/signup
.
Please fill user information and register user and authentication device.
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.
Please fill the username (email address) and password on the login view and click the login button.
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.
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.
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.