In this article, Janua’s CTO shares tips and tricks about understanding Keycloak user Federation

1 Understanding Keycloak user Federation

1.1 Overview

Keycloak comes up with a user storage SPI. By default Keycloak comes with 3 different user storage federation adapters:

The customer can write his own user storage implementation using the userstorage SPI. This can be very handy in case of former migration an old customer tdatabase (such as SQL or other) to a LDAP database. The customer has only to implement a a specific user storage plugin during the migration time.

1.2 User Federation storage Provider

https://www.keycloak.org/docs/latest/server_admin/index.html#_ldap

Out of the box we have support for LDAP and Active Directory and Kerberos. You can also code your own extension for any custom user databases you might have using our User Storage SPI.

The way it works is that when a user logs in, Keycloak will look into its own internal user store to find the user. If it can’t find it there it will iterate over every User Storage provider you have configured for the realm until it finds a match. Data from the external store is mapped into a common user model that is consumed by the Keycloak runtime.

1.3 Keycloak default local userstorage (SQL database)
1.3.1 Synchronizing LDAP users to keycloak

By default, Keycloak will import users from LDAP into the local Keycloak user database (sql database)

• synchronized is done on demand, or through a periodic background task.

• Only Passwords are not imported and password validation is delegated to the LDAP server.

Advantages:

• The beneficial Keycloak features will work as any extra per-user data that is needed can be stored locally.

• This approach also reduces load on the LDAP server as uncached users are loaded from the Keycloak database the 2nd time they are accessed.

• only load your LDAP server will have is password validation.

Disadvantage:

• The downside to this approach is that when a user is first queried, this will require a Keycloak database insert.

• import will also have to be synchronized with your LDAP server as needed.

Alternatively, you can choose not to import users into the Keycloak user database. In this case, the common user model that the Keycloak runtime uses is backed only by the LDAP server

• storage mode is controlled by the Import Users switch.

• Users are imported when this flag is to ON (as above on the screenshot)

• It is possible to define the synchronization frequency (periodically, or or demand).

• When this flag is to OFF, users attributes are read from LDAP

1.3.2 Synchronizing newly created Keycloak users to LDAP

The flag Sync Registration when enabled, indicates that newly created keycloak user have to be created on LDAP as well.

1.3.3 Dealing with keycloak – LDAP synchronization parameter

Keycloak LDAP sync strategy has to be has to be adjusted and tailored according to customer needs.

The most effective way to use synchronization is to base synchronization on LDAP user change log (i.e enable periodic Change Users Sync)

1.4 Using Keycloak user Federation SPI

https://www.keycloak.org/docs/latest/server_development/index.html#_user-storage-spi

https://www.keycloak.org/docs/latest/server_development/index.html#provider-capability-interfaces

Keycloak defines the following user provider interfaces

SPIDescription
org.keycloak.storage.user.UserLookupProviderThis interface is required if you want to be able to log in with users from this external store. Most (all?) providers implement this interface.
org.keycloak.storage.user.UserQueryProviderDefines complex queries that are used to locate one or more users. You must implement this interface if you want to view and manage users from the administration console.
org.keycloak.storage.user.UserRegistrationProviderImplement this interface if your provider supports adding and removing users.
org.keycloak.storage.user.UserBulkUpdateProviderImplement this interface if your provider supports bulk update of a set of users.
org.keycloak.credential.CredentialInputValidatorImplement this interface if your provider can validate one or more different credential types (for example, if your provider can validate a password).
org.keycloak.credential.CredentialInputUpdaterImplement this interface if your provider supports updating one or more different credential types.
1.5 Using Keycloak Provider interfaces

Keycloak comes with 2 samples which are:

• keycloak user-storage-simple

https://github.com/keycloak/keycloak-quickstarts/tree/latest/user-storage-simple

• keycloak user-storage-jpa

https://github.com/keycloak/keycloak-quickstarts/tree/latest/user-storage-jpa

Those both examples can be downloaded from keycloak quickstart

1.6 keycloak user storage simple (read-only)
1.6.1 Deploying providers

The user has to compile and deploy the example, which entails the creation of 2 providers:

• read-only-property-file

• write-only-property-file

step 0

starting keycloak

sh standalone.sh

step 1

deploying the providers

cd user-storage-simple

mvn clean install wildfly:deploy

Step2

Selecting the newly created provider

and click “save”

step 3

The user is defined is in the file users.properties

user-storage-simple/src/main/resources$ cat users.properties
#
tbrady=superbowl

Step 4 – Logging

It is possible to use the account realm client-credentials

Once logged, is displayed the credentials.

1.7 User storega simple provider (write only)
1.7.1 Configuring the write only provider

The write only provider behaves in the same way, as previous one.

You have to enter the path of your write only provider file

1.7.2 example-user.properties

In example-users.properties, the entries user1, user2, user3 are created

orivat@asus:~/dev/keycloak_4.83/keycloak-quickstarts-latest/user-storage-simple$ cat /tmp/example-users.properties
#
#Mon Feb 25 10:37:20 CET 2019
user1=password
user2=password
uers3=password

1.7.3 Logging to keycloak

It possible to log to the keycloak account app

The keycloak user session has been created as well

1.7.4 Displaying all the users

When using the keycloak quickstart gitlab sources, you need to modify the sources as follows, if you want to display the users within the keycloak admin console

(modif 1)

