Skip to content

Two-factor authentication with Google Authenticator (part 1/2)

Hello all! In this post (or better to say in a series of 2 posts) we would talk about using 2 factor authentication with Vertx applications. I’ve already described, how we can enable SMS two-factor authentication in Vertx. Although, in the previous publication, I concentrated on a delivery method itself, and today we gonna dive into the subject of one-time passwords itself. So, we would start with what is an one-time password, how it is used and then would build a first half of the example app – signup part. In the next part we would continue and finish login and validation matters.

Prerequisites

In order to follow this tutorial, you should be familiar with Java 8+ and a bit with Vertx framework, as we would use concepts, like router, handlers, verticles etc. Here is a list of what you need to have installed:

  • JDK 8+
  • Maven
  • You should have a smartphone with Google Authenticator app. It is free, and you can find it both on iOS and Android

One time passwords

Before we would get our hands dirty with code, we need to clarify what is an one time password and how to use it. Remember, when you log in to any application, you usually supply an username (often it is an email or phone number) and your password. One time passwords bring an additional level of security – the user has to go through one more step to log in successfully. Take a look on the graph below:

There is a comparison of two models – without and with one time password. It is considered, that the second method is more secure, because a criminal cannot access the user’s account unless they have access to both the user’s regular password and one time password. There two main methods to “deliver” one time passwords to user – SMS and by using generator app on user’s smartphone.

We distinguish HOTP and TOTP. HOTP stands for HMAC-based One Time Password, and TOTP – for Time-Based One Time password. What is a difference between them? Well, HOTP passwords can be valid for an unknown amount of time, while the TOTP passwords keep on changing and are only valid for a short window in time. Therefore, TOTP are considered to be more secure. In this tutorial, we would stick with second one, as it is easier to demonstrate, but generally speaking, steps of implementation are same.

Set up a demo project

We need to create new Maven project first. In this tutorial we would build a classic Model-View-Controller app, but don’t worry – same goes for REST-based microservices as well. Just MVC in this tutorial would decrease some boilerplate complexity.

So, add following dependencies to your pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>net.mednikov.vertx-examples</groupId>
    <artifactId>GoogleAuthenticatorExample</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <app.version.vertx>3.8.0</app.version.vertx>
        <app.version.guice>4.2.2</app.version.guice>
        <app.version.googleauth>1.1.2</app.version.googleauth>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-core</artifactId>
            <version>${app.version.vertx}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web</artifactId>
            <version>${app.version.vertx}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web-templ-freemarker</artifactId>
            <version>${app.version.vertx}</version>
        </dependency>
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>${app.version.guice}</version>
        </dependency>
        <dependency>
            <groupId>com.warrenstrange</groupId>
            <artifactId>googleauth</artifactId>
            <version>${app.version.googleauth}</version>
        </dependency>
    </dependencies>
</project>

Also, we need to create a templates directory in the resource folder – we would put Freemarker templates there. We don’t use static files in this tutorial, all dependencies are connected via CDN in each view separately.

Signup

This section demonstrates step-by-step guide on how to implement signup process in our example app.

Step 1. Enable a router

