Overview
The purpose of this effort is to determine the preferred path forward for bridging the gap between users in the system (e.g. patrons, etc.) and users of the system (those who log in and use Folio).
Background
When Eureka introduced Keycloak, it was clear that some user information needs to reside in Keycloak. The term AuthUsers was used to refer to user records in Keycloak, composed primarily of data required for authentication and authorization, but also of some ancillary data to identify the user and facilitate administration / user mgmt. The prevailing thought was that we don’t need AuthUser records for all users in the system. In fact, the vast majority of users in Folio represent patrons or other people who are users in the system, but not users of the system. For clarity, users in the system but not of the system are referred to as “Person” records.
Problem Statement
Problems arise when certain operations are performed on person records. Setting a password, assigning roles, etc. all require an AuthUser record to succeed. The purpose of this investigation is to answer the question: How do we bridge the gap between Persons and AuthUsers?
Scope
In Scope
All operations which create or modify user records in Folio
Creation/mgmt of users in the Folio UI
Creation/mgmt of users via Folio APIs
User import via mod-users-import
User migration APIs for migrating from the legacy platform to the Eureka platform
Assignment of roles/capabilities to users
Credential management (Setting a user’s password)
Support / System / Tenant users
Out of Scope
Changes to the legacy platform are out of scope since this problem is specific to the Eureka platform
Deliverables
There are two main approaches to consider. Key evaluation metrics include:
Required effort
Solution completeness
Performance
Cost
Security
Solution complexity
Architectural fit
User experience
Hosting/administrative impact
Option 1 - Promotion of users in the system to users of the system
Introduce a mechanism for promoting a user in the system to a user of the system. Only users of the system will have AuthUser records. Users can be created via standard mechanisms familiar to those using the legacy platform (mod-users, mod-users-import, etc.). These users will not initially have AuthUser records, but such records will be created as needed. There are two flows here:
Implicit
When performing actions which require an AuthUser, check if one exists, and create one if needed. Examples of this include setting a user’s password, or assigning roles/capabilities to the user.
Whether or not this “just in time” AuthUser creation happens automatically behind the scenes, or if confirmation is required on the part of the user is an implementation detail, and could even be configurable behavior.
Explicit
Allow administrators to explicitly promote a user, resulting in the creation of an AuthUser record. This is mostly a UX concern given that a use could be explicitly promoted by performing an operation which triggers the implicit creation of an AuthUser record (e.g. setting the user’s password).
It may be desirable to add an action menu item, button, etc. to ui-users allowing for explicit promotion.
NOTE: This can initially be implemented as a one-way operation, and later incorporate a way to demote a user (removing their AuthUser record).
NOTE: Presently, ui-users has been modified for Eureka to conditionally call the mod-users-keycloak APIs when creating/modifying users. This means that all users created via the users UI will have AuthUser records. The expectation is that if we choose to implement this solution, those changes will be backed out and ui-users will interact with mod-users. The only potential exception being explicit promotion (see above).
Implementation
Create a new endpoint in mod-users-keycloak which performs the check to see if an AuthUser record exists for the specified user, and optionally create one if needed.
Rollback/revert the changes in ui-users which conditionally call mod-users-keycloak
Adjustments to mod-login-keycloak (
POST /authn/credentials
) to call the new endpoint described above prior to attempting to set credentialsAdjustments to mod-roles-keycloak (
POST /roles/users
) to call the new endpoint described above prior to attempting to create role/user relationships.Adjustments to mod-roles-keycloak (
POST /users/capabilities
) to call the new endpoint described above prior to attempting to create/modify user/capability assignments.Adjustments to mod-roles-keycloak (
POST /users/capability-sets
) to call the new endpoint described above prior to attempting to create/modify user/capability-set assignments.
NOTE: If we want to do something like display a confirmation dialog to the user prior to JIT user creation, we would need to also make adjustments in ui-developer (passwd), ui-authorization-roles/ui-users (role/user, capability/user, capability-set/user assignments)
Pros
Existing code in mod-users, mod-users-import, etc. does not need to change.
AuthUsers only exist for users of the system, keeping the number of users in Keycloak lower, and more managable.
From the administrator’s perspective, the impact on their processes should be minimal. Depending on details of the implementation, there could be no impact at all.
Cons
There are a bunch of places where additional business logic is required to check for AuthUser presence and just-in-time creation. However, much of the business logic can be centralized by creating a new API endpoint in mod-users-keycloak and calling it as needed from other modules.
Option 2 - Users in the system == Users of the system
Create AuthUser records for all users in Folio. This means that users need to always be created via mod-users-keycloak, either directly or indirectly.
ui-users already uses mod-users-keycloak when the users-keycloak interface is enabled for the current tenant
mod-user-import would need to be adjusted to also (conditionally) call mod-users-keycloak instead of mod-users behind the scenes. As far as determining in the module whether we should call mod-users or mod-users-keycloak, we have several options, none of which are fantastic…
We could add an environment variable which controls this. Probably the easiest thing to do, but maybe not ideal.
We could try to determine if the interface is available, but that requires calling OKAPI on the legacy platform and the mgr-tenant-entitlements on the new platform... so how do we determine which endpoint to call?
Make this an env. variable configuration? I feel like this gets complicated fast given that the OKAPI and MTE responses are completely different, etc.
Try calling OKAPI, if the call fails, try calling MTE. Maybe only need to do this once at startup to determine which needs to be called, then perform the interface availability check as needed (probably with some caching). I think the circuit breaker pattern is overkill here... it's unlikely that you're going to change from OKAPI -> MTE or vice versa once the system is running (aside from during migration of old -> new platform)
There’s always the risk that the mod-users API will be invoked directly. There isn’t much we can do to prevent that aside from documenting the implications of doing so.
There are ways to address this, but none of them are particularly great:
Add logic in mod-users to call mod-users-keycloak to ensure an AuthUser record is created. Unfortunately this will cause a circular dependency between mod-users and mod-users-keycloak. There are ways to get around this (e.g. incorporate Kafka) but the further we chase this the more complex the solution becomes.
Merge mod-users and mod-users-keycloak into a single module. On one hand this simplifies things. On the other hand, if we want this combined users module to work on both the legacy and Eureka platform, it would probably mean supporting multiple code paths. Regardless of the platform you’re using, there would likely be a non-trivial amount of unreachable code.
Implementation
mod-user-import would need to be adjusted to also (conditionally) call mod-users-keycloak, e.g. based on one or more environment variables.
Pros
There’s no need to check if AuthUser records exist or for just-in-time user creation. Those records should always be present
Cons
There isn’t a simple, reliable way to prevent the direct use of mod-users for user creation
Could potentially become complex.
AuthUser records are created when not needed
Potential performance impact with having a very large number of users in Keycloak
Comparison
Option 1 | Option 2 | |
---|---|---|
Required effort | Moderate - The work is not difficult or complex, but changes are required in several places. | Easy - Moderate (it depends) In it’s simplest form very little effort is required. A more complete solution would require additional effort. |
Solution Completeness | Complete, aside from “demotion” of users, but this doesn’t preclude that from being added later. | Complete aside from preventing direct use of mod-users for user creation/mgmt. |
Performance | Some operations which require checking for AuthUser records and JIT creation may be slower, but these are administrative operations as opposed to librarian operations (check-in/check-out/acquisitions/cataloging/etc.) |
|
Cost | Same | Same |
Security | More secure since AuthUser records exist only for users of the system. | Less secure since all users have AuthUser records. This means patrons and other users in the system are closer to being able to authenticate when really there is no need for that. |
Solution complexity | Moderate | Simple - Complex (it depends) In it’s basic form this solution is very simple. A more complete solution would require significantly more complexity. |
Architectural fit | Good | Less ideal since it’s considered unattractive to have AuthUser records for ALL users |
UX | Dependent on implementation details | Unchanged |
Hosting/admin impact | None | Potentially may need to specify additional environment variables when starting various modules. |
Workaround(s)
One possible workaround which can be leveraged in the short term is to educate users on how different classes or types of users should be created. For instance:
Use mod-user-import for creating many users who will not be authenticating and using the system
Use mod-users-keycoak for creating users who will be using the system (staff, admins, etc.)
etc.
Another workaround is to call the migration API provided by mod-users-keycloak to ensure AuthUser records are created. This would likely require some minor adjustments to this endpoint to be effective.
Conclusion
TBD.
[CAM] My personal preference is for Option #1 but we need to discuss amongst the Eureka architects / team lead.
Spike Status: IN PROGRESS
Open issues & Considerations
…