Identity Management, One-Time-Passwords and Two-Factor-Auth with Spring Boot and Keycloak

November 26th, 2017 by

Communicating with  identity and access management systems is a common task for many web-applications exposing secured resources.

Keycloak is an open source software that provides not also such authorization services but also offers a lot of features from Single-Sign-On, Identity-Brokering, Social-Login, User-Federation, multiple client-adapters up to the administration console or support for protocols like OpenID, SAML, OAuth2, Kerberos and more.

I will demonstrate how to integrate a Spring Boot web application with Keycloak and configure an authentication flow that requires a two-factor-authentication with user credentials and also one-time-passwords.

Keycloak - OTP Key Configuration

Keycloak - OTP Key Configuration

 

What we’re going to build

In the following tutorial we will install a fresh new Keycloak server instance and configure realms, roles, users as well as one-time-password (OTP) authentication step by step.

Afterwards we’ll implement a web application with Spring Boot that exposes some secured resources to authenticated users within a specific role and communicates with our Keycloak server instance to authenticate users.

Installing and Starting Keycloak

Keycloak is easy to install, either by downloading the binaries from the Keycloak website or using Docker.

Manual installation

Download the binaries from the Keycloak website and run the following command to start the server:

$ sh ./bin/standalone.sh

Using Docker

Simply use the Docker image jboss/keycloak from Docker Hub here:

$ docker run jboss/keycloak

We should be able now to access our Keycloak server by pointing our browser to the address http://localhost:8080/auth.

Further details can be found in the excellent documentation on the Keycloak website here: “Server Installation and Configuration“.

If not interested in the details of the Keycloak configuration, please skip to the Spring Boot section of this tutorial.

Keycloak Setup

Now that we’ve got a running server we need to add some configuration like realms, users, roles and security configuration..

Creating the Administration Account

When running Keycloak for the first time, we’re required to create an administration account and set a password for this administrator.

Then we’re able to log into the master-realm at http://localhost:8080/auth/admin/ using these credentials.

Creating a new Realm

We’re creating a new realm that will be used for our web-application named “tutorial“.

Keycloak - Creating a new realm

Keycloak - Creating a new realm

Creating a new Client

In the next step, we’re creating a new client named “hascode-tutorial-app“.

Keycloak - Creating a new client

Keycloak - Creating a new client

In the client configuration screen we now need to specify a URL pattern for Valid Redirect URLs that we’re setting to http://localhost:8081/* (we’ll be running our web-application at this address and port later..):

Keycloak - Configure valid redirect URL patterns

Keycloak - Configure valid redirect URL patterns

Creating a new Role

Create new role named “user“. In our Spring Boot application we’ll be restricting access to a specific page to users with that role.

Keycloak - Creating a new role

Keycloak - Creating a new role

Creating a new User with Credentials and Role Assignments

We’re now creating a new user with username “lisa“, for now we don’t need to add any other information here.

Keycloak - Creating new user

Keycloak - Creating new user

In the user credentials screen we’re adding a new password of choice for the user and we’re disabling the setting for temporary credentials as we do not want our user to change his password on first login.

Keycloak - Setting user credentials

Keycloak - Setting user credentials

Finally in the tab named “Role Mappings” we’re assigning our user “lisa” to the role “user“.

Keycloak - Assigning role to user

Keycloak - Assigning role to user

Now we’re done with the basic configuration and just need to fine-tune the settings for one-time-passwords.

One-Time-Password (OTP) Configuration

In our realm configuration in the “Authentication” settings we’re modifying our “Flows” configuration and we’re setting the value of  OTP Form to “required“.

Keycloak - Configuring OTP Form Requirement

Keycloak - Configuring OTP Form Requirement

In the tab “OTP Policy” we’re updating the number of digits for our OTP code to 8 digits.

Keycloak - Configuring OTP Digit Size

Keycloak - Configuring OTP Digit Size

Installing an OTP Client

Now we need and OTP client that acts as a key generator and may be installed on our smartphone.

Three apps for Android that I know are..

