EUREKA-588 Spike - Investigate options for m2m calls based on defined module permissions
- 1 Spike Overview
- 1.1 Overview
- 1.2 Background
- 1.3 Problem Statement
- 1.4 Scope
- 1.5 Proposed Solutions
- 1.6 Conclusion and Next Steps
Spike Overview
ID: EUREKA-588
Overview
In the current FOLIO implementation, cross-module communication is handled by sidecars that enhance requests using system tokens.
Currently, sidecars in the Eureka platform include an overly privileged system token in module-to-module (M2M) calls. This setup compromises security boundaries because:
Any sidecar can invoke any endpoint of any module.
The intended module-specific permissions (
modulePermissions) are effectively ignored.Fine-grained control at the endpoint level is lacking, even though
modulePermissionswere designed to support this.
Background
Eureka's architecture uses sidecars to handle authorization by validating tokens before forwarding requests to downstream services.
The OKAPI platform dynamically adjusts tokens in-flight to include module-specific permissions. This supports more granular and secure access control, aligning better with the principle of least privilege.
Problem Statement
Using a globally privileged token in sidecars weakens FOLIO’s security posture by:
Allowing unrestricted access to internal APIs.
Increasing the risk of lateral movement if a sidecar is compromised.
Permitting modules to invoke endpoints they were never explicitly granted access to.
This approach violates the principle of least privilege and complicates efforts to reason about and audit access boundaries across the system.
Scope
This spike investigates and evaluates different M2M authorization strategies for FOLIO using Keycloak and module descriptors. The goal is to define a secure, scalable, and maintainable mechanism for module-to-module communication. The implementation should:
Support endpoint-level permissions via
modulePermissions.Avoid requiring manual permission logic in each module.
Minimize the number of Keycloak clients and service accounts.
Keep token handling lightweight.
Be backward-compatible or require minimal changes for module developers.
Work with existing Keycloak and Okapi capabilities (or with minimal enhancements).
Proposed Solutions
Option 1: Token Exchange via Keycloak
Leverage Keycloak’s OAuth 2.0 Token Exchange protocol (RFC 8693) to issue short-lived tokens that represent a combination of user and module-level permissions.
Token Composition Strategy ("Effective Token")
Replace the static system token with a dynamic, composite token that includes:
The original user token (from the incoming request).
Additional claims specific to the target module and endpoint (e.g.,
modulePermissions).
This results in an "effective token" that represents:
User Identity + Module-Specific Capabilities
Authorization decisions can then be made based on the intersection of user and module endpoint permissions.
Implementation Steps
Enable Token Exchange in Keycloak:
Navigate to Clients > Your Client > Settings in Keycloak.
Enable Token Exchange.
Configure required permissions to allow the sidecar to impersonate or exchange tokens.
Sidecar Performs Token Exchange:
http POST /auth/realms/myrealm/protocol/openid-connect/token Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange &subject_token=<<USER_ACCESS_TOKEN>> &subject_token_type=urn:ietf:params:oauth:token-type:access_token &requested_token_type=urn:ietf:params:oauth:token-type:access_token &audience=module-client-id &scope=module-specific-permission &client_id=sidecar-client-id &client_secret=sidecar-client-secretsubject_token: original user access tokenaudience: client ID of the target modulescope: module-specific permissions
Sidecar Uses the Effective Token for Authorization:
Token includes user claims + permissions scoped to the target module and endpoint.
Authorization checks are based on the intersection of both.
Pros and Cons
Aspect | Pros | Cons |
|---|---|---|
Security | Ensures fine-grained access control per user and endpoint | Adds complexity in token handling and Keycloak configuration |
Flexibility | Easily adaptable via client scopes and protocol mappers | Token exchange introduces network latency |
Separation | Clean distinction between user and module authorization responsibilities | Requires robust handling of protocol mappers and scopes |
Centralized Auth | Authorization decisions are token-driven and consistent across modules | Larger tokens may exceed HTTP header limits (e.g., 16 MB) |
Standards-Based | Based on OAuth 2.0 Token Exchange (RFC 8693), supported by Keycloak | Requires extra development for custom protocol mappers |
Option 2: Per-Module Keycloak Clients + Roles
Define one Keycloak client per module, and assign roles based on endpoint-level access.
Each client is responsible for maintaining its allowed scopes/permissions. Authorization is enforced by assigning the correct client credentials (with pre-defined roles) when invoking downstream modules.
Pros:
Clear boundaries between modules and their permissions
Simplifies token validation at the receiving module
Cons:
Increases the number of clients to manage in Keycloak
Requires changes in deployment or provisioning processes to issue and rotate credentials
Conclusion and Next Steps
The most promising approach is token exchange via Keycloak, where a module-specific token is generated by combining the user’s token with the necessary module permissions. This offers the most accurate and secure way to enforce access boundaries while preserving the user context.
However, this adds an extra round-trip to Keycloak and potentially increases token size. We must ensure tokens remain lightweight and compatible with HTTP header size limitations.
Next steps:
Prototype token exchange using Keycloak’s RFC 8693 support
Define and test protocol mappers for injecting
modulePermissionsAssess token size and performance impact in real scenarios
Explore caching techniques to mitigate added latency
Spike Status: IN Progress