2016-04-18 21:02:18 +00:00
[[_providers]]
2016-05-13 08:28:41 +00:00
== Service Provider Interfaces (SPI)
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
{project_name} is designed to cover most use-cases without requiring custom code, but we also want it to be customizable.
To achieve this {project_name} has a number of Service Provider Interfaces (SPI) for which you can implement your own providers.
2016-04-18 21:02:18 +00:00
2019-01-15 13:24:22 +00:00
=== Implementing an SPI
2016-04-18 21:02:18 +00:00
2017-06-15 19:22:34 +00:00
To implement an SPI you need to implement its ProviderFactory and Provider interfaces. You also need to create a service configuration file.
2016-04-18 21:02:18 +00:00
2019-01-15 13:24:22 +00:00
For example, to implement the Theme Selector SPI you need to implement ThemeSelectorProviderFactory and ThemeSelectorProvider and also provide the file
2018-02-07 14:41:56 +00:00
`META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory`.
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
Example ThemeSelectorProviderFactory:
2016-05-13 08:28:41 +00:00
[source,java]
2016-04-18 21:02:18 +00:00
----
package org.acme.provider;
import ...
2018-02-07 14:41:56 +00:00
public class MyThemeSelectorProviderFactory implements ThemeSelectorProviderFactory {
2016-04-18 21:02:18 +00:00
2019-04-18 15:37:06 +00:00
@Override
public ThemeSelectorProvider create(KeycloakSession session) {
return new MyThemeSelectorProvider(session);
}
2016-04-18 21:02:18 +00:00
2019-04-18 15:37:06 +00:00
@Override
public void init(Config.Scope config) {
}
2016-04-18 21:02:18 +00:00
2019-04-18 15:37:06 +00:00
@Override
public void postInit(KeycloakSessionFactory factory) {
}
2016-06-08 06:10:04 +00:00
2019-04-18 15:37:06 +00:00
@Override
public void close() {
}
2016-04-18 21:02:18 +00:00
2019-04-18 15:37:06 +00:00
@Override
public String getId() {
return "myThemeSelector";
}
2016-04-18 21:02:18 +00:00
}
----
2018-02-07 14:41:56 +00:00
NOTE: Keycloak creates a single instance of provider factories which makes it possible to store state for multiple requests.
2019-01-15 13:24:22 +00:00
Provider instances are created by calling create on the factory for each request so these should be light-weight object.
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
Example ThemeSelectorProvider:
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
[source,java]
2016-04-18 21:02:18 +00:00
----
package org.acme.provider;
import ...
2018-04-06 05:32:03 +00:00
public class MyThemeSelectorProvider implements ThemeSelectorProvider {
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
public MyThemeSelectorProvider(KeycloakSession session) {
2016-04-18 21:02:18 +00:00
}
2016-06-08 06:10:04 +00:00
@Override
2018-02-07 14:41:56 +00:00
public String getThemeName(Theme.Type type) {
return "my-theme";
2016-06-08 06:10:04 +00:00
}
2018-04-06 05:32:03 +00:00
2019-04-18 15:37:06 +00:00
@Override
2018-04-06 05:32:03 +00:00
public void close() {
2019-04-18 15:37:06 +00:00
}
2016-04-18 21:02:18 +00:00
}
2016-05-13 08:28:41 +00:00
----
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
Example service configuration file (`META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory`):
2016-04-18 21:02:18 +00:00
[source]
----
2018-02-07 14:41:56 +00:00
org.acme.provider.MyThemeSelectorProviderFactory
2016-05-13 08:28:41 +00:00
----
2016-04-18 21:02:18 +00:00
2016-09-01 05:04:26 +00:00
You can configure your provider through `standalone.xml`, `standalone-ha.xml`, or `domain.xml`.
2017-08-28 12:50:14 +00:00
See the link:{installguide_link}[{installguide_name}] for more details on
2019-01-15 13:24:22 +00:00
where to find these files.
2016-04-18 21:02:18 +00:00
2016-09-01 05:04:26 +00:00
For example by adding the following to `standalone.xml`:
[source,xml]
2016-05-13 08:28:41 +00:00
----
2018-02-07 14:41:56 +00:00
<spi name="themeSelector">
<provider name="myThemeSelector" enabled="true">
2016-09-01 05:04:26 +00:00
<properties>
2018-02-07 14:41:56 +00:00
<property name="theme" value="my-theme"/>
2016-09-01 05:04:26 +00:00
</properties>
</provider>
</spi>
2016-05-13 08:28:41 +00:00
----
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
Then you can retrieve the config in the `ProviderFactory` init method:
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
[source,java]
----
public void init(Config.Scope config) {
2018-02-07 14:41:56 +00:00
String themeName = config.get("theme");
2016-05-13 08:28:41 +00:00
}
2016-04-18 21:02:18 +00:00
----
2016-05-13 08:28:41 +00:00
Your provider can also lookup other providers if needed. For example:
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
[source,java]
----
2018-02-07 14:41:56 +00:00
public class MyThemeSelectorProvider implements EventListenerProvider {
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
private KeycloakSession session;
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
public MyThemeSelectorProvider(KeycloakSession session) {
2016-05-13 08:28:41 +00:00
this.session = session;
}
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
@Override
public String getThemeName(Theme.Type type) {
return session.getContext().getRealm().getLoginTheme();
2016-04-18 21:02:18 +00:00
}
2016-05-13 08:28:41 +00:00
}
----
2016-06-08 07:08:38 +00:00
[[_providers_admin_console]]
2018-02-07 14:41:56 +00:00
==== Show info from your SPI implementation in admin console
2016-05-13 08:28:41 +00:00
2019-01-15 13:24:22 +00:00
Sometimes it is useful to show additional info about your Provider to a {project_name} administrator. You can show provider build time information (eg. version of
2016-05-13 08:28:41 +00:00
custom provider currently installed), current configuration of the provider (eg. url of remote system your provider talks to) or some operational info
2018-02-07 14:41:56 +00:00
(average time of response from remote system your provider talks to). {project_name} admin console provides Server Info page to show this kind of information.
2016-05-13 08:28:41 +00:00
To show info from your provider it is enough to implement `org.keycloak.provider.ServerInfoAwareProviderFactory` interface in your `ProviderFactory`.
2018-02-07 14:41:56 +00:00
Example implementation for `MyThemeSelectorProviderFactory` from previous example:
2016-05-13 08:28:41 +00:00
[source,java]
----
package org.acme.provider;
import ...
2019-05-15 23:06:55 +00:00
public class MyThemeSelectorProviderFactory implements ThemeSelectorProviderFactory, ServerInfoAwareProviderFactory {
2016-05-13 08:28:41 +00:00
...
2016-04-18 21:02:18 +00:00
@Override
public Map<String, String> getOperationalInfo() {
Map<String, String> ret = new LinkedHashMap<>();
2018-02-07 14:41:56 +00:00
ret.put("theme-name", "my-theme");
2016-04-18 21:02:18 +00:00
return ret;
}
}
----
2016-05-13 08:28:41 +00:00
=== Registering provider implementations
2016-04-18 21:02:18 +00:00
2018-02-07 14:41:56 +00:00
There are two ways to register provider implementations. In most cases the simplest way is to use the {project_name} deployer
2017-04-21 09:02:47 +00:00
approach as this handles a number of dependencies automatically for you. It also supports hot deployment as well as re-deployment.
2016-12-03 14:56:58 +00:00
2017-04-21 09:02:47 +00:00
The alternative approach is to deploy as a module.
2018-02-07 14:41:56 +00:00
If you are creating a custom SPI you will need to deploy it as a module, otherwise we recommend using the {project_name} deployer approach.
2016-12-03 14:56:58 +00:00
2018-02-07 14:53:22 +00:00
==== Using the {project_name} Deployer
2016-12-03 14:56:58 +00:00
2018-02-07 14:53:22 +00:00
If you copy your provider jar to the {project_name} `standalone/deployments/` directory, your provider will automatically be deployed.
2018-01-24 13:47:45 +00:00
Hot deployment works too. Additionally, your provider jar works similarly to other components deployed in a {appserver_name}
2016-12-03 14:56:58 +00:00
environment in that they can use facilities like the `jboss-deployment-structure.xml` file. This file allows you to
set up dependencies on other components and load third-party jars and modules.
Provider jars can also be contained within other deployable units like EARs and WARs. Deploying with a EAR actually makes
it really easy to use third party jars as you can just put these libraries in the EAR's `lib/` directory.
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
==== Register a provider using Modules
2016-04-18 21:02:18 +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 `module.xml`.
For example to add the event listener sysout example provider using the `jboss-cli` script execute:
[source]
----
2018-02-07 14:41:56 +00:00
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.acme.provider --resources=target/provider.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
2016-04-18 21:02:18 +00:00
----
2018-02-07 14:41:56 +00:00
Or to manually create it start by creating the folder `KEYCLOAK_HOME/modules/org/acme/provider/main`.
Then copy `provider.jar` to this folder and create `module.xml` with the following content:
2016-04-18 21:02:18 +00:00
2018-02-08 21:09:26 +00:00
[source,xml]
2016-04-18 21:02:18 +00:00
----
<?xml version="1.0" encoding="UTF-8"?>
2018-02-07 14:41:56 +00:00
<module xmlns="urn:jboss:module:1.3" name="org.acme.provider">
2016-04-18 21:02:18 +00:00
<resources>
2018-02-07 14:41:56 +00:00
<resource-root path="provider.jar"/>
2016-04-18 21:02:18 +00:00
</resources>
<dependencies>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
</dependencies>
</module>
----
2018-02-07 14:41:56 +00:00
Once you've created the module you need to register this module with {project_name}.
2016-09-01 05:04:26 +00:00
This is done by editing the keycloak-server subsystem section of
`standalone.xml`, `standalone-ha.xml`, or `domain.xml`, and adding it to the providers:
2016-04-18 21:02:18 +00:00
2016-09-01 05:04:26 +00:00
[source,xml]
2016-04-18 21:02:18 +00:00
----
2016-09-01 05:04:26 +00:00
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<web-context>auth</web-context>
<providers>
<provider>module:org.keycloak.examples.event-sysout</provider>
</providers>
...
2016-04-18 21:02:18 +00:00
----
2016-05-13 08:28:41 +00:00
==== Disabling a provider
2016-04-18 21:02:18 +00:00
2016-09-01 05:04:26 +00:00
You can disable a provider by setting the enabled attribute for the provider to false
in `standalone.xml`, `standalone-ha.xml`, or `domain.xml`.
2016-04-18 21:02:18 +00:00
For example to disable the Infinispan user cache provider add:
2016-09-01 05:04:26 +00:00
[source,xml]
2016-04-18 21:02:18 +00:00
----
2016-09-01 05:04:26 +00:00
<spi name="userCache">
<provider name="infinispan" enabled="false"/>
</spi>
2016-12-03 14:56:58 +00:00
----
=== Leveraging Java EE
2018-02-02 13:54:47 +00:00
The service providers can be packaged within any Java EE component so long as you set up the `META-INF/services`
2016-12-03 14:56:58 +00:00
file correctly to point to your providers. For example, if your provider needs to use third party libraries, you
2019-04-18 15:37:06 +00:00
can package up your provider within an ear and store these third party libraries in the ear's `lib/` directory.
2016-12-03 14:56:58 +00:00
Also note that provider jars can make use of the `jboss-deployment-structure.xml` file that EJBs, WARS, and EARs
2018-01-24 13:47:45 +00:00
can use in a {appserver_name} environment. See the {appserver_name} documentation for more details on this file. It
2016-12-03 14:56:58 +00:00
allows you to pull in external dependencies among other fine grain actions.
`ProviderFactory` implementations are required to be plain java objects. But, we also currently support
2018-02-02 13:54:47 +00:00
implementing provider classes as Stateful EJBs. This is how you would do it:
2016-12-03 14:56:58 +00:00
[source,java]
----
@Stateful
@Local(EjbExampleUserStorageProvider.class)
public class EjbExampleUserStorageProvider implements UserStorageProvider,
UserLookupProvider,
UserRegistrationProvider,
UserQueryProvider,
CredentialInputUpdater,
CredentialInputValidator,
OnUserCache
{
@PersistenceContext
protected EntityManager em;
protected ComponentModel model;
protected KeycloakSession session;
public void setModel(ComponentModel model) {
this.model = model;
}
public void setSession(KeycloakSession session) {
this.session = session;
}
@Remove
@Override
public void close() {
}
...
}
----
You have to define the `@Local` annotation and specify your provider class there. If you don't do this, EJB will
not proxy the provider instance correctly and your provider won't work.
You must put the `@Remove` annotation on the `close()` method of your provider. If you don't, the stateful bean
will never be cleaned up and you may eventually see error messages.
Implementations of `ProviderFactory` are required to be plain java objects. Your factory class would
2019-04-18 15:37:06 +00:00
perform a JNDI lookup of the Stateful EJB in its `create()` method.
2016-12-03 14:56:58 +00:00
[source,java]
----
public class EjbExampleUserStorageProviderFactory
implements UserStorageProviderFactory<EjbExampleUserStorageProvider> {
@Override
public EjbExampleUserStorageProvider create(KeycloakSession session, ComponentModel model) {
try {
InitialContext ctx = new InitialContext();
EjbExampleUserStorageProvider provider = (EjbExampleUserStorageProvider)ctx.lookup(
"java:global/user-storage-jpa-example/" + EjbExampleUserStorageProvider.class.getSimpleName());
provider.setModel(model);
provider.setSession(session);
return provider;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
----
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
=== Available SPIs
2019-04-18 15:37:06 +00:00
If you want to see list of all available SPIs at runtime, you can check `Server Info` page in admin console as described in <<_providers_admin_console,Admin Console>> section.