Securing Microservices Using Keycloak, OAuth2, and Spring Security
Keeping microservices secure is crucial in today’s interconnected systems. With each microservice requiring its own secured endpoints, managing authentication and authorization can quickly become complex. That’s where solutions like Keycloak, combined with OAuth2 and Spring Security, come into play.
This guide explains how to set up centralized authentication with Keycloak, configure Spring Security as a resource server, implement role-based and scope-based access control, and manage secure communication between microservices by propagating tokens. By following these practices, you’ll be able to fortify your microservices architecture.
Table of Contents
- Centralized Authentication with Keycloak
- Spring Security and Resource Server Setup
- Role-Based and Scope-Based Access Control
- Token Propagation Between Services
- Summary
Centralized Authentication with Keycloak
Why Centralized Authentication?
Managing user authentication for multiple microservices individually can lead to redundancy and vulnerabilities. Centralized authentication simplifies this process by offloading the responsibility to a dedicated identity provider. Keycloak is a powerful, open-source solution that acts as an authentication server, supporting OAuth2, OpenID Connect, and SAML protocols.
Setting Up Keycloak
- Install Keycloak: Download and run Keycloak using Docker:
docker run -d -p 8080:8080 quay.io/keycloak/keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin
- Create a Realm:
- A realm represents a security boundary. Navigate to the Keycloak admin console and create a new realm.
- Set Up a Client:
- Each microservice corresponds to a Keycloak client. A client is essentially an application Keycloak protects.
- Configure the client’s access type as
confidential
to support server-to-server communication.
- Configure Users and Roles:
- Add users and assign them roles (e.g.,
admin
,user
).
- Add users and assign them roles (e.g.,
Keycloak’s Role in OAuth2
Keycloak serves as an Authorization Server in OAuth2, managing tokens like:
- Access Tokens: Short-lived tokens for accessing APIs.
- Refresh Tokens: Tokens to renew expired access tokens.
- ID Tokens: Tokens containing user identity information.
With Keycloak, microservices are decoupled from authentication logic, enhancing security and maintainability.
Spring Security and Resource Server Setup
Overview of Spring Security Integration
To secure microservices, Spring Security provides built-in support for OAuth2 and acts as a resource server. A resource server:
- Validates incoming access tokens.
- Protects API endpoints based on scopes and roles.
Adding Dependencies
Add the necessary dependencies in your pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Configuring Spring Security
Set up the resource server by creating a class annotated with @Configuration
:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/public/**").permitAll() // Allow public access
.antMatchers("/api/**").authenticated() // Secure API endpoints
.and()
.oauth2ResourceServer().jwt(); // Enable JWT validation
}
}
Connecting to Keycloak
Configure Keycloak’s OAuth2 endpoint in application.properties
:
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/my-realm
With this setup:
- The resource server validates JWT tokens issued by Keycloak.
- Only authenticated requests can access secured endpoints.
Role-Based and Scope-Based Access Control
Differentiating between Role-Based and Scope-Based Access
- Role-Based Access Control (RBAC):
- Focuses on user roles, such as
admin
oruser
. - Common for defining what actions a user is allowed to perform.
- Focuses on user roles, such as
- Scope-Based Access Control:
- Tied to OAuth2 scopes, such as
read:user
orwrite:data
. - Reflects the permissions granted by a token, making it more granular.
- Tied to OAuth2 scopes, such as
For example:
- A user with the
admin
role can manage users. - A token with the
write:data
scope allows writing to a specific resource.
Implementing Role-Based Access Control
Extend your SecurityConfig
to include role-based rules:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN") // Admin-only endpoint
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN") // User or Admin access
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
Roles are encoded in the token, which Spring Security extracts and matches against your rules.
Enforcing Scope-Based Access
Customize scope-based access decisions:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/resource").hasAuthority("SCOPE_write:data") // Write permission required
.antMatchers(HttpMethod.GET, "/resource").hasAuthority("SCOPE_read:data") // Read permission required
.and()
.oauth2ResourceServer().jwt();
}
Ensure that the required scopes (write:data
, read:data
) are included in the Keycloak client configuration.
Example Token Content
A decoded JWT token for a user with scopes and roles might look like this:
{
"scope": "read:data write:data",
"realm_access": {
"roles": ["admin"]
}
}
Spring Security can extract and validate these claims to secure endpoints appropriately.
Token Propagation Between Services
The Challenge of Propagating Tokens
When Service A communicates with Service B, Service B needs to validate the original client’s request. However, Service A must correctly propagate the access token it received.
Passing the Token
Use Spring’s RestTemplate or WebClient to forward the token in upstream requests.
Example Using WebClient:
@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager manager) {
return WebClient.builder()
.filter(new ServerOAuth2AuthorizedClientExchangeFilterFunction(manager))
.build();
}
Adding the Token to Request Headers:
public String callAnotherService(String token, String endpoint) {
return webClient.get()
.uri(endpoint)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.retrieve()
.bodyToMono(String.class)
.block();
}
By propagating tokens, downstream services can validate requests and enforce security rules seamlessly.
Security Between Internal Services
To secure service-to-service communication:
- Use Keycloak-issued tokens for all communication.
- Configure scopes or roles in Keycloak to allow inter-service access.
By propagating tokens securely, microservices maintain consistent security while delegating authentication to the centralized server.
Summary
Securing microservices with Keycloak, OAuth2, and Spring Security simplifies authentication and authorization while providing a flexible and scalable framework. Here are the highlights of this guide:
- Centralized Authentication: Keycloak manages users, roles, and tokens, reducing complexity in microservices.
- Resource Server Configuration: Spring Security validates OAuth2 tokens and secures endpoints.
- Access Control: Role- and scope-based access ensures fine-grained control over who can access what.
- Token Propagation: Securely pass tokens across services to enforce consistent policies.
By integrating these tools, your microservices architecture becomes robust against threats while remaining scalable and maintainable. Get started today to secure your APIs with confidence!