In this article, we're going to secure a Spring Boot application using Keycloak.

Before doing that, let's briefly recall what we have done so far.

First, we talked about the main features of Keycloak used in this series and learned how to install and boot the Keycloak server.

Then, we set Keycloak with some basic configurations to use it for securing a web application (providing it with authentication and authorization).

In this article, we're going to learn how to:

  1. Create a client in Keycloak;
  2. Set up the Spring Boot application;
  3. Define the application resources;
  4. Add access policies based on user roles.

You can check out the full source code of the demo project we're going to build on GitHub.

Let's get started!

1. Create a Client in Keycloak

The first step in securing a web application with Keycloak is creating a client. What is a client?

Clients are entities that can request authentication of a user.

In this context, a client represents a web application that wants to use Keycloak to authenticate and authorize users.

To create a new client, we need to go to Clients and then click Create.

Clients - Keycloak Admin Console

Now let's fill in the form to add a new client by providing the following information:

  • Client ID: the string used to identify the client, like app-client;
  • Client Protocol: we want to use OpenID Connect (OIDC), so we should choose openid-connect;
  • Root URL: the root URL of our web application, for example http://localhost:8080.
Keycloak window to create a new client
Add Client - Keycloak Admin Console

Once we have the client, a new page opens with further fields to configure it. Let's make sure we set the following options in this way:

  • Enabled: ON;
  • Access Type: public;
  • Standard Flow Enabled: ON.

The Access Type option defines the type of the client. As explained in the documentation, "Public access type is for client-side clients that need to perform a browser login", that is exactly what we want.

Since the access to the client will be public, for security reasons, it's essential that we restrict it by setting the redirect URIs correctly. Keycloak automatically generates them from the Root URL, but we could add more of them. The more specific we are, the better.

Moreover, it's critical to enable HTTPS on our application to encrypt the data exchanged with the server and prevent third-parties from intercept sensitive data.

The Enabled option allows turning on and off the client for requesting authentication.

The Standard Flow Enabled property is used to activate the Authorization Code Flow as defined in the OIDC standard. It's the recommended protocol to use for authenticating and authorizing browser-based applications.

I think it could be helpful illustrating the flow from the user point of view:

  1. A user visits the application on a browser and tries to access a protected resource;
  2. The application redirects the user to the Keycloak login page;
  3. The user enters their own username and password;
  4. Keycloak authenticates the user;
  5. If the authentication succeeds, Keycloak redirects the user to the protected resource of the application.

In the background, Keycloak provides the application with two tokens as defined by the OIDC protocol:

  • An Identity Token, which contains information about the logged user such as the username and the email.
  • An Access Token, digitally signed by the realm, which contains access data such as the roles assigned to the logged user.

We can also customise the look and feel of the Keycloak login page thanks to the Themes. We won't see that. Instead, in a future article, we're going to see how to use a login form internal to your application (both with Thymeleaf and AngularJS).

That's it. We have created a client that we can use to authenticate the users visiting our application.

Without further ado, let's start setting up the Spring Boot application.

2. The Demo Application

We're going to develop an application for a public library. Members will be able to browse the books available in the library. Librarians will also have the chance to manage the books.

3. Set up the Spring Boot application

Time to code!

We can make our application interact with Keycloak very easily thanks to the so-called Client Adapters.

"Keycloak client adapters are libraries that makes it very easy to secure applications and services with Keycloak."

For this project, we need the Spring Boot Adapter.

Using Gradle, we can define the required dependencies in the build.gradle file:

