Keycloak: security in the world of microservices 

Along with the upsides of microservices there also come a few new challenges, especially when it comes to security. With a microservices architecture, where many autonomous services must work together, the priority is to make sure each component is properly secured. In this article, we will discuss how microservices can be made more secure with Keycloak. We will analyse its key configuration elements and explain how to define roles, parameters and conditions in order to effectively control user access to various resources. To wrap up, we will go over selected implementation challenges.

Microservices and security

The microservices architecture has many benefits. One advantage is that the system is naturally divided into autonomous components, which are responsible for different parts of the business domain; not only are they scalable, but they can also be deployed independently. 

Microservices architecture

When a system is divided into multiple components, however, each element needs to be properly secured. This is more difficult than securing a single monolithic application. 

User verification

An important feature of microservices is their service-oriented architecture pattern, which allows the complexity of the system and the representation of business actions to be masked with a high-level API. This approach requires each service to be properly secured: you need to make sure it can only be accessed by users with relevant permissions. Access to all services should be blocked by default and only unblocked following successful user verification.

User verification answers two questions:

  • Is the user who they say they are? – Authentication
  • Does the user have permissions to access this part of the system? – Authorisation

Keycloak

Keycloak is an OAuth2 and OpenId Connect compliant server that uses JWT tokens for the purposes of user authentication and authorisation. It operates as a standalone microservice, which can be easily incorporated into your existing architecture; it will communicate with other system elements over HTTP.

Microservices architecture with Keycloak

The Keycloak configuration may be customised in the Admin Console or directly via the API. The latter is especially useful as it allows you to automate repetitive elements.

The following are the most important elements in the setup of the authentication and authorisation process:

Client

Your first step in Keycloak will be to create a client. A client is an object that represents apps acting on behalf of users that link to Keycloak to have their identity and permissions verified. In particular, a client will be used to initiate a system login. In the microservices architecture, as per the single responsibility principle, a unique client is assigned to each microservice and frontend application.

Microservices architecture with Keycloak assigned to unique client

Pay attention to correct client setup. There are three kinds of clients:

  • Public: used when the app cannot keep a secret. This affects which token request methods can be used. It is used to initialise user login through SPA and mobile apps.
  • Confidential: used when the application can securely keep a secret and use it to get a token. Most commonly, this will be an app on the server side.
  • Bearer-only: used by apps that never initialise the authentication process, i.e. are not used to get a token. These are ordinary microservices that receive, validate and pass on a token that has already been generated.

When setting up your client, you also need to decide how it will get the JWT token, i.e. authenticate users. The method needs to be compliant with the OAuth2 standard (Grant Types), for instance:

  • Standard Flow – an authentication method that supports the redirection of the user’s browser to Keycloak. Its equivalent in the OAuth2 standard is Authorisation Code Flow, which, for greater security, is often used with a PKCE extension.
  • Service Account Roles – an authentication method where a JWT token is generated using a client secret. It does not require any action from the user and is mostly used whenever two server applications need to be integrated with each other (response to system events, cyclical processes). Its OAuth2 equivalent is known as Client Credentials Grant.

The following objects are always defined in any client that stores information on user permissions (remember to enable Authorisation in client settings). 

Resource

Keycloak employs terms like Resource and Scope. Each microservice represents a business action performed on resources within a given parameter. Thanks to Scope, we can define many actions on a single resource (domain object), which reflects the way in which we design our systems.

Usually, there will be multiple business actions related to one domain object. Therefore, by dividing our business domain into resources, we can independently assign user permissions that will allow users to perform specific actions. Usually, resources are represented by nouns, which, in turn, represent objects in our business domain. Scope-type objects, on the other hand, are usually represented by verbs that define different actions performed in the business domain.

Example: assigning Resource-Scope pairs to Services:

Example: assigning Resource-Scope pairs to Services

To create Resource-Scope pairs, you can use your admin console or API: 

Creating Resource-Scope pairs with API

Once you have properly configured your resources, in the next step, you will be able to define the conditions that must be met before they can be accessed by users.

Policy

A Policy defines the conditions that Keycloak will use to grant or deny user access to a given resource. Note that a Policy object only defines a condition and is not assigned to a resource. The most important types of conditions include:

  • Role Based Policy – defines a condition based on user role:
