Keycloak Authorization Deep Dive: Building RBAC for Spring Boot Apps With API Gateway
Published: 2024-02-15 11:19:31

 
Identity and access management (IAM) is a core building block in modern applications. Keycloak provides a powerful open-source solution to handle authentication and authorization needs. In this article, we'll dive into Keycloak's authorization entities, how they're interconnected, and demonstrate how to implement fine-grained role-based access control (RBAC) within a Spring Boot application secured using Keycloak.

**Keycloak Authorization Entities**

Let's breakdown the fundamental components of Keycloak's authorization model:

* **Realm:** The outermost container within Keycloak, encapsulates a unique set of users, clients, roles, and configurations.
* **Client:** Applications or services interacting with Keycloak for security purposes.
* **User:** Identities (people, services) attempting to access protected resources.
* **Group:** A collection of users. Roles can be assigned to groups, simplifying the permission assignment process for multiple users with similar access needs.
* **Role:** A collection of permissions. Roles are assigned to users to streamline access management.
* **Resource:** A protected entity within Keycloak, such as an API endpoint, web resource, or data segment.
* **Scope:** Defines permissions boundaries within a resource (e.g., "read", "create").
* **Policy:** Ruleset determining if a user should have access. Policies consider roles, context, and more.
* **Permission:** This unit links a resource, scopes, and policies, creating an atomic piece of authorization.

**Building an RBAC Spring Boot Application**

**Scenario:** A product management application with the following access requirements:

* Product Managers: Full CRUD (Create, Read, Update, Delete) access to product data.
* Sales Representatives: Read-only access to product data.

**Steps:**

1. **Keycloak Setup**
    * Create a realm, clients (frontend, backend), users, and roles.
    * Define resources (e.g., "ProductResource") and relevant scopes (e.g., "product:read", "product:create").
    * Construct policies based on roles (e.g., "Only Product Managers Policy").
    * Create permissions associating resources, scopes, and policies.
2. **Spring Boot Integration**
    * Add Keycloak Spring Security adapter dependencies.
    * Configure Spring Security to use Keycloak.
3. **Method-Level Authorization**
    * Utilize `@PreAuthorize` with Keycloak's permission notation:
    ```java
    Java@PreAuthorize("hasAuthority('product:update')")
    public void updateProduct(Product product) { ... } 
    ```

**Code Example (Product Controller)**

```java
@RestController
@RequestMapping("/products")
public class ProductController {

    @GetMapping
    @PreAuthorize("hasAuthority('product:read')")
    public List<Product> getProducts() { ... }

    // Other CRUD endpoints with relevant @PreAuthorize annotations
}

```

**Securing Services behind API Gateway**

In a modern microservices architecture, services are exposed behind API Gateway to have robust authentication and authorization for protecting APIs. Keycloak and API Gateway provide an solution, combining centralized identity management (global authentication) with fine-grained access control within services (local authorization).

API gateways, integrated with identity providers (IDPs) like Keycloak, offer a powerful way to secure and manage access to your microservices. However, is that not every authorization scenario can or should be entirely delegated to the API gateway.

**Keycloak for Global Authentication**

Keycloak, acts as our centralized authentication provider. Here's how it integrates:

1. **User Authentication:** Users sign into Keycloak, providing credentials against a user store.
2. **Token Generation:** Keycloak generates secure access tokens (e.g., JWTs) containing verified user identities and roles.
3. **Microservice API Calls:** These tokens accompany each request to protected microservice APIs.

**API Gateway: API Gateway and Authorization**

API Gateway serves as the front-line centralizing API traffic. It enhances security in the following ways:

1. **Token Validation:** API Gateway intercepts API calls and validates the included Keycloak tokens ensuring only authenticated requests pass.
2. **API-Level Authorization:** optionally, enforces coarse-grained access control to APIs based on user roles or other token attributes.

**Spring Boot Microservices: Local Authorization**

While Keycloak and API Gateway govern authentication and API gateway-level authorization, fine-grained control within microservices is essential. Spring Boot simplifies this:

1. **Token Introspection:** Spring Security introspects the Keycloak token, extracting user roles and permissions.
2. **Method-Level Security:** Employ `@PreAuthorize` annotations to specify access rules (e.g., `@PreAuthorize("hasRole('admin')")` for admin-only endpoints).
3. **Custom Authorization Logic:** Implement intricate authorization based on business rules, resource ownership, and more as needed.

**When to Keep Authorization Logic within Spring Boot?**

Let's explore some common scenarios where your Spring Boot application might need to retain a degree of authorization decision-making:

* **1. Ownership-Based Authorization**
    * Users should only be able to interact with resources they own (e.g., their own profiles, created documents, etc.).
    * Ownership data typically resides within your application's database, necessitating Spring Boot lookups to compare against the authenticated user ID extracted from the token.
* **2. Fine-Grained Permissions Based on Application Data**
    * Access rights are determined by complex relationships within your domain model. A medical record system or a hierarchical CRM are prime examples.
    * Your Spring Boot application has the best understanding of these relationships for authorization decisions.
* **3. Workflows and State-Based Checks**
    * A resource's current state might be a vital factor in authorization.
    * The API gateway is often less aware of real-time state changes managed by your Spring Boot application (e.g., a document moving from "draft" to "submitted" status).
* **4. External System Dependencies**
    * Authorization could rely on factors outside the IDP's scope – like inventory availability, risk scores from a fraud detection system, etc.
    * Your Spring Boot application needs to orchestrate calls to these systems and incorporate their responses into authorization logic.

