EUREKA-72: Investigate options consortia UI bundles

EUREKA-72: Investigate options consortia UI bundles

Spike Overview

https://folio-org.atlassian.net/browse/EUREKA-72

Objective: To determine an approach for implementing Automatic Tenant Selection in FOLIO, allowing users to log into their home tenant without being prompted to select it manually, thereby enhancing the user experience across consortia institutions.

Background

FOLIO is a collaborative, open-source library services platform that enables multiple institutions within a consortium to use the same application. Each institution is represented as a separate tenant within FOLIO, and staff users are defined within their institution's tenant context. Users have capabilities assigned to them that define the actions they can perform within their tenant.

Currently, if a user from Institution A (Tenant A) needs to perform actions in Institution B (Tenant B), a shadow user is created in Tenant B with the necessary capabilities. The user must switch affiliations within the FOLIO application to change the tenant context for their session.

Keycloak is used as the Identity and Access Management (IAM) system for FOLIO, with each tenant represented as a separate Realm. Real FOLIO users correspond to Keycloak users with credentials, allowing them to authenticate and log in. Shadow users in FOLIO correspond to Keycloak users without credentials and cannot be used for login operations. Capabilities assigned in FOLIO are reflected as permissions granted to Keycloak users.

The current FOLIO implementation includes:

  • Single UI Bundle: Users log into a central tenant and switch affiliations as needed.

  • Manual Tenant Selection: Users select their tenant from a drop-down menu and are directed to the appropriate UI bundle for their tenant, authenticating in the Keycloak Realm associated with their tenant.

The need has arisen for an Automatic Tenant Selection feature where users are not prompted to select their tenant. Instead, the system detects the user's home tenant upon login and directs them accordingly.

Problem Statement

The spike aims to address the challenge of implementing Automatic Tenant Selection in FOLIO. The goal is to enable users to see a unified login screen and, upon authentication, be automatically directed to their home tenant without manual selection. This requires a method to detect the user's home tenant based on the user’s credentials and adjust the authentication flow accordingly within Keycloak and FOLIO.

Scope

In Scope

  • Investigate methods to automatically detect a user's home tenant during the login process.

  • Explore configurations or extensions in Keycloak to support automatic tenant selection based on user credentials.

  • Assess how automatic tenant selection affects authentication flow, session management, and capability assignments in FOLIO.

  • Ensure the proposed solution aligns with FOLIO's architecture and maintains security and compliance standards.

Out of Scope

  • Implementation or deployment of the automatic tenant selection feature.

  • Modifications to existing tenant switching mechanisms or shadow user management.

  • User interface design changes beyond the login screen.

 

Proposed Solutions

Two possible options have been identified to implement Automatic Tenant Selection in FOLIO:

  1. Custom JavaScript Functions with Login Form Customization

  2. Keycloak Custom Authenticator for All Tenants

  3. Hybrid Approach with Simplified Custom Authenticator and JavaScript Redirection

  4. Custom Keycloak Authenticator for the Central realm

Each option is analyzed in detail below, including high-level sequence diagrams, implementation details, and the pros and cons.

Option 1: Custom JavaScript Functions with Login Form Customization

Overview

This option involves customizing the Keycloak login form using JavaScript to determine the correct tenant (realm) based on the user's input (username). The process includes intercepting the login request, retrieving the user's tenant information by communicating with mod-login-keycloak, and then redirecting the user to authenticate against the appropriate tenant realm.

Sequence Diagram

The high-level sequence diagram for this option is as follows:

Implementation Details

  1. Customize Keycloak Login Page

    • JavaScript Injection: Modify the Keycloak login page to include custom JavaScript code that executes when the user submits their credentials.

    • Intercept Submission: The JavaScript code intercepts the form submission event to prevent the default behavior.

  2. Retrieve User's Tenant

    • HTTP Request to mod-login-keycloak: The JavaScript code sends an HTTP POST request to mod-login-keycloak, passing the entered username and password.

    • Authentication Check: mod-login-keycloak communicates with Keycloak to validate the user's credentials.

    • Tenant Resolution: Upon successful validation, mod-login-keycloak determines the user's home tenant.

  3. Redirect to Tenant's Login Page

    • Construct Tenant URL: The JavaScript code constructs the URL for the tenant's login page based on the tenant information received.

    • Redirection: The JavaScript function requests the tenant-specific login page. It does not render it for the user but sends the authentication Post request to Keycloak.

  4. Authenticate Against Tenant Realm

    • Submit Credentials: The user's credentials are submitted to the tenant realm's for authentication.

    • Validation: Keycloak validates the credentials within the tenant realm.

    • Authentication Success: Upon successful authentication, the user is redirected to the FOLIO landing page.