Most times I am using FreeOTP and I have tested the Keycloak interaction with this app.

Testing OTP Secured Access

Having all we need for now, we may now start a manual test of our security strategy.

We’re accessing the account view of our “tutorial” realm by accessing the following URL in our browser: http://localhost:8080/auth/realms/tutorial/account/.

In the screen, we’re entering our credentials “lisa” as username and our password.

Keycloak Login Screen

Keycloak Login Screen

In the following screen we’re required to setup our OTP key generator.

This is done by simply scanning the provided QR code with our OTP app and entering the generated code.

Keycloak - OTP Key Configuration

Keycloak - OTP Key Configuration

We should then be redirected to the Keycloak account configuration.

Keycloak - Access account details

Keycloak - Access account details

As we’re now sure that everything is working as expected on the Keycloak side, we’re ready to implement a connected web application.

Creating a secured Spring Boot Web Application

We’ll be implementing two different solutions with Spring Boot: one simple application using only spring-boot web and another one using spring-security.

There is an excellent article available in the RedHat Developer Blog (as many others) that describes in-detail how to configure Spring Boot with Keycloak and I highly recommend reading it.

In addition there is a dedicated chapter in the Keycloak documentation about the Spring Boot Adapter.

Project Setup

We’re creating a new Spring Boot application (using Spring Initializr) with the following additional (to the parent pom) dependencies:

  • spring-boot-starter-web
  • keycloak-spring-boot-starter
  • spring-boot-starter-thymeleaf

So our shortened pom.xml looks similar to this one:

