People have come to expect modern identity and user-management systems in the applications they use every day, and many prefer to use a single account for many apps – or use the same app and account across platforms, such as on PC and mobile.

Proper account security is crucial, however, as we’re trusting these applications with an increasing amount of our sensitive data. As a result, we’re seeing more apps provide multi-factor or passwordless authentication.

These requirements are present in most of our projects, and they are not necessarily the easiest to implement manually each time. For us, it seemed obvious that we needed some kind of out-of-the-box identity and user management solution to achieve these goals with less effort, and most importantly, a higher level of security. After some research and hands-on experience, we’ve found that Keycloak is a great solution for our needs.

What is Keycloak

Keycloak is an open-source Identity and Access Management solution. It implements an Identity Provider based on standard protocols such as OpenID and SAML and can be integrated with external (for example social) Identity Providers. Keycloak can provide a seamless single-sign-on experience for users when it is integrated with multiple applications as an IDP, users don’t have to reauthenticate themselves whenever they switch to a different application. It also offers an admin console and a UI where administrators can manage all aspects of the Keycloak server. This functionality is also available for application-level integration as a REST API. Keycloak is under the Apache 2.0 license so it’s free to use even in commercial projects. 

The following article will assume a basic understanding of the mentioned protocols especially OpenID-Connect, OAuth 2.0, standards like JWT and also algorithms such as asymmetric key cryptography. We will mainly concentrate on the Keycloak side of these standards and the integration of .NET applications to the Identity Provider. You can read more on these topics here: https://frontegg.com/guides/oidc-authentication

First we have to clarify a few Keycloak-specific concepts such as realms, users, roles and clients —the connections between which are quite important in the configuration and integration of Keycloak. 

As we see in the following diagram, on the top level it uses realms, these are completely separate tenants of the application. Every user can be a member of just one realm and can have many roles defined by Keycloak. These roles can be passed to external applications integrated with the Keycloak instance not just to authenticate but to authorise requests based on the user’s role.

Realms can have many clients as well, these usually represent external applications which can be accessed by users authenticated by Keycloak. For example, let’s say that our application has a web-based frontend and mobile client using the same backend service which uses Keycloak for authentication then usually these two client applications are defined separately in the realm.

Keycloak flow diagram - role, user, realm, client

Advantages of using Keycloak

We’ve applied a Keycloak-based solution in many of our projects in various businesses like insurance, logistics and even in the highly regulated governmental sector. In our experience, using Keycloak instead of a custom-built user-management system offers a variety of advantages:

  • It lifts the sheer effort of developing user-related functions, not only end-user facing pages like login/registration, password reset/change, but also administrative user management functions. These pages and functions can be highly customised based on application-specific needs.
  • Implementing a secure user-management system is not an easy task, with Keycloak we can avoid DIY security, which can potentially open up security leaks. It can be patched separately from our application later, when, for example, there’s an unexpected security issue.
  • Keycloak supports many external IDPs besides managing users itself. We can have a variety of users from different sources like users synchronised by LDAP protocol from an external IDP like an on-prem AD or Keycloak users who have registered themselves.
  • Since Keycloak follows the mentioned OpenID and SAML protocols, it can be easily replaced or substituted in certain environments with other IDPs such as Azure AD.
  • Keycloak’s base functionality can be extended by 3rd party modules known as extensions. Many times when we encounter something that is not part of the standard Keycloak there’s already an existing extension for that.

Deployment of Keycloak

Keycloak is a Java application which can be hosted on a physical server or a virtual machine directly. Still, the latter can be cumbersome — this is why, when we use Keycloak in our infrastructure, we usually deploy it in the form of a Docker image either using Kubernetes or some kind of cloud solution like Azure AppService, Container Instances or AKS.

Keycloak requires a persistent store to work, in production, the following relational databases are supported: MariaDB, MSSQL, MySQL, Oracle and Postgres but for testing purposes we can use a file-based storage as well. We most often use Postgres or MSSQL, Postgres is usually in a containerised form, while MSSQL is on a VM or in a separate service (such as Azure SQL).

The latest official Keycloak images can be found not at the Docker Hub but at Red Hat’s Quay.io registry: https://quay.io/repository/keycloak/keycloak

Let’s have a look at a Keycloak dockerfile intended for production environments. Here, Keycloak uses an external (not containerised) MSSQL on the same host as a persistent store.

FROM quay.io/keycloak/keycloak:20.0.3 as builder

ENV KC_DB=mssql
ENV KC_TRANSACTION_XA_ENABLED=false
ENV KC_HEALTH_ENABLED=true

WORKDIR /opt/keycloak
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:20.0.3

