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
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
);
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());
}
}
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;
}
}
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"
}
}
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);
}
}
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);
}
}
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);
}
}
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);
Recommendation
For Spring Boot applications with Keycloak requiring creator-based and business group authorization:
- Use Keycloak for authentication and basic group membership
- Implement custom authorization layer for relationship-based decisions
- Store ownership relationships in your application database
- Cache authorization decisions for performance
- 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)