@Override
public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
List users = new LinkedList<>();
int i = 0;
for (Object obj : properties.keySet()) {
String username = (String)obj;
///modif
if ((search !=null) && (!username.contains(search))) continue;
if (i++ < firstResult) continue;
UserModel user = getUserByUsername(username, realm);
users.add(user);
if (users.size() >= maxResults) break;
}
return users;
}

(modif 2)

@Override
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
// only support searching by username
String usernameSearchString = params.get(« username »);
// does not exit
// in case user are displayed
// if (usernameSearchString == null) return Collections.EMPTY_LIST;
return searchForUser(usernameSearchString, realm, firstResult, maxResults);
}

1.8 Keycloak user storage JPA provider
1.8.1 Presentation

Keycloak provides out of the box an implementation for JavaEE apps using JPA, allowing to connect to SQL database.

https://www.keycloak.org/docs/latest/server_development/index.html#leveraging-java-ee-2

1.8.2 Using JPA

JPA is de facto a standard when using SQL database,and allows to perform sql queries from the java app whithout being knowledgeable about sql

Some key features of JPA with SQL:

• Named queries

◦ it allows to write/define high level queries from Java

• Entity Manager (Persitence Manager)

▪ An EntityManager instance may represent either a remote connection to a remote database server

• Entity

◦ JPA Entity classes are user defined classes whose instances can be stored in a database.

When deploying a Jpa app, those are some of the key elepments to pay attention to.

Pointers:

https://www.objectdb.com/java/jpa

https://examples.javacodegeeks.com/enterprise-java/jpa/jpa-criteriabuilder-example/

https://www.logicbig.com/tutorials/java-ee-tutorial/jpa/connecting-oracle.html

1.8.3 Keycloak user storage jpa example

Keycloak provides a user-storage-jpa example,in the keycloak quickstart guide

This example is created a XA database at deployment, but breaks later

step 0

Keycloak needs to be launched as follows:

sh standalone.sh -Djboss.socket.binding.port-offset=100 –debug 8100

Step1

From

https://github.com/keycloak/keycloak-quickstarts/tree/latest/user-storage-jpa

You must first deploy the datasource it uses. Start up the Keycloak server. Then in the directory of this example type the following maven command:

mvn -Padd-datasource install

The command mvn -Padd-datasource install is failing as follows:

mvn -Padd-datasource install
[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building Keycloak Quickstart: user-storage-jpa 4.8.3.Final
[
INFO: ELY00001: WildFly Elytron version 1.1.7.Final
[INFO] ————————————————————————
[INFO] BUILD FAILURE
[INFO] ————————————————————————
[INFO] Total time: 3.226 s
[INFO] Finished at: 2019-02-25T11:32:09+01:00
[INFO] Final Memory: 32M/388M
[INFO] ————————————————————————
[ERROR] Failed to execute goal org.wildfly.plugins:wildfly-maven-plugin:1.2.2.Final:add-resource (add-datasource) on project keycloak-user-storage-jpa: Could not execute goal add-resource. Reason: Operation failed: {« WFLYCTL0062: Composite operation failed and was rolled back. Steps that failed: » => {« Operation step-1 » => « WFLYCTL0158: Operation handler failed: org.jboss.msc.service.DuplicateServiceException: Service jboss.data-source.\ »jboss.naming.context.java.jboss.datasources.ExampleXADS\ » is already registered »}} -> [Help 1]
………….
………
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] https://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

Some internal analysis have showcased that the file pom.xml is incorrect :

orivat@asus:~/dev/keycloak_4.83/keycloak-quickstarts-latest/user-storage-jpa$ diff pom.xml.good pom.xml
148c148
< <address>xa-data-source=ExampleXADS</address>

> <address>xa-data-source=java:jboss/datasources/ExampleXADS</address>
the correct line is:
<address>xa-data-source=ExampleXADS</address>

You should update your pom.xml accordingly

Once you have updted your pom.xml, creation of the datasource should proceed quietly

1.8.4 Testing XA data source with keycloak console management

A possible way to proceed with XA data source testing is to use the keycloak admin management console.

The admin management console is launched at URL 10090

https://localhost:10090/console

You also need to have created an admin management user to use the console using add-user.sh.

Like this, you can see all datasources, and also test that the datasource is enabled.

1.8.5 Testing with with the Ejb appl

You need to deploy the app manually

Jboss-cli.sh
connect localhost:10090
[standalone@localhost:10090 /] deploy –force /home/orivat/dev/keycloak_4.83/keycloak-quickstarts-latest/user-storage-jpa/target/user-storage-jpa-example.jar

1.8.6 Rendering users visible in the admin console

By default, cretaed users are not visible in the amin console

You need to do 2 code mode modification to render them visible in the admin console

(modif1)

@Override
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
//return Collections.EMPTY_LIST;
return getUsers(realm, firstResult, maxResults);
}

(modif2)

The following method needs to be updated as follows:

/*
*
* @Override
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
List<UserModel> users = new LinkedList<>();
int i = 0;
for (Object obj : properties.keySet()) {
String username = (String)obj;
if ((search !=null) && (!username.contains(search))) continue;
if (i++ < firstResult) continue;
UserModel user = getUserByUsername(username, realm);
users.add(user);
if (users.size() >= maxResults) break;
}
return users;
}

1.9 Pointers

https://www.keycloak.org/docs/latest/server_development/index.html#leveraging-java-ee-2

https://www.keycloak.org/docs/latest/server_admin/index.html#_user-storage-federation

https://www.keycloak.org/docs-api/4.8/javadocs/index.html

janua
Les derniers articles par janua (tout voir)