COPY –from=builder /opt/keycloak/ /opt/keycloak/
COPY /Imports /opt/keycloak/data/import
COPY /Theme /opt/keycloak/themes/material

ENTRYPOINT [“/opt/keycloak/bin/kc.sh”]

As we can see we’re using the multi-stage build feature of docker to have the smallest docker image as fast as possible and we’re building on top of the official Keycloak images.

During the build stage, we specify the type of database which we want to use. This is MSSQL in this case, this kind of database does not support XA global transactions which can span multiple resources so we disable those. Finally, since this is a production image, we want to expose the health endpoints.

In the final stage, we copy the specifically for MSSQL compiled application from the builder stage. Here we can apply our customisations as well. Keycloak allows a JSON-based export on its Admin Console which can include many configurations, this is quite convenient as it means that we don’t have to apply these settings each time we manually create a new environment or change something in the configuration. These configurations will be applied automatically on the container’s startup after the underlying database has been migrated if needed in a newer version. 

We also copy a custom Material-based theme so we can override the appearances of the default Keycloak login and registration pages. Almost any Keycloak page can be customised in design even the Admin Console using the FreeMarker template syntax.

Let’s see a docker-compose file using the dockerfile above.

auth:
image: “myapp-auth:${IMAGE_TAG:-latest}”
build:
context:
../src/Backend/Auth
dockerfile: Dockerfile  
environment:
KEYCLOAK_ADMIN:
“keycloak”
KEYCLOAK_ADMIN_PASSWORD: “password”
KC_PROXY: edge
KC_HOSTNAME_STRICT: false
KC_DB_URL_HOST: host.docker.internal
KC_DB_URL_PORT: 1433
KC_DB_URL_DATABASE: “MyApp.Auth”
KC_DB_URL_PROPERTIES: “;encrypt=true;trustServerCertificate=true”
KC_DB_USERNAME: “keycloak”
KC_DB_PASSWORD: “password”
command: start –optimized

Here we specify the dockerfile we want to use, and also its build context. After that we can configure the image by setting the following environmental variables:

  • KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD: these are the initial credentials of a default administrator who can access the admin console that can be changed later once the admin has logged in. 
  • KC_PROXY: we set it to edge value because we want to access the Keycloak endpoints and pages through a reverse-proxy which does TLS termination.
  • KC_HOSTNAME_STRICT: we set it to false, because our reverse proxy can verify the host headers.
  • KC_DB_URL_HOST, KC_DB_URL_PORT, KC_DB_URL_DATABASE, and KC_DB_URL_PROPERTIES: these set the properties of the database connection, in the example above, we have an MSSQL on the same host as the docker engine. Therefore we can use the host.docker.internal domain to access the host machine, we’ve also opened the 1433 port on the firewall and have an MSSQL instance listen on it. Lastly, we specify the name of the database and additional connection properties which can be database-specific.
  • KC_DB_USERNAME and KC_DB_PASSWORD: these are the credentials used to access the MSSQL database.

In this example, we’ve used credentials hard-coded in the compose file which is obviously not suitable for production. In a real environment we can use Kubernetes secrets or in a cloud environment a managed identity.

We start the Keycloak instance in production mode with the start–optimised command. We can use the start-dev command to initialise the instance in development mode, but it’s not suitable for production environments.

The configuration we have covered in this docker-compose file can easily be applied to a cloud solution. For example, we can create a new Azure AppService which runs our docker image and configure the same environmental variables and start command we have used above.

Architecture

In this section we describe a simple architecture hosted in Azure and using Keycloak as an IDP. 

As we can see in the following diagram, we have a KeyCloak instance hosted in an Azure AppService as a Docker image. In this setup, we also need an Azure Container Registry where we can push our images which will be automatically deployed to the connected AppService. Our Keycloak instance uses an Azure SQL database as a persistence layer.

Azure Keycloak architecture

We also have a backend application which acts as a protected resource server and hosts a REST API. Each incoming HTTP request is authenticated by determining whether it has an attached JWT access token issued by our trusted Keycloak instance and whether it’s signed by the Keycloak’s private key. This is achieved by the backend application accessing the signing key pair’s public part used in our Keycloak server and using it for validation.

We have two client applications, a browser-based Blazor WebAssembly and a MAUI mobile application. Both use our Keycloak to log the user in and to acquire an access token. Whenever they make an API call towards our REST API at the backend side they attach the access token. From another point of view, the backend application is also a client application because it uses the REST API of our Keycloak to perform various operations like listing users, and roles and even changing some of their settings.

Look out for part two of our exploration of Keycloak, where we’ll cover the configuration of client and server-side applications (which are also visible and connected to Keycloak on the diagram above).