Implementing Multi-tenancy with Keycloak and NestJS

Hey devs! 👋

If you’ve ever found yourself scratching your head trying to implement a system where different organizations (or clients) need their own isolated space, with their own users and configurations, you’re probably dealing with multi-tenancy….


This content originally appeared on DEV Community and was authored by Ítalo Queiroz

Hey devs! 👋

If you've ever found yourself scratching your head trying to implement a system where different organizations (or clients) need their own isolated space, with their own users and configurations, you're probably dealing with multi-tenancy. In this article, I'll share a practical approach to implementing this using Keycloak and NestJS.

The Problem

Imagine you're building a SaaS and each client needs:

  • Their own isolated environment
  • Manage their own users
  • Have their own authentication settings
  • Be able to use different identity providers

Sounds complex? Well, that's where our solution with Keycloak and NestJS comes in!

Why Keycloak?

Keycloak is a powerful Identity and Access Management (IAM) tool that provides native support for multi-tenancy through "realms". Each realm is like an isolated mini authentication server, with its own settings, users, and clients.

The Solution

Let's break down our implementation into main parts:

1. Dynamic Realm Creation

First, we need a service that will manage our realms in Keycloak. Here's a simplified example:

@Injectable()
class KeycloakService {
  private kcAdminClient: KeycloakAdminClient;

  constructor(private configService: ConfigService) {
    this.kcAdminClient = new KeycloakAdminClient({
      baseUrl: this.configService.get('keycloak.url'),
      realmName: 'master'
    });
  }

  async createRealm(config: {
    organizationName: string;
    displayName: string;
    theme?: string;
  }) {
    await this.authenticate();

    const realmConfig = {
      realm: config.organizationName,
      displayName: config.displayName,
      enabled: true,
      sslRequired: 'external',
      loginTheme: config.theme || 'default',
      // Other security settings...
    };

    await this.kcAdminClient.realms.create(realmConfig);

    // Configure OAuth2 clients, default roles, etc...
  }
}

2. Tenant Identification Middleware

In each request, we need to identify which tenant (organization) is making the call. A common approach is to use a custom header:

@Injectable()
export class TenantMiddleware implements NestMiddleware {
  async use(req: Request, res: Response, next: NextFunction) {
    const organizationId = req.headers['x-organization-id'];

    if (!organizationId) {
      throw new UnauthorizedException('Organization ID is required');
    }

    req.tenantId = organizationId;
    next();
  }
}

3. Token Validation per Tenant

Each tenant has their own realm in Keycloak, so we need to validate tokens considering this:

export async function validateToken(token: string, organizationId: string) {
  const jwksClient = getJwksClient(organizationId);

  try {
    const decodedToken = jwt.decode(token, { complete: true });
    const key = await jwksClient.getSigningKey(decodedToken.header.kid);

    return jwt.verify(token, key.getPublicKey(), {
      algorithms: ['RS256'],
      issuer: `${KEYCLOAK_URL}/realms/${organizationId}`
    });
  } catch (error) {
    throw new UnauthorizedException('Invalid token');
  }
}

4. Keycloak Context Decorator

To facilitate access to the Keycloak admin client in the current tenant context:

export const KeycloakContext = createParamDecorator(
  async (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const organizationId = request.tenantId;

    const adminClient = new KeycloakAdminClient({
      baseUrl: KEYCLOAK_URL
    });

    await adminClient.auth({
      grantType: 'client_credentials',
      clientId: 'admin-cli',
      clientSecret: ADMIN_SECRET
    });

    adminClient.setConfig({ realmName: organizationId });

    return {
      adminClient,
      realm: organizationId
    };
  }
);

5. Using in a Controller

Now we can put it all together in a controller:

@Controller('users')
export class UserController {
  @Post()
  async createUser(
    @KeycloakContext() kc: KeycloakContext,
    @Body() userData: CreateUserDto
  ) {
    const { adminClient, realm } = kc;

    // Create user in the tenant-specific realm
    const user = await adminClient.users.create({
      realm,
      username: userData.email,
      email: userData.email,
      enabled: true,
      // other properties...
    });

    return user;
  }
}

Tips and Considerations

  1. Smart Caching: Implement caching for JWT tokens and user information, but remember to separate by tenant!

  2. Data Migration: Have a clear strategy for data migration when creating new tenants.

  3. Monitoring: Add detailed logs to debug tenant-specific issues.

  4. Testing: Create tests that validate isolation between tenants.

Conclusion

Implementing multi-tenancy might seem daunting at first, but with the right tools (like Keycloak and NestJS) and a well-thought-out architecture, we can create a robust and scalable solution.

The code I showed here is just the tip of the iceberg, there's much more to consider like per-tenant database management, distributed caching, per-organization rate limiting, etc. But that's a topic for another article! 😉


This content originally appeared on DEV Community and was authored by Ítalo Queiroz


Print Share Comment Cite Upload Translate Updates
APA

Ítalo Queiroz | Sciencx (2025-06-27T22:44:55+00:00) Implementing Multi-tenancy with Keycloak and NestJS. Retrieved from https://www.scien.cx/2025/06/27/implementing-multi-tenancy-with-keycloak-and-nestjs/

MLA
" » Implementing Multi-tenancy with Keycloak and NestJS." Ítalo Queiroz | Sciencx - Friday June 27, 2025, https://www.scien.cx/2025/06/27/implementing-multi-tenancy-with-keycloak-and-nestjs/
HARVARD
Ítalo Queiroz | Sciencx Friday June 27, 2025 » Implementing Multi-tenancy with Keycloak and NestJS., viewed ,<https://www.scien.cx/2025/06/27/implementing-multi-tenancy-with-keycloak-and-nestjs/>
VANCOUVER
Ítalo Queiroz | Sciencx - » Implementing Multi-tenancy with Keycloak and NestJS. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/06/27/implementing-multi-tenancy-with-keycloak-and-nestjs/
CHICAGO
" » Implementing Multi-tenancy with Keycloak and NestJS." Ítalo Queiroz | Sciencx - Accessed . https://www.scien.cx/2025/06/27/implementing-multi-tenancy-with-keycloak-and-nestjs/
IEEE
" » Implementing Multi-tenancy with Keycloak and NestJS." Ítalo Queiroz | Sciencx [Online]. Available: https://www.scien.cx/2025/06/27/implementing-multi-tenancy-with-keycloak-and-nestjs/. [Accessed: ]
rf:citation
» Implementing Multi-tenancy with Keycloak and NestJS | Ítalo Queiroz | Sciencx | https://www.scien.cx/2025/06/27/implementing-multi-tenancy-with-keycloak-and-nestjs/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.