DEV Community

Ed Legaspi
Ed Legaspi

Posted on • Updated on • Originally published at czetsuyatech.com

How to Configure Multitenancy in Keycloak with Spring API Gateway Integration

1. Introduction

This article aims to demonstrate how multi-tenancy can be achieved in a microservice using Keycloak and Spring with Spring API Gateway in front of the services.

Spring API Gateway brings an out-of-the-box flexible routing with security, resilience, and monitoring management. How does Keycloak validate realm information and forward it to microservices?

In the example microservice project, the tenant configuration will be loaded based on the Keycloak-Realm header information to authenticate and authorize a user correctly.

2. The Problem

In every software application, there is a need to create and manage users. We will design a software architecture where the client request is handled by an API Gateway and routed to the user service.

Use Case Diagram.

Image description

Sequence Diagram

Image description

3. Keycloak

Keycloak is an open-source identity and access management platform. It provides out-of-the-box features such as authentication and authorization.

For this exercise, we will use a custom Keycloak server from https://github.com/czetsuyatech/ct-keycloak-iam. This project downloads a specific version of the Keycloak server and builds customizations such as theme, provider, realm, and more.

This exercise needs two tenants to demonstrate multi-tenancy, and thus the need to create two Keycloak realms.

4. Microservice Project in Spring

Create a Spring service project and name it user-services. It will have one controller, UserController, and one endpoint that returns the information of the current log user, depending on which tenant the user belongs to.

In the getUserInfo method below, we are getting the Keycloak token from the security context and getting the principal. We can then typecast the object into KeycloakPrincipal, where we can get the accessToken and the user's primary attributes, such as first and last name.

@GetMapping("/user-info")
public String userInfo() {
  return getUserInfo();
}

@SuppressWarnings("unchecked")
private String getUserInfo() {

  KeycloakAuthenticationToken authentication = (KeycloakAuthenticationToken) SecurityContextHolder.getContext()
      .getAuthentication();

  final Principal principal = (Principal) authentication.getPrincipal();

  String tokenInfo = null;
  if (principal instanceof KeycloakPrincipal) {

    KeycloakPrincipal<keycloaksecuritycontext> kPrincipal = (KeycloakPrincipal<keycloaksecuritycontext>) principal;
    KeycloakSecurityContext ksc = kPrincipal.getKeycloakSecurityContext();
    IDToken token = ksc.getIdToken();
    AccessToken accessToken = kPrincipal.getKeycloakSecurityContext().getToken();
    tokenInfo = accessToken.getSubject();

    // this value is the one use to call another service as bearer token
    // Authorization : Bearer kcs.getTokenString()
    // use this link to read the token https://jwt.io

    return String.format("Hello %s %s [subject=%s]", accessToken.getGivenName(), accessToken.getFamilyName(),
        tokenInfo);
  }

  return "Hello World";
}
Enter fullscreen mode Exit fullscreen mode

5. Spring API Gateway Project

We need another Spring project that will have a dependency on the spring-cloud-starter-gateway. In this project, we must define request routing and Jwt issuer URIs for each realm.

With TokenRelayGatewayFilterFactory, it acts as an OAuth2 consumer and forwards the incoming token to the outgoing request resource, in this case to the user service.

@Bean
public RouteLocator gatewayRouter(RouteLocatorBuilder builder, TokenRelayGatewayFilterFactory filterFactory) {
 
return builder.routes()
    .route(p -> p.path("/test")
        .uri("http://httpbin.org"))
    .route("users", p -> p.path("/users/**")
        .filters(f -> f.filter(filterFactory.apply()))
        .uri("http://localhost:8001"))
    .build();
}
Enter fullscreen mode Exit fullscreen mode

6. Testing

6.1 Keycloak

You can use the custom Keycloak project I linked in the references section. You will need two realms to test the application. Included in the project.

Image description

6.2 Postman Collection

I am attaching a Postman collection that you can use to test the endpoints. Included in the project.

Tenant1 / User1

Image description

Tenant2 / User2

Image description

6.3 User Service

Image description

6.4 API Gateway

Image description

7. Improvements

To avoid redeploying a war file, add a new tenant keycloak.json file and external storage such as S3.

8. References

Top comments (0)