<project>
    [..]
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.8.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
 
    [..]
 
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.keycloak</groupId>
			<artifactId>keycloak-spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
	</dependencies>
 
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.keycloak.bom</groupId>
				<artifactId>keycloak-adapter-bom</artifactId>
				<version>${keycloak.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
    [..]
</project>

The complete descriptor file is available in my repository here.

Controller

In our controller class, we’re defining a data-transfer-object for articles and two actions to render a list of blog articles and to logoff a user.

To logout a user we’re accepting the HttpServletRequest as method-parameter and use its logout method.

package com.hascode.tutorial;
 
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
 
@Controller
class ArticleController {
 
  static class Article {
 
    private String title;
    private String url;
 
    public Article(String title, String url) {
      this.title = title;
      this.url = url;
    }
 
    public String getTitle() {
      return title;
    }
 
    public String getUrl() {
      return url;
    }
  }
 
  @GetMapping(path = "/articles")
  public String getProducts(Model model) {
    model.addAttribute("articles", Arrays.asList(
        new Article("Microbenchmarks with JMH / Java Microbenchmark Harness",
            "https://www.hascode.com/2017/10/microbenchmarks-with-jmh-java-microbenchmark-harness/"),
        new Article(
            "Resilient Architecture in Practice – Circuit Breakers for Java: Failsafe, Javaslang, Hystrix and Vert.x",
            "https://www.hascode.com/2017/02/resilient-architecture-circuit-breakers-for-java-hystrix-vert-x-javaslang-and-failsafe-examples/"),
        new Article("Assuring Architectural Rules with ArchUnit",
            "https://www.hascode.com/2017/07/assuring-architectural-rules-with-archunit/")));
    return "articles";
  }
 
  @GetMapping(path = "/logout")
  public String logout(HttpServletRequest request) throws ServletException {
    request.logout();
    return "logout";
  }
}

Application

Our application class contains the usual minimalistic Spring Boot setup, nothing special.

package com.hascode.tutorial;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class ArticleApplication {
 
	public static void main(String[] args) {
		SpringApplication.run(ArticleApplication.class, args);
	}
}

Static Start Page

This is the static HTML file that our application serves as the start page:

<html>
<head>
  <title>hasCode.com Sample Spring Boot App</title>
</head>
<body>
<h1>hasCode.com Sample Spring Boot App</h1>
<a href="/articles">Articles (requires authentication)</a>
</body>
</html>

Thymeleaf Articles Template

This is our Thymeleaf template to display a list of given blog articles.

More detailed information about the Thymeleaf template engine syntax can be found in its documentation.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>hasCode.com Articles</title></head>
<body>
<h1>hasCode.com Articles</h1>
<ul>
  <li th:each="article : ${articles}">
    <a th:href="${article.url}" th:text="${article.title}"/>
  </li>
</ul>
 
<p>
  <a href="/logout">Logout</a>
</p>
</body>
</html>

Spring Configuration

Finally we need to tell our application how to address Keycloak, especially by adding the following entries to our applications application.properties:

  • keycloak.auth-server-url: The address of our Keycloak server
  • keycloak.realm: The selected realm’s name
  • keycloak.public-client: Public client setting
  • keycloak.resource: Name of the resource
  • keycloak.security-constraints[0].authRoles[0]: Name of the required role
  • keycloak.security-constraints[0].securityCollections[0].patterns[0]: Pattern of the secured resource

In addition to the settings above, we’re adding a server.port entry set to 8081 because port 8080 is already used by Keycloak.

So our final config file should look similar  to this one:

keycloak.auth-server-url=http://localhost:8080/auth
keycloak.realm=tutorial
keycloak.public-client=true
keycloak.resource=hascode-tutorial-app
 
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/articles/*
 
server.port=8081

Running the Application

Finally we’re ready to start our application and find out if the integration with Keycloak is working as expected.

We’re starting our application by running the following command in the command-line:

$ mvn clean spring-boot:run

Afterwards we should be able to access our application in the browser by pointing it at http://localhost:8081/.

We should see the start-page as shown in the following screenshot:

Spring Boot Application - Start Page

Spring Boot Application - Start Page

Clicking on “Articles” redirects us to the Keycloak Login Screen, where we’re entering our credentials and then are asked to enter our OTP code from the app:

Keycloak - OTP Entry

Keycloak - OTP Entry

With valid credentials we’re now redirected to our application’s articles overview:

Spring Boot Application - Articles overview

Spring Boot Application - Articles overview

The logout action terminates our authentication and we’re redirected to the logout page.

Alternative with Spring Security

In a real production app, there is often Spring Security involved that’s why we’re modifying our existing application to work with Spring Security now.

To make it work we only need to apply one dependency, modify our configuration file and add a configuration class to the application.

Dependencies

First of all to address Spring Security we need to add its dependency to our project by adding the following entry to our project’s pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Spring Configuration

In the next step we may remove the following two entries from our application.properties config file as we’ll be handling necessary roles and secured URL schemes with Spring Security mechanisms in our security configuration class.

keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/articles/*

Security Configuration

Last but not least we’re adding the following security configuration class to our application.

Here we’re exposing instances of KeycloakConfigResolver and SessionAuthenticationStrategy for injection and we’re adding global security configuration as well as role-restrictions for our articles view.

package com.hascode.tutorial;
 
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
 
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
 
  @Bean
  public KeycloakConfigResolver KeycloakConfigResolver() {
    return new KeycloakSpringBootConfigResolver();
  }
 
  @Bean
  @Override
  protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
    return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
  }
 
  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
    keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
    auth.authenticationProvider(keycloakAuthenticationProvider);
  }
 
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    super.configure(http);
    http
        .authorizeRequests()
        .antMatchers("/articles*").hasRole("user")
        .anyRequest().permitAll();
  }
}

That’s all that we needed to change and when re-running our application we should be able to run the same authentication flow as in our simple version.

Disabling the Keycloak Spring Boot Adapter

It’s easy to disable the Keycloak adapter e.g. for testing purpose by adding the following item to our Spring configuration (application.properties):

keycloak.enabled = false

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/keycloak-springboot-otp.git

Resources

OAuth2 Identity/Resources Server with Spring Boot

When interested in implementing identity as well as resource servers with Spring Boot, please feel free to visit the following article of mine: “Setting up an OAuth2 Authorization Server and Resource Provider with Spring Boot“.

Tags: , , , , , , , , , , , , , , , , , , ,

Search
Categories