Admin User Policy

Users can access the object if they are in an ADMIN role.

  • Client Based Policy – defines a condition based on client type:
Demo Backend Client policy

Users can access the object if their access tokens were generated with a demo-backend-client.

  • User Based Policy – defines a condition based on a specific user:
Example User Policy

Users can access the object if their login is exampleUser.

  • Aggregated Policy – allows complex conditions to be created based on other Policy objects:
Demo Aggregated Policy

Users can access the object if they meet the conditions of one of the Policies.  

Once you have created your Resources and Policies, you can aggregate them to decide what conditions must be met by users before they can get resource permissions. By linking resources to services, you will then get a system where users will need to meet specific conditions to access selected services. 

Permission

Permissions link Resource-Scope pairs to conditions, defined by Policies, which are tested during the verification of user access permissions:

User Create Permission

A Permission object linked to the USER Resource in the CREATE Scope and the following Policy objects:

  • Admin Role Policy
  • Demo Backend Client Policy
  • Example User Policy

A Permission object is configured to grant access to its linked resources when at least one of the conditions defined in the Policy objects is met (Decision strategy).

Authentication with Keycloak

Once you have configured users and their permissions, you can use Keycloak for user system login.

Let’s analyse the authentication process using Authorisation Code Flow:

Authentication process using Authorisation Code Flow

If the user is not logged in, the front-end application redirects them to the Keycloak login screen, where they will need to introduce their login and password to generate a JWT token before they can access the system. System components will use the token to verify their identity. The token is sent along with every HTTP request and validated by the microservices. If it is incorrect, a microservice will return a message: HTTP Status Unauthorised – 401.

A JWT token consists of three encoded elements:

  • Header – contains metadata that define token type and the name of the algorithm used to sign it.
  • Payload – contains data, such as login and roles, about the user for whom the token was issued, and stores useful information on the token as such.
  • Signature – generated by the key encryption of the first two elements of the JWT token with a key that is not publicly accessible. It is used to verify token correctness and make sure the token was not modified during transmission.

Authorisation with Keycloak

Once the user is authenticated, the system must decide whether to grant or deny them access to a given service linked to a specific Resource-Scope pair. Let’s analyse the authorisation process, taking user account creation as an example: 

The admin has correctly logged in to the system (got a JWT token) and tries to access the user account creation service. The service was configured to verify permissions to the USER resource in the CREATE scope.

Process of authorisation with Keycloak

After the JWT has been validated, the User Service will generate a RPT (Requesting Party Token) to download user permissions. A RPT is a JWT that has been supplemented with permission data, which can be found in the permissions field. 

{
"authorization" : {
  "permissions" : [
  {
   "rsid" : "bd1b652e-e5e2-4aa9-9df0-e4c24b831408",
   "rsname" : "USER"
  }
  {
   "rsid" : "59a64ae1-4406-4752-ac73-2f8273b035d6",
   "rsname" : "Default Resource"
  }
  ]
}
}

As you can see, the user does have permissions to access USER(resource)_CREATE(scope), so they can start any service that verifies those permissions. If the user does not have the relevant permissions, the service will return the following message: HTTP Status 403 – Forbidden.   

USER_CREATE access permissions were included in the user’s RPT, because one of the conditions defined in Policy objects pinned to the Permission object linked to USER_CREATE was met. 

Keycloak challenges

In order to use Keycloak correctly in your microservices architecture, you need to keep in mind a few things.

Efficiency

Keycloak services will be heavily used by system components to generate and validate tokens. To reduce delays to a minimum, remember about proper cache implementation (with a reasonable refresh time) and effective token validation.

Admin function security

Keycloak comes with an admin console and API, which are used for Keycloak configuration, including permission model setup. You need to secure admin functions against unauthorised access.

Availability

Keycloak will be used by all system components. If it cannot be accessed, the system will not be able to verify user identity and permissions, and the whole system will become unavailable. You need to ensure high availability.

HTTP header size

Tokens are often transmitted with HTTP headers, so try to keep the amount of information saved in the token low. If the header is too large, the HTTP request may be blocked during transfer.

Conclusion

Keycloak is an excellent tool to make user authentication and authorisation much faster. Thanks to its API, it can be added to your existing microservices architecture and permits the implementation of complex security solutions.