Pros

  • User Experience

    • Unified Login Screen: Users access a single login screen without the need to select their tenant manually.

    • Seamless Redirection: Redirection to the tenant realm happens transparently to the user.

  • Implementation Simplicity

    • Minimal Backend Changes: The logic is handled primarily on the client side through JavaScript.

    • No Custom Authenticator Required: Avoids the complexity of developing and maintaining a custom Keycloak authenticator.

  • Flexibility

    • Easy Updates: Changes to tenant resolution logic can be applied by updating the JavaScript code.

Cons

  • Security Concerns

    • Credential Exposure Risk: User credentials are handled in the browser and sent over HTTP to mod-login-keycloak before authentication, increasing the risk of interception.

    • Client-Side Vulnerabilities: JavaScript code can be manipulated or disabled by users or malicious actors.

    • Man-in-the-Middle Attacks: Increased risk if HTTPS is not strictly enforced.

    • mod-login-keycloak has to expose unathorized API that can be used for hacking

  • Maintenance Overhead

    • Browser Compatibility: Ensuring the JavaScript functions correctly across all browsers and devices.

    • Debugging Difficulty: Client-side issues can be harder to trace and resolve.

    • mod-login-keycloakextra complexity: mod-login-keycloak must be improved to handle credentials validation and realm resolution.

  • Reliance on Client-Side Logic

    • User Manipulation: Technically savvy users could alter the JavaScript to change the authentication flow.

    • Accessibility Issues: Users with JavaScript disabled or using assistive technologies may encounter problems.

  • Complex Error Handling

    • User Feedback: Providing meaningful error messages is more complex when multiple steps are involved client-side.

JavaScript functions example

