DEV Community

DEV-AI
DEV-AI

Posted on

Implementing Advanced and Business Group Authorization with Spring Boot and Keycloak

The ReBAC Challenge in Keycloak

Keycloak's authorization model fundamentally operates on static role assignments and group memberships, making it challenging to implement dynamic relationship-based access patterns like "users can edit resources they created" or "business group members access group-owned resources"[2][4]. The platform lacks native support for expressing relationships between users, resources, and business entities.

Architectural Approaches

Hybrid Authorization Pattern

The most effective approach combines Keycloak's authentication strengths with custom relationship modeling in your Spring Boot application:

Keycloak Responsibilities:

  • User authentication and session management
  • Business group membership via hierarchical groups
  • Basic role assignments (admin, manager, member)

Custom Authorization Layer:

  • Creator-resource relationships
  • Dynamic business context evaluation
  • Fine-grained permission decisions

Group Structure Design

Implement a hierarchical group structure in Keycloak to represent business entities[1]:

/business-entities
├── company-acme
│   ├── departments
│   │   ├── engineering
│   │   └── sales
│   └── roles
│       ├── acme_admin
│       ├── acme_manager
│       └── acme_member
└── company-contoso
    ├── departments
    │   └── marketing
    └── roles
        ├── contoso_admin
        └── contoso_member
Enter fullscreen mode Exit fullscreen mode

Implementation Strategy

1. Database Schema for Relationships

Create tables to model creator relationships and business context:

