Accessibility Now
Join our upcoming webinar about building accessible web applications! June 7, 2022.
Blog

Using U2F dongles for two-factor authentication

By  
Tommi Laukkanen
·
On Mar 15, 2016 6:00:00 AM
·

Two-factor authentication is common way to add an extra layer of security. One time passwords, for example time based passwords and a helper app running in a mobile phone, is one way to implement it, but requires an inconvenient user action. Similar extra layer of security can be accomplished with special hardware as well.

FIDO Alliance has created two standards to solve our password problems for good. The second factor experience (U2F standard) enables standardized hardware tokens  in form of USB sticks to be used in website logins as an additional security measure. Browser support is emerging as Chrome currently supports U2F and Firefox support is under development. Passwordless experience (UAF standard) in turn allows biometric login to websites and apps. In 2017 anyone with modern devices can press their finger on the fingerprint reader or speak to their device to sign in. 

This post is about adding U2F capability to a Vaadin application with minimal fuss.  If your application already has second factor / multi factor functionality, then U2F integration work is likely to take less than half a day. Design and implementation of well rounded MFA functionality from scratch, including administrator and end user components, will probably take from one day to one week depending on the level of ambition.

Adding dependencies

Thankfully, most of the hard lifting has been implemented in Yubico java-u2flib-server and in the the u2f-api.js. You can include the library to your pom as follows:

<dependency>
  <groupId>com.yubico</groupId>
  <artifactId>u2flib-server-core</artifactId>
  <version>0.15.0</version>
</dependency>

Implementing Vaadin JavaScript connector

Now we are ready to start coding. We will need a Vaadin JavaScript connector with double JSON encoding due to the fact that JSON encoding schemes of Vaadin and java-u2flib-server are not compatible. The connector has two functions for initiating dongle registration and authentication. 

window.org_bubblecloud_ilves_security_U2fConnector = function() {
 
  var connector = this;
 
  this.register = function(requestJson) {
      var request = JSON.parse(requestJson);
      u2f.register(request.registerRequests, request.authenticateRequests, function(data) {
          connector.onRegisterResponse(JSON.stringify(data), data.errorCode);
      });
  }

  this.authenticate = function(requestJson) {
      var request = JSON.parse(requestJson);
      u2f.sign(request.authenticateRequests, function(data) {
          connector.onAuthenticateResponse(JSON.stringify(data), data.errorCode);
      });
  }
}

The JavaScript connector’s server side part is where the magic happens. Here are, as an example, the constructor and registration related methods. 

private final U2F u2f = new U2F();
 
public U2fConnector() {
  extend(UI.getCurrent());
  addFunction("onRegisterResponse", new JavaScriptFunction() {
      @Override
      public void call(final JsonArray arguments) {
          onReqisterResponse(arguments);
      }
  });

  addFunction("onAuthenticateResponse", new JavaScriptFunction() {
      @Override
      public void call(final JsonArray arguments) {
          onAuthenticateResponse(arguments);
      }
  });
}

Starting the dongle registration and handling registration response are one-liners when you use the U2F server side API:

public void sendRegisterRequest() {
    ...
    final RegisterRequestData registerRequestData = u2f.startRegistration(
        appId, registrations, email));
    requests.put(registerRequestData.getRequestId(), registerRequestData.toJson());
    callFunction("register", registerRequestData.toJson());
    ...
}

public void onReqisterResponse(JsonArray arguments) {
    ...
    final RegisterResponse registerResponse = RegisterResponse.fromJson(
        arguments.getString(0));
    final RegisterRequestData registerRequestData = RegisterRequestData.fromJson(
        requests.remove(registerResponse.getRequestId()));
    final DeviceRegistration registration = u2f.finishRegistration(
        registerRequestData, registerResponse);
    registrations.add(registration);
    ...
}

A full solution requires, in addition, some security logic from your application. For a full example of a working solution, you can study the code at github:

https://github.com/bubblecloud/ilves/blob/master/ilves-vaadin/src/main/java/org/bubblecloud/ilves/security/U2fConnector.java

For trying out the user experience, there is a demo of the working system available here:

http://ilves.herokuapp.com/

I am happy to give you advice over email if you have questions while further securing your website with U2F.

Tommi Laukkanen
Tommi Laukkanen is a long term Vaadin fan and author of Lazy Query Container add-on.
Other posts by Tommi Laukkanen