Every Web app, built with Vertx, has HttpServer and Router. This example is not an exception. We create a new verticle, that handle both view rendering and API calls (go with /api/* prefix).

public class AuthService extends AbstractVerticle {

    @Inject private TemplateEngine templateEngine;
    @Inject private IUsersRepository usersRepository;
    @Inject private GoogleAuthenticator authenticator;

    public AuthService(AbstractModule module){
        Guice.createInjector(module).injectMembers(this);
    }

    @Override
    public void start() throws Exception {
        super.start();
        HttpServer server = vertx.createHttpServer();
        Router router = Router.router(vertx);
        router.get("/login").handler(this::showLogin);
        router.get("/signup").handler(this::showSignup);
        router.route("/api/*").handler(BodyHandler.create());
        router.post("/api/login").handler(this::doLogin);
        router.post("/api/signup").handler(this::doSignup);
        server.requestHandler(router).listen(4567, res->{
            if (res.succeeded()){
                System.out.println("Server listens to port 4567");
            } else {
                System.out.println("Something went wrong");
                System.out.println(res.cause().getLocalizedMessage());
            }
        });
    }

    private void showSignup(RoutingContext ctx){
        // ...
    }

    private void showLogin(RoutingContext ctx){
        // ..
    }

    private void doLogin(RoutingContext ctx){
        /// ...
    }

    private void doSignup(RoutingContext ctx){
        // ...
    }
}

Note, we use Google Guice to handle a dependency injection. There is a wonderful tutorial on how to use Guice with Vertx. Other steps are very straightforward – create server, routes, router and assemble all together.

There are three dependencies we use:

  1. TemplateEngine
  2. GoogleAuthenticator
  3. IUsersRepository

TemplateEngine is a Vertx-Web interface that uses a specific template and the data in a routing context to render a resource into a buffer. This an abstraction to handle server-side generation of views. In this tutorial, we use Apache Freemarker template engine. Google Authenticator is a Java server library that implements the TOTP algorithm. We use to generate keys and validate one-time codes. And finally we have a repository. It stores data; we use the most simple form – just an in-memory list, but it can be a database or storage like Redis. Take a look on a code snippet below, that demonstrates the repository interface definition:

public interface IUsersRepository {

    User add (String username, String password, String key);

    Optional<User> findByUsername (String username);
}

Then, add User model itself:

public class User {

    private String username;
    private String password;
    private String key;

    public User(String username, String password, String key){
        setKey(key);
        setUsername(username);
        setPassword(password);
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

NB!: it seems a much much better to make a model immutable, also it would not hurt to use Lombok to generate boilerplate getters/setters. This is just a tutorial, but in real-life projects, you should better to consider these options.

And as a final point – repository implementation:

public class UsersRepositoryImpl implements IUsersRepository {

    private List<User> users;

    public UsersRepositoryImpl(){
        users = new ArrayList<>();
    }

    @Override
    public User add(String username, String password, String key) {
        User user = new User(username, password, key);
        users.add(user);
        return user;
    }

    @Override
    public Optional<User> findByUsername(String username) {
        for (User user: users){
            if (user.getUsername().equalsIgnoreCase(username)){
                return Optional.of(user);
            }
        }
        return Optional.empty();
    }
}

Step 2. Signup steps

Again, as the main goal of this tutorial (series of tutorials) is to demonstrate HOTP implementation, we would not dive into things, required for real projects. Signup process is incredibly simple:

  1. Get username and password from user
  2. Send them to server as JSON (NB: you could not send these credentials insecure)
  3. Generate GoogleAuthenticator key for user
  4. Store user data in repository
  5. Return key to user

Work is divided into two parts – backend and frontend. We use (oh no!!!) JQuery library to handle API calls from frontend part. It is not required – you can do it with vanilla JS or if you use any framework – there are of course HTTP libraries. JQuery just abstracts the most creepy code and allows us to focus on logic, not on low-level HTTP call details. On backend, we’ve already created doSignup() handler. Let add logic there:

private void doSignup(RoutingContext ctx){
        // 1. Get data from view
        JsonObject req = ctx.getBodyAsJson();
        String username = req.getString("username");
        String password = req.getString("password");
        
        // 2. generate key
        GoogleAuthenticatorKey authKey = authenticator.createCredentials();
        String key = authKey.getKey();

        // 3. store data in repository
        User user  = usersRepository.add(username, password, key);

        // 4. send response to user
        JsonObject res = new JsonObject().put("key", key);
        ctx.response().setStatusCode(200).end(res.encode());
}

Step 3. Test signup

The backend part is implemented. Let use Postman to assert results:

Up and running. Now, as it is working, we need to create view and connect it with backend.

Step 4. Show a view

We use Apache Freemarker to render templates. Handler code is simple:

private void showSignup(RoutingContext ctx){
        templateEngine.render(ctx.data(), "templates/signup.ftl", res->{
            if (res.succeeded()){
                ctx.response().end(res.result());
            }
        });
}

Next, we create template. It does not use static files or webjars – everything is connected via CDNs. “Everything” means Bulma and JQuery. Well, not the best solution, but again the purpose of post – to demonstrate Google Authenticator library, not best principles of frontend development. I would make a separate tutorial series on frontend later on. Check this code below:

<!DOCTYPE html>
<html>
    <head>
        <title>Title</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script
			  src="https://code.jquery.com/jquery-3.4.1.min.js"
			  integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
			  crossorigin="anonymous"></script>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.css" integrity="sha256-ujE/ZUB6CMZmyJSgQjXGCF4sRRneOimQplBVLu8OU5w=" crossorigin="anonymous" />
    </head>
    <body>
        <div>
            <div class="columns">
                <div class="column">
                    <div class="field">
                        <label class="label">Email</label>
                        <input class="input" type="text" id="signup-username-field">
                    </div>
                    <div class="field">
                        <label class="label">Password</label>
                        <input class="input" type="password" id="signup-password-field">
                    </div>
                    <div class="field">
                        <label class="label">Repeat password</label>
                        <input class="input" type="password" id="signup-confirm-field">
                    </div>
                    <button class="button is-primary" onclick="signup()">Sign up</button>
                </div>
                <div class="column">
                    <p id="signup-code-text"></p>
                </div>
            </div>
        </div>
    </body>

    <script>
        // Here would API call
    </script>
</html>

We have username field and two fields for passwords – we would assert that user enters passwords correctly and they match. Also, we have tag, where we would supply server response with generate key. Start app and navigate to localhost:4567/signup. You should see something like this:

It is shown, but does nothing. We need to connect view and backend together.

Step 5. Connect view and backend

JQuery library is not the most cool one, but it is perfect for the purpose of this post. It offers extremely easy AJAX handling – take a look on this tutorial to learn more. Return to our view’s <script> tag and add following code inside it:

function signup(){
    // 1. get data from inputs
    var password = $("#signup-password-field").val();
    var confirm = $("#signup-confirm-field").val();
    var username = $("#signup-username-field").val();
    // 2. validate passwords
    if (password != confirm){
        alert("Passwords don't match!");
    } else {
        // 3. prepare payload
        var payload = {"username": username, "password": password}
        var data = JSON.stringify(payload);
        // 4. do call
        $.ajax({
            url: "http://localhost:4567/api/signup", 
            data: data,
            type: 'POST',
            success: function(res){
                // 5. show response
                $("#signup-code-text").text(res);
            }
        });
    }
}

First, we grab values from input fields. Then, validate that passwords match. Then we prepare payload JSON object that to send to server. Finally, we do AJAX Post call with $.ajax() method and show key to user. Run app again and check results:

We can use this key in Google Authenticator app now to generate one-time passwords.

Step 6. Set up Google Authenticator app

Run Google Authenticator on your smartphone. As I switched 100% to Android, I would demonstrate screenshots for Android, but iOS has same steps.

First, press Add button and select “Enter a provided key” (you can also improve this example to generate QR code with some API and show as image).

Save it, and you can use Google Authenticator to generate one-time passwords now:

In the next part, we would continue and finish our example and complete login and validation. Stay tuned!

Conclusion

In the first part of 2-part series we talked about one-time passwords and then implemented signup process – both on backend and frontend levels. We also configured Google Authenticator app. Next time we would continue with this topic and finish login and validation stuff. Meanwhile, I encourage you to follow my twitter andreevi_ch to be informed about my new posts. If you have questions, write me message or drop comment below. Have a nice day!

References

  • Aurelio De Rosa. How to Use jQuery’s $.ajax() for Asynchronous HTTP Requests (2015) SitePoint. read here
  • How to Use Google Authenticator – Everything You Need To Know (2019) Joy of Android, read here
  • Prakash Sharma. How Time-based One-Time Passwords work and why you should use them in your app. (2018) FreeCodeCamp. read here
Copy link
Powered by Social Snap