The User Storage SPI of Keycloak can be used to write extensions that connect to external user databases and credential stores for customized user federation. The built-in LDAP and ActiveDirectory providers are an implementation of the SPI in default Keycloak distributions.
This example demonstrates how to write such an extension using a JSON file based user repository as the external user & credential database. The JSON file will be created as userDB.json
under the user's home directory the first time this extension is run.
The code base is developed by extending the example given in this documentation from RedHat.
By following the factory design pattern, a FileUserStorageProviderFactory
creates a FileUserStorageProvider
instance in each Keycloak transaction, which further leverages a UserModel
instance as an adapter to bridge to the custom user model defined in the file based repository.
The FileUserStorageProvider
implements these provider capability interfaces to enable user lookup & update, authentication, query, registration & removal.
During development, this problem was raised up as the author had little idea on how to persist user data back to the repository.
As a coarse solution, the author decided to put file data persistence step in the close()
method which executes at the end of a transaction. This code can be found in the import_strategy
branch.
Update by author at a later stage
A self-defined Keycloak transaction is enlisted after the main authentication transaction to persist user data. This occurs in the setAttribute()
method of the user model adapter, found in both the master
and demo
branches.
This implementation is found in the master
branch. It leverages the federated user storage feature provided by Keycloak, and only stores additional attributes and role mappings etc. that cannot be handled by the external storage into the local database.
A UserModel
instance is created by inheriting the abstract AbstractUserAdapterFederatedStorage
class, which provides customized getters and setters to manage user data via either federated or external storage, or both.
The demo
branch shows how certain attributes can be skipped by the federated user storage by overriding the setAttribute()
method.
This implementation is found in the import_strategy
branch. It leverages the user import feature provided by Keycloak and creates a local copy of each user in its own database.
The ImportedUserValidationInterface
provides a way to delegate imported users and override their default behavior. In this example, there is a requirement that further attribute setting needs to be avoided on imported users, hence the setAttribute()
function is overridden to avoid that.
mvn clean install
creates a jar
file in the target folder.
Keycloak server version: 6.0.1
- Create a file with path
META-INF/services/org.keycloak.storage.UserStorageProviderFactory
which contains the classname of the provider factory (included in this repo) - Run the build command to obtain the
jar
artifact - Copy the
jar
file tokeycloak-6.0.1/standalone/deployments
found in Keycloak's runtime directory, and the Keycloak server should be able to detect and install it - Open the admin console of the Keycloak server, and add in this customized provider in the User Federation section
Suppose there is a web application that leverages this Keycloak server for identity and access management.
- Users in the file based repository get loaded into Keycloak's internal user database
- Successful login with such users
- Brokered external SAML/OIDC identify provider
- Logged-in users get loaded/updated into Keyloak's internal user database as well as the file based repository
- Non-common attributes (e.g.
favouriteLine
in this code repo) are successfully managed (skip setting when needed) as long as they are mapped correctly
- Once a user record is removed via the admin console, it also gets deleted from the file based repository
Ruifeng Ma (mrfflyer@gmail.com)