Secure Service-to-Service (S2S) Authentication in Microservices with Keycloak & Audience 

Our Problem: The Fragility of Legacy S2S in a Microservices World 

For years, when we needed one application to talk to another via a REST API, the solution was straightforward: we’d create a dedicated Active Directory (AD) service user. This approach worked beautifully with our monolithic applications, especially those running on .NET Framework on IIS. The tight integration with Windows Authentication made it seamless. 

Then came the shift to microservices

Suddenly, managing AD service users became a major pain point. Our services were now OS-agnostic, running in Linux containers, and scaling independently. The old approach felt clunky, platform-dependent, and introduced unnecessary complexity into our modern, distributed architecture. We needed a solution for app-to-app authentication that was: 

  • OS agnostic
  • Easy and safe to implement
  • Decoupled from the underlying infrastructure (like AD). 
  • Standards-based

Our Solution: Keycloak S2S with OIDC and the Audience Claim 

The answer was clear: Service-to-Service (S2S) Authentication using Keycloak’s OpenID Connect (OIDC) features. This approach, built on the Client Credentials Grant, turned out to be the perfect, safe, and easy-to-implement solution that also had the benefit of libraries available in almost every development language. 

For S2S communication, the service acts on its own behalf, not on behalf of a user. This is why the correct OIDC flow is the Client Credentials Grant

The Critical Security Layer: Audience (aud) Validation 

The most crucial aspect of making this solution safe is verifying that a token issued for one service cannot be misused to call a different, unrelated service. This is achieved using the Audience (aud) Claim. The aud claim verifies that the token was specifically intended for the service receiving the request. 

Component Role OIDC Mechanism 
Client A (Caller) The service initiating the request. Uses its Client ID and Secret to get a token. 
Service B (Target) The service being called (the protected resource). Defines the Audience that must be present in the token. 
Token Validation Security check performed by Service B. Checks the JWT’s aud claim against its own ID. 

Real-Life Use Case: Chat Application to CRM Integration 

To illustrate a corporate example, let’s look at a common scenario: 

  • Client A (Caller): The Chat App Service (chat-app-service) which needs to create a support ticket. 
  • Service B (Target): The CRM API Gateway (crm-api-gateway) which has a protected endpoint for data entry. 

The Goal: The Chat App Service must prove to the CRM API Gateway that it has a valid, dedicated token to submit a ticket. 

How We Implemented S2S in Keycloak (Configuration Steps) 

Here are the precise steps we followed to configure both the receiving service and the calling service in Keycloak (v26.0+). 

A. Configure the Target Service (Service B) – The Resource Server 

This client defines the Audience that must be included in the token. 

  1. Navigate to Clients and click Create client
  1. Set the Client ID to the unique identifier for your target service (e.g., crm-api-gateway). 
  1. Set Client authentication to ON
  1. Set Authorization to OFF (unless you are using Keycloak’s Authorization Services). 
  1. Click Save

B. Configure the Calling Service (Client A) – The Token Requester 

This client is the one that will request the token, so it needs the Client Credentials Grant enabled. 

  1. Navigate to Clients and click Create client
  1. Set the Client ID for your calling service (e.g., chat-app-service). 
  1. Set Client authentication to ON
  1. Toggle Service accounts enabled to ON. (This is crucial to allow the use of the Client Credentials Grant). 
  1. Click Save
  1. Go to the client’s Credentials tab to retrieve the Client Secret needed by the service. 

C. Create a Dedicated Client Scope and Audience Mapper 

This is the key step where we tell Keycloak to insert the Target Service’s ID into the token issued to the Calling Service

  1. Navigate to Client Scopes
  1. Click Create Client Scope
  1. Set the Name (e.g., crm-audience). 
  1. Click Save
  1. On the new scope, go to the Mappers tab. 
  1. Click Create Protocol Mapper
  1. Select Mapper Type: Audience
  1. Set: 
  • Name: CRM Service Audience Mapper (or similar). 
  • Included Client Audience: Enter the Client ID of Service B (e.g., crm-api-gateway). 
  • Add to access token: ON (Crucial!). 
  1. Click Save

D. Assign the Audience Scope to the Calling Client (Client A) 

Finally, we link the Audience Scope to the client that will be requesting the token. 

  1. Navigate to Clients and select your calling service (e.g., chat-app-service). 
  1. Go to the Client Scopes tab. 
  1. Under Default Client Scopes, click Add client scope and select the scope you created (e.g., crm-audience). 
  1. Click Save

The Chat App Service can now use its Client ID/Secret to get a token. That token will contain the aud claim set to crm-api-gateway, and the CRM API Gateway will validate this claim, completing a secure, standardized, and OS-agnostic S2S flow. Problem solved! 🎉 

AVI E.

Leave a Reply

Discover more from Rafael IT Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading