2015-01-28 09:48:26 +00:00
|
|
|
<chapter id="providers">
|
|
|
|
<title>Providers and SPIs</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Keycloak is designed to cover most use-cases without requiring custom code, but we also want it to be
|
|
|
|
customizable. To achive this Keycloak has a number of SPIs which you can implement your own providers for.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Implementing a SPI</title>
|
|
|
|
<para>
|
|
|
|
To implement an SPI you need to implement it's ProviderFactory and Provider interfaces. You also need to
|
|
|
|
create a provider-configuration file. For example to implement the Event Listener SPI you need to implement
|
|
|
|
EventListenerProviderFactory and EventListenerProvider and also provide the file
|
|
|
|
<literal>META-INF/services/org.keycloak.events.EventListenerProviderFactory</literal>
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
For example to implement the Event Listener SPI you start by implementing EventListenerProviderFactory:
|
2015-11-10 11:55:36 +00:00
|
|
|
<programlisting><![CDATA[
|
2015-01-28 09:48:26 +00:00
|
|
|
package org.acme.provider;
|
|
|
|
|
|
|
|
import ...
|
|
|
|
|
|
|
|
public class MyEventListenerProviderFactory implements EventListenerProviderFactory {
|
|
|
|
|
|
|
|
private List<Event> events;
|
|
|
|
|
|
|
|
public String getId() {
|
|
|
|
return "my-event-listener";
|
|
|
|
}
|
|
|
|
|
|
|
|
public void init(Config.Scope config) {
|
|
|
|
int max = config.getInt("max");
|
|
|
|
events = new MaxList(max);
|
|
|
|
}
|
|
|
|
|
|
|
|
public EventListenerProvider create(KeycloakSession session) {
|
|
|
|
return new MyEventListenerProvider(events);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void close() {
|
|
|
|
events = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2015-11-10 11:55:36 +00:00
|
|
|
]]></programlisting>
|
2015-03-20 07:39:56 +00:00
|
|
|
The example uses an imagined MaxList which has a maximum size and is concurrency safe. When the maximum size is reached
|
2015-01-28 09:48:26 +00:00
|
|
|
and new entries are added the oldest entry is removed. Keycloak creates a single instance of
|
|
|
|
EventListenerProviderFactory which makes it possible to store state for multiple requests. EventListenerProvider
|
|
|
|
instances are created by calling create on the factory for each requests so these should be light-weight.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Next you would implement EventListenerProvider:
|
2015-11-10 11:55:36 +00:00
|
|
|
<programlisting><![CDATA[
|
2015-01-28 09:48:26 +00:00
|
|
|
package org.acme.provider;
|
|
|
|
|
|
|
|
import ...
|
|
|
|
|
|
|
|
public class MyEventListenerProvider implements EventListenerProvider {
|
|
|
|
|
|
|
|
private List<Event> events;
|
|
|
|
|
|
|
|
public MyEventListenerProvider(List<Event> events) {
|
|
|
|
this.events = events;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onEvent(Event event) {
|
|
|
|
events.add(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void close() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2015-11-10 11:55:36 +00:00
|
|
|
]]></programlisting>
|
2015-01-28 09:48:26 +00:00
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
The file <literal>META-INF/services/org.keycloak.events.EventListenerProviderFactory</literal> should
|
|
|
|
contain the full name of your ProviderFactory implementation:
|
2015-02-14 10:07:25 +00:00
|
|
|
<programlisting><![CDATA[org.acme.provider.MyEventListenerProviderFactory]]></programlisting>
|
2015-01-28 09:48:26 +00:00
|
|
|
</para>
|
2015-11-10 11:55:36 +00:00
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Show info from you SPI implementation in Keycloak admin console</title>
|
|
|
|
<para>
|
|
|
|
Sometimes it is useful to show additional info about your Provider to a Keycloak administrator.
|
|
|
|
You can show provider build time informations (eg. version of custom provider currently installed),
|
|
|
|
current configuration of the provider (eg. url of remote system your provider talks to) or some operational
|
|
|
|
info (average time of response from remote system your provider talks to).
|
|
|
|
Keycloak admin console provides Server Info page to show this kind of information.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
To show info from your provider it is enough to implement
|
|
|
|
<literal>org.keycloak.provider.ServerInfoAwareProviderFactory</literal> interface in your ProviderFactory.
|
|
|
|
Example implementation for MyEventListenerProviderFactory from previous example:
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
package org.acme.provider;
|
|
|
|
|
|
|
|
import ...
|
|
|
|
|
|
|
|
public class MyEventListenerProviderFactory implements EventListenerProviderFactory, ServerInfoAwareProviderFactory {
|
|
|
|
|
|
|
|
private List<Event> events;
|
|
|
|
private int max;
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void init(Config.Scope config) {
|
|
|
|
max = config.getInt("max");
|
|
|
|
events = new MaxList(max);
|
|
|
|
}
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Map<String, String> getOperationalInfo() {
|
|
|
|
Map<String, String> ret = new LinkedHashMap<>();
|
|
|
|
ret.put("version", "1.0");
|
|
|
|
ret.put("listSizeMax", max + "");
|
|
|
|
ret.put("listSizeCurrent", events.size() + "");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
]]></programlisting>
|
|
|
|
|
|
|
|
</para>
|
|
|
|
</section>
|
2015-01-28 09:48:26 +00:00
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Registering provider implementations</title>
|
|
|
|
<para>
|
2015-03-06 06:13:15 +00:00
|
|
|
Keycloak can load provider implementations from JBoss Modules or directly from the file-system. Using Modules
|
|
|
|
is recommended as you can control exactly what classes are available to your provider. Any providers loaded
|
|
|
|
from the file-system uses a classloader with the Keycloak classloader as its parent.
|
2015-01-28 09:48:26 +00:00
|
|
|
</para>
|
2015-03-06 06:13:15 +00:00
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Register a provider using Modules</title>
|
|
|
|
<para>
|
2015-03-31 12:42:58 +00:00
|
|
|
To register a provider using Modules first create a module. To do this you can either use the jboss-cli
|
|
|
|
script or manually create a folder inside KEYCLOAK_HOME/modules and add your jar and a <literal>module.xml</literal>.
|
|
|
|
For example to add the event listener sysout example provider using the jboss-cli script execute:
|
2015-11-10 11:55:36 +00:00
|
|
|
<programlisting><![CDATA[
|
2016-01-15 23:44:17 +00:00
|
|
|
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-events-api"
|
2015-11-10 11:55:36 +00:00
|
|
|
]]></programlisting>
|
2015-03-31 12:42:58 +00:00
|
|
|
Or to manually create it start by creating the folder <literal>KEYCLOAK_HOME/modules/org/keycloak/examples/event-sysout/main</literal>.
|
|
|
|
Then copy <literal>event-listener-sysout-example.jar</literal> to this folder and create <literal>module.xml</literal>
|
2015-03-06 06:13:15 +00:00
|
|
|
with the following content:
|
2015-11-10 11:55:36 +00:00
|
|
|
<programlisting><![CDATA[
|
2015-03-06 06:13:15 +00:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.examples.event-sysout">
|
|
|
|
<resources>
|
|
|
|
<resource-root path="event-listener-sysout-example.jar"/>
|
|
|
|
</resources>
|
|
|
|
<dependencies>
|
|
|
|
<module name="org.keycloak.keycloak-core"/>
|
2016-01-15 23:44:17 +00:00
|
|
|
<module name="org.keycloak.keycloak-server-spi"/>
|
2015-03-06 06:13:15 +00:00
|
|
|
</dependencies>
|
|
|
|
</module>
|
2015-11-10 11:55:36 +00:00
|
|
|
]]></programlisting>
|
2015-03-31 12:42:58 +00:00
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Once you've created the module you need to register this module with Keycloak. This is done by editing
|
|
|
|
keycloak-server.json and adding it to the providers:
|
2015-03-06 06:13:15 +00:00
|
|
|
<programlisting><![CDATA[{
|
2015-03-20 07:39:56 +00:00
|
|
|
"providers": [
|
|
|
|
...
|
|
|
|
"module:org.keycloak.examples.event-sysout"
|
|
|
|
]
|
2015-03-06 06:13:15 +00:00
|
|
|
}]]></programlisting>
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Register a provider using file-system</title>
|
|
|
|
<para>
|
|
|
|
To register your provider simply copy the JAR including the ProviderFactory and Provider classes and the
|
|
|
|
provider configuration file to <literal>standalone/configuration/providers</literal>.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
You can also define multiple provider class-path if you want to create isolated class-loaders. To do this
|
|
|
|
edit keycloak-server.json and add more classpath entries to the providers array. For example:
|
2015-01-28 09:48:26 +00:00
|
|
|
<programlisting><![CDATA[{
|
|
|
|
"providers": [
|
|
|
|
"classpath:provider1.jar;lib-v1.jar",
|
|
|
|
"classpath:provider2.jar;lib-v2.jar"
|
|
|
|
]
|
|
|
|
}]]></programlisting>
|
2015-03-06 06:13:15 +00:00
|
|
|
The above example will create two separate class-loaders for providers. The classpath entries follow the
|
|
|
|
same syntax as Java classpath, with ';' separating multiple-entries. Wildcard is also supported allowing
|
|
|
|
loading all jars (files with .jar or .JAR extension) in a folder, for example:
|
2015-01-28 09:48:26 +00:00
|
|
|
<programlisting><![CDATA[{
|
|
|
|
"providers": [
|
|
|
|
"classpath:/home/user/providers/*"
|
|
|
|
]
|
2015-03-20 07:39:56 +00:00
|
|
|
}]]></programlisting>
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Configuring a provider</title>
|
|
|
|
<para>
|
|
|
|
You can pass configuration options to your provider by setting them in <literal>keycloak-server.json</literal>.
|
|
|
|
For example to set the max value for <literal>my-event-listener</literal> add:
|
|
|
|
<programlisting><![CDATA[{
|
|
|
|
"eventsListener": {
|
|
|
|
"my-event-listener": {
|
|
|
|
"max": 100
|
|
|
|
}
|
|
|
|
}
|
2015-01-28 09:48:26 +00:00
|
|
|
}]]></programlisting>
|
2015-03-06 06:13:15 +00:00
|
|
|
</para>
|
|
|
|
</section>
|
2015-01-28 09:48:26 +00:00
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Available SPIs</title>
|
|
|
|
<para>
|
|
|
|
Here's a list of the available SPIs and a brief description. For more details on each SPI refer to
|
|
|
|
individual
|
|
|
|
sections.
|
|
|
|
<variablelist>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Account</term>
|
|
|
|
<listitem>
|
|
|
|
Provides the account manage console pages. The default implementation uses FreeMarker templates.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Connections Infinispan</term>
|
|
|
|
<listitem>
|
|
|
|
Loads and configures Infinispan connections. The default implementation can load connections
|
|
|
|
from
|
|
|
|
the Infinispan subsystem, or alternatively can be manually configured in keycloak-server.json.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Connections Jpa</term>
|
|
|
|
<listitem>
|
2015-02-14 10:07:25 +00:00
|
|
|
Loads and configures Jpa connections. The default implementation can load datasources
|
2015-01-28 09:48:26 +00:00
|
|
|
from
|
|
|
|
WildFly/EAP, or alternatively can be manually configured in keycloak-server.json.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Connections Jpa Updater</term>
|
|
|
|
<listitem>
|
|
|
|
Updates database schema. The default implementation uses Liquibase.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Connections Mongo</term>
|
|
|
|
<listitem>
|
|
|
|
Loads and configures MongoDB connections. The default implementation is configured in
|
|
|
|
keycloak-server.json.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Email</term>
|
|
|
|
<listitem>
|
|
|
|
Formats and sends email. The default implementation uses FreeMarker templates and JavaMail.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Events Listener</term>
|
|
|
|
<listitem>
|
|
|
|
Listen to user related events for example user login success and failures. Keycloak provides two
|
|
|
|
implementations out of box. One that logs events to the server log and another that can send
|
|
|
|
email
|
|
|
|
notifications to users on certain events.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Events Store</term>
|
|
|
|
<listitem>
|
|
|
|
Store user related events so they can be viewed through the admin console and account management
|
|
|
|
console.
|
|
|
|
Keycloak provides implementations for Relational Databases and MongoDB.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Export</term>
|
|
|
|
<listitem>
|
|
|
|
Exports the Keycloak database. Keycloak provides implementations that export to JSON files
|
|
|
|
either
|
2015-02-14 10:07:25 +00:00
|
|
|
as a single file, multiple files in a directory or a encrypted ZIP archive.
|
2015-01-28 09:48:26 +00:00
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Import</term>
|
|
|
|
<listitem>
|
2015-02-14 10:07:25 +00:00
|
|
|
Imports an exported Keycloak database. Keycloak provides implementations that import from JSON
|
2015-01-28 09:48:26 +00:00
|
|
|
files either
|
2015-02-14 10:07:25 +00:00
|
|
|
as a single file, multiple files in a directory or a encrypted ZIP archive.
|
2015-01-28 09:48:26 +00:00
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Login</term>
|
|
|
|
<listitem>
|
|
|
|
Provides the login pages. The default implementation uses FreeMarker templates.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Login Protocol</term>
|
|
|
|
<listitem>
|
|
|
|
Provides protocols. Keycloak provides implementations of OpenID Connect and SAML 2.0.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Realm</term>
|
|
|
|
<listitem>
|
|
|
|
Provides realm and application meta-data. Keycloak provides implementations for Relational
|
|
|
|
Databases
|
|
|
|
and MongoDB.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Realm Cache</term>
|
|
|
|
<listitem>
|
|
|
|
Caches realm and application meta-data to improve performance. Keycloak provides a basic
|
|
|
|
in-memory
|
|
|
|
cache and a Infinispan cache.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Theme</term>
|
|
|
|
<listitem>
|
|
|
|
Allows creating themes to customize look and feel. Keycloak provides implementations that can
|
|
|
|
load
|
|
|
|
themes from the file-system or classpath.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>Timer</term>
|
|
|
|
<listitem>
|
|
|
|
Executes scheduled tasks. Keycloak provides a basic implementation based on java.util.Timer.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>User</term>
|
|
|
|
<listitem>
|
|
|
|
Provides users and role-mappings. Keycloak provides implementations for Relational Databases
|
|
|
|
and MongoDB.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>User Cache</term>
|
|
|
|
<listitem>
|
|
|
|
Caches users and role-mappings to improve performance. Keycloak provides a basic in-memory
|
|
|
|
cache and a Infinispan cache.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>User Federation</term>
|
|
|
|
<listitem>
|
|
|
|
Support syncing users from an external source. Keycloak provides implementations for LDAP and
|
|
|
|
Active Directory.
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>User Sessions</term>
|
|
|
|
<listitem>
|
|
|
|
Provides users session information. Keycloak provides implementations for basic in-memory,
|
|
|
|
Infinispan,
|
|
|
|
Relational Databases and MongoDB
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
</variablelist>
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
</chapter>
|