**The Benefits of a Hybrid Approach**

* **Simplify Where Possible:** Delegate foundational role-based access control and common API-level policies to the API gateway, streamlining a significant portion of authorization.
* **Targeted Control:** Retain authorization logic within Spring Boot for the nuanced, domain-driven scenarios outlined above.
* **Scalability and Maintainability:** This balanced approach helps manage complexity and ensures your authorization system can evolve with your application's unique requirements.

**Authorization Checks at the API Manager Layer**

API managers, when tightly integrated with an identity provider (IDP) like Keycloak, can take on a substantial portion of your authorization burden. Here's what they are well-suited to handle:

* **Token Validation and Introspection:**
    * The API manager ensures incoming requests carry valid access tokens issued by the IDP.
    * It can often perform token introspection (an API call to the IDP) to retrieve richer user details and permissions.
* **Role-Based Access Control (RBAC):**
    * Enforce access to APIs or specific API operations based on the roles extracted from the user's access token.
    * Example: "Editors" can create and update articles, "viewers" have read-only access.
* **Scope Enforcement:**
    * OAuth2 scopes associated with tokens can control granular permissions. For instance, a "profile:read" scope might be required to access user profile information.
* **Rate Limiting and Throttling:**
    * Protect your APIs from excessive usage or potential abuse by applying rate limits at the API gateway level. These can be based on user identity, subscription tiers, or other criteria.

**Pros of API Gateway Authorization**

* **Centralization:** Offloading authorization logic to the gateway promotes consistency across multiple microservices and reduces redundant code in your Spring Boot applications.
* **Simplified Application Code:** Spring Boot services can focus on core business logic, potentially leading to cleaner code and less need for security-related annotations.
* **API Management Features:** API gateways offer value beyond just authorization: rate limiting, analytics, developer portals, and more.

**Cons of API Gateway Authorization**

* **Limited Domain Knowledge:** The API gateway typically has less visibility into the intricate details and state of your application's data model compared to your Spring Boot code.
* **Loss of Fine-Grained Control:** Complex authorization scenarios based on data relationships or external system checks might be difficult to model entirely within the constraints of the API gateway.
* **Performance Overhead:** An extra network hop (user -> API gateway -> Spring Boot service) could introduce some latency, although this can be mitigated with caching and optimization.

**In Practice: Example**

Consider an e-commerce application:

* **API Gateway:** Handles authentication via the IDP, enforces rate limiting, manages subscriptions, could even have basic role checks ("customer" vs. "admin").
* **Spring Boot:** Determines if a customer can edit their own order, potentially factoring in order status ("shipped" orders are uneditable). It might check an external inventory service before allowing a new order to be placed.

**Example: E-commerce Microservices**

* **Authentication:** User logs into Keycloak.
* **API call to Product Microservice:** API Gateway validates the token and allows general access to fetch product details.
* **API Call to Order Microservice:** API Gateway validates the token. Spring Boot within the order microservice further checks for the "customer" role before allowing order placement.

Let's explore custom authorization logic examples within CRUD-based microservices, emphasizing how it complements the global authentication and gateway-level authorization provided by Keycloak and API GW.

**Scenario: Online Document Collaboration Platform**

* Microservices: Document Microservice, Collaboration Microservice, User Microservice
* Keycloak: Handles authentication, role management (e.g., "editor," "owner," "viewer")
* API GW: Validates tokens for API-level access.

**Custom Authorization Examples within the Document Microservice**

1. **Editing Permissions**
    * Spring Boot Controller:
    ```java
    @PutMapping("/documents/{documentId}")
    @PreAuthorize("hasAuthority('document:edit') and isDocumentOwner(#documentId)")
    public void updateDocument(@PathVariable String documentId, @RequestBody Document updates) {
         // ... update logic
    }
    ```
    _**Explanation:_*  
    * `hasAuthority('document:edit')`: Ensures the user has a token with this Keycloak-derived permission.  
    * `isDocumentOwner(#documentId)`: A custom Spring Expression Language (SpEL) method. Checks if the authenticated user's ID matches the document's 'ownerId' field (retrieved separately).
2. **Controlling Document Visibility**
    * Spring Boot Controller:
    ```java
    @GetMapping("/documents/{documentId}")
    public Document getDocument(@PathVariable String documentId) {
        User currentUser = getCurrentUser(); // Get authenticated user details
        if (isDocumentOwner(documentId) || hasCollaboratorRole(documentId, "viewer")) { 
            // ... fetch document
        } else {
            throw new AccessDeniedException();
        }
    }
    ```
    _**Explanation:_* Beyond Keycloak roles, custom methods (`isDocumentOwner`, `hasCollaboratorRole`) check business logic for finer-grained viewing access. This could involve querying the Collaboration Microservice to determine the relationship.

**Beyond CRUD**

Custom authorization often extends beyond simple CRUD actions:

* **Action-Based Permissions:** "approveDocument," "shareDocument," etc., could have dedicated logic.
* **Resource State:** A document's "published" status could affect user access rights.

**Conclusion**

Keycloak offers a flexible and robust authorization framework. By understanding its core entities and their relationships, you can effectively implement RBAC (or more complex models) in your Spring Boot applications. The provided example is a starting point, and Keycloak's policy mechanisms allow you to build highly custom authorization logic suited to the specific and evolving needs of your applications.

other Useful resources: [https://www.appsdeveloperblog.com/fine-grained-authorization-services-in-keycloak/](https://www.appsdeveloperblog.com/fine-grained-authorization-services-in-keycloak/)