buildscript {
    ext {
        springBootVersion = '2.0.8.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.thomasvitale'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

ext {
    set('keycloakVersion', '4.8.3.Final')
}

dependencies {
    // Spring
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-devtools'
    
    // Keycloak
    implementation 'org.keycloak:keycloak-spring-boot-starter'
    
    // Test
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
    imports {
        mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keycloakVersion}"
    }
}

We're going to use Spring Boot 2.0.x to build our application. We set the keycloakVersion property to 4.8.3, that is the version we set up in the previous articles. Please notice that if we had to use Spring Boot 1.5.x, then we wouldn't be able to use any newer versions than 4.0.0, the last one supported for the old major version of Spring Boot.

The next step is configuring our Spring Boot application to use Keycloak. Let's open the application.properties file and write the following configuration:

keycloak.realm=public-library
keycloak.resource=app-client
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.ssl-required=external
keycloak.public-client=true

Let's quickly go over each property:

  • keycloak.realm: the name of the realm, required;
  • keycloak.resource: the client-id of the application, required;
  • keycloak.auth-server-url: the base URL of the Keycloak server, required;
  • keycloak.ssl-required: establishes if communications with the Keycloak server must happen over HTTPS. Here, it's just required for external requests (default value), but in production, we should set it to all. Optional;
  • keycloak.public-client: prevents the application from sending credentials to the Keycloak server (false is the default value). We want to set it to true whenever we use public clients instead of confidential. Optional.

4. Define the application resources

To demonstrate how Keycloak can handle authentication and authorization for a Spring Boot application, we'll define three resources:

  • /index will be freely accessible;
  • /books will be accessible only by users with normal privileges (Member role), who can browse the books available at the library;
  • /manager will be accessible only by users with administrative privileges (Librarian role), who can manage the books.

The last two resources require the users both to be authenticated and have the proper role. It's also helpful adding a fourth resource for logging out. Here's the code:

@Controller
public class ApplicationController {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private BookRepository bookRepository;

    @GetMapping(value = "/")
    public String getHome() {
        return "index";
    }

    @GetMapping(value = "/books")
    public String getBooks(Model model) {
        configCommonAttributes(model);
        model.addAttribute("books", bookRepository.readAll());
        return "books";
    }

    @GetMapping(value = "/manager")
    public String getManager(Model model) {
        configCommonAttributes(model);
        model.addAttribute("books", bookRepository.readAll());
        return "manager";
    }

    @GetMapping(value = "/logout")
    public String logout() throws ServletException {
        request.logout();
        return "redirect:/";
    }

    private void configCommonAttributes(Model model) {
        model.addAttribute("name", getKeycloakSecurityContext().getIdToken().getGivenName());
    }

    private KeycloakSecurityContext getKeycloakSecurityContext() {
        return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
    }
}

This is a normal Controller as used in every Spring MVC application. It's worth noticing how we're using the KeycloakSecurityContext to retrieve the IdToken, from which we can get the first name of the authenticated user. In a future article we're going to talk more about this object and what can we do with it.

To provide the app with some basic functionality, we're using an in-memory BookRepository class which allows read all the Book entities stored in it.

public class Book {
    private String id;
    private String title;
    private String author;

    public Book(String id, String title, String author) {
        this.id = id;
        this.title = title;
        this.author = author;
    }

    // Getters and setters
}
@Repository
public class BookRepository {
    private static Map<String, Book> books = new ConcurrentHashMap<>();

    static {
        books.put("B01", new Book("B01", "Harry Potter and the Deathly Hallows", "J.K. Rowling"));
        books.put("B02", new Book("B02", "The Lord of the Rings", "J.R.R. Tolkien"));
        books.put("B03", new Book("B03", "War and Peace", "Leo Tolstoy"));
    }

    public List<Book> readAll() {
        List<Book> allBooks = new ArrayList<>(books.values());
        allBooks.sort(Comparator.comparing(Book::getId));
        return allBooks;
    }
}

As a template engine, we're using Thymeleaf. We have a template for each resource as well as a special template to handle unauthorized requests. You can check out the full source code of this demo project on GitHub.

5. Add access policies based on user roles

So far we can run the application and navigate it freely through a browser. Even though we have correctly configured and integrated Keycloak into our application, we haven't defined yet which resources we want to protect and which privileges a user needs to access them.

It's the perfect job for Spring Security, but we'll use that in the next article. Here, to have a first look at how Keycloak works in the context of a web application, we're going to exploit the plain Spring Boot configuration.

Let's define the access policies in the application.properties file:

keycloak.securityConstraints[0].authRoles[0]=Member
keycloak.securityConstraints[0].authRoles[1]=Librarian
keycloak.securityConstraints[0].securityCollections[0].name=member resource
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/books

keycloak.securityConstraints[1].authRoles[0]=Librarian
keycloak.securityConstraints[1].securityCollections[0].name=librarian resource
keycloak.securityConstraints[1].securityCollections[0].patterns[0]=/manager

We want to define two security constraints, one for each resource to protect. For each security constraint, we set the authorization roles that a user must have to access a protected resource. Then, we specify a name and a pattern for the URL associated with the resource.

Actually, the second row is not needed. Since we defined the Librarian role as composite, it automatically has Member privileges as well.

When a user tries to access a protected resource, they will be redirected to Keycloak that authenticates them. If the authentication succeeds, Keycloak will redirect the user to the application.

At this point, the application will have received by Keycloak an IdToken with the information about the identity of the user and an Access Token containing all the information relevant to the authorization of the user, including the user roles. If they don't have the role needed to access the resource, Spring Boot will show an error page.

Conclusion

In this article, we have seen how to create a client in Keycloak and how to use it as a resource to get authentication and authorization services for a Spring Boot application.

The essential parts have been the use of the Spring Boot Adapter, and the Keycloak configuration and the access policies definition in the application.properties.

You can check out the full source code of the demo project on GitHub.

Next time, we'll use Spring Security to have more control while still relying on Keycloak. Also, we'll go over the information associated with each user and how it can be retrieved from the KeycloakSecurityContext.

Have you secured your application using Keycloak? Leave a comment and let me know about it!

Keycloak Series