-- Resource ownership tracking
CREATE TABLE resource_ownership (
    id BIGSERIAL PRIMARY KEY,
    resource_type VARCHAR(50) NOT NULL,
    resource_id VARCHAR(100) NOT NULL,
    creator_id VARCHAR(100) NOT NULL,
    business_group_id VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Business group hierarchies
CREATE TABLE business_group_hierarchy (
    parent_group_id VARCHAR(100) NOT NULL,
    child_group_id VARCHAR(100) NOT NULL,
    depth INTEGER NOT NULL
);
Enter fullscreen mode Exit fullscreen mode

2. Custom Authorization Service

Implement a service that combines Keycloak claims with database relationships:

@Service
public class BusinessAuthorizationService {

    @Autowired
    private ResourceOwnershipRepository ownershipRepo;

    @Autowired
    private BusinessGroupRepository groupRepo;

    public boolean canAccessResource(Authentication auth, String resourceType, 
                                   String resourceId, String requiredPermission) {
        Jwt jwt = (Jwt) auth.getPrincipal();
        String userId = jwt.getSubject();
        String userBusinessGroup = jwt.getClaimAsString("business_group");

        // Check if user is creator
        if (isCreator(userId, resourceType, resourceId)) {
            return hasCreatorPermission(auth, requiredPermission);
        }

        // Check business group access
        if (hasBusinessGroupAccess(userBusinessGroup, resourceType, resourceId)) {
            return hasGroupPermission(auth, requiredPermission);
        }

        return false;
    }

    private boolean isCreator(String userId, String resourceType, String resourceId) {
        return ownershipRepo.existsByCreatorIdAndResourceTypeAndResourceId(
            userId, resourceType, resourceId);
    }

    private boolean hasBusinessGroupAccess(String userGroup, String resourceType, 
                                         String resourceId) {
        ResourceOwnership ownership = ownershipRepo
            .findByResourceTypeAndResourceId(resourceType, resourceId);

        if (ownership == null) return false;

        // Check if user's group has access to resource's business group
        return groupRepo.hasGroupAccess(userGroup, ownership.getBusinessGroupId());
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Method-Level Authorization

Use Spring Security's @PreAuthorize with custom authorization logic[6][7]:

@RestController
@RequestMapping("/api/projects")
public class ProjectController {

    @GetMapping("/{projectId}")
    @PreAuthorize("@businessAuthService.canAccessResource(authentication, 'PROJECT', #projectId, 'READ')")
    public ProjectDto getProject(@PathVariable String projectId) {
        return projectService.findById(projectId);
    }

    @PutMapping("/{projectId}")
    @PreAuthorize("@businessAuthService.canAccessResource(authentication, 'PROJECT', #projectId, 'WRITE')")
    public void updateProject(@PathVariable String projectId, 
                             @RequestBody ProjectDto project) {
        projectService.update(projectId, project);
    }

    @PostMapping
    @PreAuthorize("hasRole('PROJECT_CREATOR')")
    public ProjectDto createProject(@RequestBody CreateProjectDto project, 
                                   Authentication auth) {
        Jwt jwt = (Jwt) auth.getPrincipal();
        String creatorId = jwt.getSubject();
        String businessGroup = jwt.getClaimAsString("business_group");

        ProjectDto created = projectService.create(project);

        // Record ownership relationship
        ownershipService.recordOwnership("PROJECT", created.getId(), 
                                       creatorId, businessGroup);

        return created;
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Keycloak Token Enhancement

Configure protocol mappers to inject business group information into tokens[1]:

// Keycloak Protocol Mapper Configuration
{
  "name": "business-group-mapper",
  "protocol": "openid-connect",
  "protocolMapper": "oidc-group-membership-mapper",
  "config": {
    "claim.name": "business_group",
    "full.path": "false",
    "group.name.regex": "^/business-entities/([^/]+)/.*",
    "group.name.replacement": "$1"
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Advanced Relationship Patterns

Implement complex business rules through custom policies:

@Component
public class BusinessGroupPolicyEngine {

    public boolean evaluateGroupPolicy(String userGroup, String resourceGroup, 
                                     String operation) {
        // Parent group members can access child group resources
        if (isParentGroup(userGroup, resourceGroup)) {
            return true;
        }

        // Peer groups in same organization can read each other's resources
        if (operation.equals("READ") && isSameOrganization(userGroup, resourceGroup)) {
            return true;
        }

        // Cross-department collaboration rules
        if (hasCollaborationAgreement(userGroup, resourceGroup)) {
            return allowedOperations(userGroup, resourceGroup).contains(operation);
        }

        return false;
    }

    private boolean isParentGroup(String parentCandidate, String childCandidate) {
        return groupHierarchyRepo.existsByParentAndChild(parentCandidate, childCandidate);
    }
}
Enter fullscreen mode Exit fullscreen mode

Alternative: External ReBAC Engine

For complex relationship modeling, consider integrating an external ReBAC engine like Permit.io[4]:

@Service
public class ExternalAuthorizationService {

    private final PermitApi permitClient;

    public boolean checkPermission(String userId, String action, String resource) {
        try {
            return permitClient.check(userId, action, resource);
        } catch (Exception e) {
            // Fallback to default deny
            return false;
        }
    }
}

@RestController
public class SecureController {

    @GetMapping("/documents/{id}")
    @PreAuthorize("@externalAuthService.checkPermission(authentication.name, 'read', 'document:' + #id)")
    public DocumentDto getDocument(@PathVariable String id) {
        return documentService.findById(id);
    }
}
Enter fullscreen mode Exit fullscreen mode

Performance Optimization

Caching Strategy

Implement caching for frequently accessed relationships:

@Service
@CacheConfig(cacheNames = "authorization")
public class CachedAuthorizationService {

    @Cacheable(key = "#userId + ':' + #resourceType + ':' + #resourceId")
    public boolean isCreator(String userId, String resourceType, String resourceId) {
        return ownershipRepo.existsByCreatorIdAndResourceTypeAndResourceId(
            userId, resourceType, resourceId);
    }

    @Cacheable(key = "#userGroup + ':' + #resourceGroup")
    public boolean hasGroupAccess(String userGroup, String resourceGroup) {
        return groupHierarchyRepo.hasAccess(userGroup, resourceGroup);
    }
}
Enter fullscreen mode Exit fullscreen mode

Database Optimization

Create composite indexes for authorization queries:

-- Optimize creator lookups
CREATE INDEX idx_resource_ownership_lookup 
ON resource_ownership(creator_id, resource_type, resource_id);

-- Optimize group hierarchy queries
CREATE INDEX idx_group_hierarchy_parent 
ON business_group_hierarchy(parent_group_id, child_group_id);
Enter fullscreen mode Exit fullscreen mode

Recommendation

For Spring Boot applications with Keycloak requiring creator-based and business group authorization:

  1. Use Keycloak for authentication and basic group membership
  2. Implement custom authorization layer for relationship-based decisions
  3. Store ownership relationships in your application database
  4. Cache authorization decisions for performance
  5. Consider external ReBAC engines for very complex relationship models

This hybrid approach provides the security benefits of Keycloak while enabling sophisticated relationship-based access control patterns that pure Keycloak implementations cannot support[2][4].

Citations:
[1] Implementing Organization-Based Access Control with Keycloak https://dev.to/devaaai/implementing-organization-based-access-control-with-keycloak-42km
[2] Best Practices for Implementing Permissions in Keycloak - Permit.io https://www.permit.io/blog/configuring-keycloak-authorization-best-practices
[3] Complex roles and permissions model on nested group ... https://stackoverflow.com/questions/71155832/complex-roles-and-permissions-model-on-nested-group-structure-in-keycloak
[4] Fine-Grained Keycloak Authorization with ABAC and ReBAC https://www.permit.io/blog/fine-grained-keycloak-authorization-with-abac-and-rebac
[5] How to manage and implement business context roles in resource ... https://stackoverflow.com/questions/74081314/how-to-manage-and-implement-business-context-roles-in-resource-server-with-openi
[6] Spring Boot 2 and Keycloak Authorization: implementing a simple ... https://stackoverflow.com/questions/53326032/spring-boot-2-and-keycloak-authorization-implementing-a-simple-rest-api
[7] Keycloak Authorization Deep Dive: Building RBAC for Spring Boot Apps With API Gateway https://dev.to/devaaai/keycloak-authorization-deep-dive-building-rbac-for-spring-boot-apps-2p77
[8] Authorization Services Guide - Keycloak https://www.keycloak.org/docs/latest/authorization_services/index.html
[9] How to implement permission-based access control to a resource owned by a group a user is part of in Keycloak? https://stackoverflow.com/questions/64983808/how-to-implement-permission-based-access-control-to-a-resource-owned-by-a-group
[10] Protecting Your Spring Boot Applications with the Keycloak ... https://www.springcloud.io/post/2022-02/keycloak-springboot/
[11] Authorization Academy - Relationship-Based Access Control (ReBAC) https://www.osohq.com/academy/relationship-based-access-control-rebac
[12] Implementing user-client associations in Keycloak ... https://community.latenode.com/t/implementing-user-client-associations-in-keycloak-authentication/19643
[13] How to manage security between APP -> BACKEND / Keycloak ... https://stackoverflow.com/questions/78616799/how-to-manage-security-between-app-backend-keycloak-spring-boot-ionic
[14] Securing Spring Boot Applications with Keycloak and ... https://www.atlantbh.com/securing-spring-boot-applications-with-keycloak-and-spring-security/
[15] Keycloak and Spring Boot OAuth 2.0 and OpenID Connect (OIDC ... https://www.linkedin.com/pulse/keycloak-spring-boot-oauth-20-openid-connect-oidc-young-gyu-kim-e444c
[16] Server Developer Guide - Keycloak https://www.keycloak.org/docs/latest/server_development/index.html
[17] Spring Boot 3 Keycloak OAuth 2 Tutorial with Spring Security https://www.youtube.com/watch?v=_0oXZKr97ro
[18] Part 10.1 - Configure Keycloak for Spring Boot 3 authentication https://www.youtube.com/watch?v=-uKONEemAVc
[19] 3 Keycloak authorization strategies to secure app access | TechTarget https://www.techtarget.com/searchsecurity/feature/Keycloak-authorization-strategies-to-secure-app-access
[20] Server Administration Guide - Keycloak https://www.keycloak.org/docs/latest/server_admin/index.html
[21] How to Setup Role Based Access Control (RBAC) with Keycloak https://www.permit.io/blog/implementing-dynamic-keycloak-rbac-with-permitio
[22] Authorization Basics - SWCODE BLOG https://blog.swcode.io/authz/2023/08/22/authz-basics/
[23] Keycloak with Spring Boot based on roles does not work ... https://stackoverflow.com/questions/74873222/keycloak-with-spring-boot-based-on-roles-does-not-work-which-were-assigned-to-t

Top comments (0)