function get_realm_link_by_username() { const username = document.getElementById("username").value; // Determine which link to return based on the username content // This is just a stub. The HTTP call to mod-login-keycloak must be done here instead. if (username.includes("coltest")) { const link = document.getElementById("social-college"); return link ? link.href : null; } else if (username.includes("unitest")) { const link = document.getElementById("social-university"); return link ? link.href : null; } // Return null if no matching link is found return null; } async function process_complex_realm_login() { const href = get_realm_link_by_username(); if (!href) { console.error("No matching realm link found for username."); return; } try { // Perform a GET request to the resolved href const response = await fetch(href); // Check if the response is OK (status code 200-299) if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } // Parse the response as text const result = await response.text(); // Create a new DOM parser to convert the HTML string to a document object const parser = new DOMParser(); const doc = parser.parseFromString(result, "text/html"); // Extract the action attribute from the form in the parsed document const fetchedFormAction = doc.querySelector("#kc-form-login")?.action; // Check if the action was found and apply it to the current form, then submit if (fetchedFormAction) { const currentForm = document.getElementById("kc-form-login"); currentForm.action = fetchedFormAction; // Set the current form's action currentForm.method = "post"; // Set the current form's method to POST. // Submit the form programmatically currentForm.submit(); } else { console.error("Form action not found in the fetched content."); } } catch (error) { // Log any errors to the console console.error("Error fetching login link:", error); } }

Summary

While this option offers a quick way to implement Automatic Tenant Selection with minimal backend changes, it introduces significant security risks and maintenance challenges due to its reliance on client-side scripting and handling of user credentials in the browser.

Option 2: Keycloak Custom Authenticator for All Tenants

Overview

This option involves developing a custom Keycloak Authenticator (e.g., FolioAuthenticator) and configuring Keycloak to use this authenticator across all realms configured for consortia and institutional tenants. The custom authenticator handles tenant detection and authentication flow server-side, enhancing security and centralizing the logic.

Sequence Diagram

The high-level sequence diagram for this option is as follows:

Implementation Details

  1. Develop Custom Authenticator (FolioAuthenticator)

    • Server-Side Logic: Implement the authenticator using Keycloak's Service Provider Interface (SPI).

    • Tenant Detection: Incorporate logic to determine the user's home tenant based on the username or other identifiers and configured identity providers.

    • Encrypted Data Handling: Prepare and handle encrypted custom data containing credentials and timestamp for secure transmission between realms.

  2. Configure Keycloak Realms

    • Consortia Realm: Apply the FolioAuthenticator to the central consortia realm.

    • Institutional Realms: Apply the same authenticator to each institutional tenant realm.

    • Authentication Flow: Modify the authentication flow in Keycloak to include the custom authenticator.

  3. Authentication Flow

    • Initial Login Request: User submits credentials to the consortia realm.

    • Tenant Resolution: The custom authenticator checks if the username exists and retrieves the user's identity provider (tenant realm).

    • Redirect Handling: If the user's identity provider is found, the authenticator prepares encrypted custom data and redirects the user to the tenant realm's login page.

    • Fallback Mechanism: If the identity provider is not found, proceed with default authentication in the consortia realm.

  4. Tenant Realm Authentication

    • Data Decryption: The tenant realm's custom authenticator decrypts the received custom data.

    • Credential Validation: The tenant realm validates the user's credentials.

    • Authentication Success: User is authenticated and redirected to the FOLIO landing page.

Pros

  • Enhanced Security

    • Server-Side Credential Handling: Credentials are processed within Keycloak servers, reducing exposure.

    • Encrypted Data Transmission: Sensitive information is encrypted when transmitted between realms.

    • Reduced Attack Surface: Less reliance on client-side scripting minimizes vulnerabilities.

  • Centralized Logic

    • Consistency Across Realms: The custom authenticator standardizes authentication flow for all tenants.

    • Easier Maintenance: Updates to authentication logic are centralized.

  • Improved Performance

    • Streamlined Authentication: Fewer external calls and redirections improve login speed.

    • Scalable Solution: Better suited for handling a large number of tenants.

  • User Experience

    • Seamless Login Flow: Users experience a smooth login process without unnecessary redirects or prompts.

    • Fallback Authentication: Supports default authentication if tenant detection fails.

Cons

  • Development Complexity

    • Custom Authenticator Development: Requires in-depth knowledge of Keycloak's SPI and authentication mechanisms.

    • Testing and Validation: Extensive testing is needed to ensure reliability across different scenarios.

  • Maintenance Overhead

    • Version Compatibility: The custom authenticator must be maintained with Keycloak updates.

    • Configuration Management: All realms must be correctly configured to use the custom authenticator. This configuration must be made transparently during the Consortia configuration. (mgr-tenants? mod-consortia-keycloak? which one should be responsible for that?)

  • Deployment Challenges

    • Rollout Strategy: Deploying changes across multiple realms requires careful planning.

    • Error Handling Complexity: Properly managing authentication errors across realms can be complex.

  • Potential Single Point of Failure

    • Authenticator Dependency: Issues with the custom authenticator could affect authentication for all users.

Authenticator implementation example

The authenticator implementation example can be found here:
https://github.com/folio-org/folio-keycloak-plugins/tree/feature/ecs-folio-authenticator/ecs-folio-authenticator

Summary

This option offers a secure, server-side solution that centralizes authentication logic within Keycloak, enhancing performance and scalability. However, significant development effort and careful maintenance are required to ensure compatibility with Keycloak updates and realm configurations.

Option 3: Hybrid Approach with Simplified Custom Authenticator and JavaScript Redirection

Overview

This hybrid option combines server-side tenant resolution with client-side redirection to implement Automatic Tenant Selection in FOLIO. It utilizes a simplified custom Keycloak Authenticator that only resolves the user's home tenant (realm) based on the provided username and does not handle credential validation or generate encrypted data. The Authenticator either authenticates the user if they belong to the current realm or redirects them to their home tenant's login page. Client-side JavaScript on the login page manages redirections transparently, providing a seamless user experience.

Sequence Diagram

Diagram Explanation

  • Actors and Participants:

    • Folio User: The end-user attempting to log in.

    • Browser: The user's web browser executing JavaScript and handling HTTP requests/responses.

    • Keycloak Consortia Realm (kc_cr): The central Keycloak realm with the simplified custom Authenticator configured.

    • Keycloak Tenant Realm (kc_tr): The user's home tenant realm in Keycloak.

  • Flow Breakdown:

    1. The user accesses the unified login page.

    2. The browser displays the login page.

    3. The user enters their credentials and submits the form.

    4. JavaScript intercepts the form submission and sends an AJAX POST request with the username to the consortia realm (kc_cr).

    5. The simplified custom Authenticator in the consortia realm processes the username to determine the user's home tenant realm.

    6. The Authenticator responds:

      • If the user belongs to the consortia realm, it proceeds with authentication.

      • If the user belongs to a different tenant realm, it responds with a redirect to the appropriate realm's login page.

    7. JavaScript in the browser handles the redirect seamlessly by updating the form action and submitting the form to the tenant realm (kc_tr).

    8. The user is authenticated in their home tenant realm.

    9. Upon successful authentication, the user is redirected to their FOLIO landing page.

Implementation Details

Client-Side JavaScript

  • Event Listener: Adds an event listener to intercept the login form submission.

  • Form Data Collection: Collects the username input by the user.

  • AJAX Request: Sends an asynchronous POST request to the consortia realm's login endpoint with the username.

  • Response Handling:

    • If the response indicates that the user belongs to the consortia realm, the JavaScript proceeds with the authentication flow.

    • If the response is a redirect to the tenant realm, the JavaScript updates the form action to the tenant realm's login endpoint and submits the form.

Simplified Custom Keycloak Authenticator (Server-Side)

  • Consortia Realm Authenticator:

    • Processes the username to determine the user's home tenant realm.

    • If the user belongs to the consortia realm:

      • Proceeds with the standard authentication process (validating credentials).

    • If the user belongs to a different tenant realm:

      • Responds with a redirect to the tenant realm's login page.

  • Tenant Realm Authentication:

    • The tenant realm handles authentication using its standard process.

    • No custom Authenticator is required in the tenant realm.

Security Measures

  • Server-Side Credential Handling: Credentials are only processed on the server side, minimizing exposure.

  • Reduced Client-Side Logic: JavaScript handles redirection without processing credentials.

Pros

Enhanced Security

  • Server-Side Credential Validation: Credentials are validated on the server side, reducing exposure risk.

  • No Credential Handling in JavaScript: Client-side code does not process or transmit credentials.

Improved User Experience

  • Seamless Redirection: JavaScript manages redirections transparently, providing a smooth login process.

  • Unified Login Screen: Users access a single login page without manual tenant selection.

Simplified Implementation

  • Simplified Authenticator: Reduces complexity by only resolving the tenant realm without handling credentials or encryption.

  • No Custom Data Encryption Needed: Eliminates the need for handling encrypted data between realms.

Flexibility

  • Centralized Tenant Resolution: Tenant determination logic is centralized in the consortia realm.

  • Ease of Updates: Changes to tenant resolution logic are confined to the simplified Authenticator.

Cons

CORS Configuration Requirement

  • Cross-Origin Resource Sharing (CORS): Must configure CORS between the FOLIO landing page and Keycloak login form to allow JavaScript redirection using window.location.href = xhr.responseURL;.

    • Implications:

      • Security Considerations: Configuring CORS requires careful management to avoid exposing the application to cross-origin attacks.

      • Additional Complexity: Increases setup and maintenance overhead to ensure proper CORS policies are in place.

      • Browser Behavior: Inconsistent or incorrect CORS configuration may lead to login failures or inconsistent behavior across different browsers.

Development Effort

  • Custom Authenticator Development: Requires development and maintenance of the simplified Keycloak Authenticator.

  • JavaScript Maintenance: Client-side code needs to be maintained and tested across browsers.

Configuration Complexity

  • Realm Configuration: The consortia realm must be configured to use the simplified Authenticator.

  • Deployment Coordination: Synchronizing updates between server-side and client-side components.

Potential Error Handling Challenges

  • Redirection Failures: If JavaScript fails to handle redirects correctly, users may encounter login issues.

  • Fallback Mechanisms: Ensuring proper error messages and fallback options in case of authentication failures.

JavaScript Code Example

https://github.com/folio-org/folio-keycloak/blob/ecs-folio-auth-username-password-form/custom-theme/login/resources/js/login-helper.js

Keycloak Custom Authenticator Example

https://github.com/folio-org/folio-keycloak-plugins/blob/feature/ecs-folio-authenticator-v2/ecs-folio-authenticator/src/main/java/org/folio/authentication/FolioEcsUsernamePasswordForm.java

Summary

This hybrid approach leverages a simplified custom Authenticator in the consortia realm to resolve the user's home tenant based on the username. It does not handle credential validation or encrypted data transmission, reducing complexity while maintaining security. Client-side JavaScript manages redirections seamlessly without processing credentials, providing a unified and user-friendly login experience.

By centralizing tenant resolution and minimizing client-side logic, this option offers a secure and scalable solution for Automatic Tenant Selection in FOLIO. It balances implementation effort with the benefits of enhanced security and improved user experience.

Option 4: Custom Keycloak Authenticator (FolioEcsUsernamePasswordFormV2)

Overview

This option involves developing a custom Keycloak Authenticator (FolioEcsUsernamePasswordFormV2) that dynamically determines the realm to be used for user authentication based on the provided credentials. The Authenticator processes the authentication request within the current realm if the user belongs to it. If the user belongs to a different realm (linked through an Identity Provider), the Authenticator programmatically authenticates the user against that realm without redirecting the user or requiring any client-side JavaScript modifications.

Key Characteristics:

  • Server-Side Processing: All logic is handled on the server side within Keycloak.

  • No Login Form Customization: Does not require any changes to the login page or client-side JavaScript.

  • Enhanced Security: Prevents intruders from deducing user-realm associations, as no redirection or realm information is exposed to the client.

  • Seamless User Experience: Users experience a unified login process without manual tenant selection or noticeable redirections.

Sequence Diagram

Diagram Explanation

  • Actors and Participants:

    • Folio User: The end-user attempting to log in.

    • Browser: The user's web browser.

    • Keycloak with Custom Authenticator (kc_custom): Keycloak realm configured with the FolioEcsUsernamePasswordFormV2 Authenticator.

    • Identity Provider Realm (kc_idp): The user's home tenant realm or external Identity Provider.

  • Flow Breakdown:

    1. The user accesses the unified login page.

    2. The browser displays the login page.

    3. The user enters their credentials and submits the form.

    4. The browser sends the authentication request to Keycloak (kc_custom).

    5. The custom Authenticator in Keycloak processes the credentials:

      • Identifies the realm associated with the user.

      • If the user belongs to the current realm, it proceeds with authentication.

      • If the user belongs to a linked realm or Identity Provider, it programmatically authenticates the user against that realm.

    6. Upon successful authentication (either locally or against the Identity Provider), Keycloak returns the authentication response to the client.

    7. The user is redirected to their FOLIO landing page.

 

Implementation Details

Custom Keycloak Authenticator (FolioEcsUsernamePasswordFormV2)

  • Purpose: To determine the correct realm for user authentication and handle the authentication process accordingly without exposing realm information to the client.

  • Functionality:

    • Realm Identification: Based on the username provided, the Authenticator checks if the user exists in the current realm or is associated with a federated Identity Provider.

    • Local Authentication: If the user belongs to the current realm, it proceeds with standard password validation.

    • Programmatic Authentication Against Linked Realm:

      • If the user is linked to an Identity Provider, the Authenticator programmatically performs authentication against that Identity Provider using the provided credentials.

      • Utilizes the OAuth 2.0 Resource Owner Password Credentials Grant to obtain an access token from the Identity Provider.

      • Determines authentication success based on the response from the Identity Provider.

  • Security Measures:

    • Server-Side Credential Handling: Credentials are only handled within the Keycloak server environment.

    • No Exposure of Realm Information: Does not redirect the user or expose any realm-specific URLs or data to the client.

    • Brute-Force Protection: By not revealing user-realm associations, it prevents attackers from inferring relationships and targeting specific realms.

Key Components of the Authenticator Code:

  • Action Method:

    • Intercepts the authentication flow and retrieves the user based on the provided username.

    • Checks for federated identities associated with the user.

  • Password Validation:

    • If the user is linked to a federated Identity Provider, it performs programmatic authentication against that provider.

    • Otherwise, it proceeds with standard password validation.

  • Programmatic Authentication Against OIDC Identity Provider:

    • Constructs a token request to the Identity Provider's token endpoint.

    • Includes client credentials, username, and password in the request.

    • Parses the response to determine if authentication was successful.

Keycloak Configuration:

  • Authenticator Setup:

    • The custom Authenticator (FolioEcsUsernamePasswordFormV2) must be deployed to the Keycloak server.

    • Configure the authentication flow to include the custom Authenticator.

  • Identity Providers Configuration:

    • Identity Providers (other realms) must be set up in Keycloak with appropriate client credentials.

    • Ensure that the Identity Providers allow authentication using the Resource Owner Password Credentials Grant.

Pros

Enhanced Security

  • No Realm Information Exposure: Prevents intruders from discovering user-realm associations, as no redirection or realm hints are provided to the client.

  • Server-Side Credential Handling: Credentials are processed entirely on the server side, reducing the risk of interception.

  • Brute-Force Protection: Limits the ability of attackers to target specific realms or users.

Simplified User Experience

  • Unified Login Process: Users access a single login page without manual tenant selection or noticeable redirections.

  • Seamless Authentication: The authentication process is transparent to the user, regardless of their home realm.

No Client-Side Modifications Required