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
2021-03-15 17:14:59 +00:00
[[_implementing_spi]]
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
}
----
2021-06-23 18:10:41 +00:00
NOTE: {project_name} 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
}
2019-07-10 22:01:30 +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
2022-10-05 18:43:15 +00:00
To configure your provider, see the link:https://www.keycloak.org/server/configuration-provider[Configuring Providers] guide.
2022-02-08 13:07:16 +00:00
2022-08-19 11:15:51 +00:00
For example, to configure a provider you can set options as follows:
2022-02-08 13:07:16 +00:00
[source,bash]
----
bin/kc.[sh|bat] --spi-theme-selector-my-theme-selector-enabled=true --spi-theme-selector-my-theme-selector-theme=my-theme
----
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
----
2022-10-19 06:43:27 +00:00
Your provider can also look up other providers if needed. For example:
2016-04-18 21:02:18 +00:00
2016-05-13 08:28:41 +00:00
[source,java]
----
2019-07-09 12:54:51 +00:00
public class MyThemeSelectorProvider implements ThemeSelectorProvider {
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]]
2021-06-23 18:10:41 +00:00
==== Show info from your SPI implementation in the Admin Console
2016-05-13 08:28:41 +00:00
2021-06-23 18:10:41 +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 (for example, version of
2022-10-19 06:43:27 +00:00
custom provider currently installed), current configuration of the provider (e.g. url of remote system your provider talks to) or some operational info
2021-06-23 18:10:41 +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;
}
}
2019-07-10 22:01:30 +00:00
----
2016-04-18 21:02:18 +00:00
2021-03-15 17:14:59 +00:00
[[_use_available_providers]]
=== Use available providers
In your provider implementation, you can use other providers available in {project_name}. The existing providers can be typically retrieved with the
usage of the `KeycloakSession`, which is available to your provider as described in the section <<_implementing_spi,Implementing an SPI>>.
{project_name} has two provider types:
* *Single-implementation provider types* - There can be only a single active implementation of the particular provider type in {project_name} runtime.
2022-03-23 14:34:07 +00:00
+
2021-03-15 17:14:59 +00:00
For example `HostnameProvider` specifies the hostname to be used by {project_name} and that is shared for the whole {project_name} server.
Hence there can be only single implementation of this provider active for the {project_name} server. If there are multiple provider implementations available to the server runtime,
2022-02-08 13:07:16 +00:00
one of them needs to be specified as the default one.
For example such as:
[source,bash]
----
bin/kc.[sh|bat] build --spi-hostname-provider=default
----
2021-03-15 17:14:59 +00:00
The value `default` used as the value of `default-provider` must match the ID returned by the `ProviderFactory.getId()` of the particular provider factory implementation.
In the code, you can obtain the provider such as `keycloakSession.getProvider(HostnameProvider.class)`
* *Multiple implementation provider types* - Those are provider types, that allow multiple implementations available and working together
2022-03-23 14:34:07 +00:00
in the {project_name} runtime.
+
For example `EventListener` provider allows to have multiple implementations available and registered, which means
2021-03-15 17:14:59 +00:00
that particular event can be sent to all the listeners (jboss-logging, sysout etc). In the code, you can obtain a specified instance of the provider
for example such as `session.getProvider(EventListener.class, "jboss-logging")` . You need to specify `provider_id` of the provider as the second argument
2022-03-23 14:34:07 +00:00
as there can be multiple instances of this provider type as described above.
+
The provider ID must match the ID returned by the `ProviderFactory.getId()` of the
2021-03-15 17:14:59 +00:00
particular provider factory implementation. Some provider types can be retrieved with the usage of `ComponentModel` as the second argument and some (for example `Authenticator`) even
need to be retrieved with the usage of `KeycloakSessionFactory`. It is not recommended to implement your own providers this way as it may be deprecated in the future.
2022-02-08 13:07:16 +00:00
=== Registering provider implementations
2021-03-15 17:14:59 +00:00
2022-02-08 13:07:16 +00:00
Providers are registered with the server by simply copying them to the `providers` directory.
If your provider needs additional dependencies not already provided by Keycloak copy these to the `providers` directory.
After registering new providers or dependencies Keycloak needs to be re-built with the `kc.[sh|bat] build` command.
==== Disabling a provider
You can disable a provider by setting the enabled attribute for the provider to false.
For example to disable the Infinispan user cache provider use:
[source,bash]
----
bin/kc.[sh|bat] build --spi-user-cache-infinispan-enabled=false
----
2016-04-18 21:02:18 +00:00
2019-11-01 12:09:56 +00:00
[[_script_providers]]
2021-06-23 18:10:41 +00:00
=== JavaScript providers
2019-11-01 12:09:56 +00:00
{project_name} has the ability to execute scripts during runtime in order to allow administrators to customize specific functionalities:
2019-12-20 10:05:07 +00:00
* Authenticator
2019-11-01 12:09:56 +00:00
* JavaScript Policy
2019-12-20 10:05:07 +00:00
* OpenID Connect Protocol Mapper
2022-09-09 11:47:47 +00:00
* SAML Protocol Mapper
2019-12-20 10:05:07 +00:00
==== Authenticator
Authentication scripts must provide at least one of the following functions:
`authenticate(..)`, which is called from `Authenticator#authenticate(AuthenticationFlowContext)`
`action(..)`, which is called from `Authenticator#action(AuthenticationFlowContext)`
Custom `Authenticator` should at least provide the `authenticate(..)` function.
You can use the `javax.script.Bindings` script within the code.
`script`::
the `ScriptModel` to access script metadata
`realm`::
the `RealmModel`
`user`::
the current `UserModel`
`session`::
the active `KeycloakSession`
`authenticationSession`::
the current `AuthenticationSessionModel`
`httpRequest`::
the current `org.jboss.resteasy.spi.HttpRequest`
`LOG`::
a `org.jboss.logging.Logger` scoped to `ScriptBasedAuthenticator`
NOTE: You can extract additional context information from the `context` argument passed to the `authenticate(context)` `action(context)` function.
[source,javascript]
----
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
LOG.info(script.name + " --> trace auth for: " + user.username);
if ( user.username === "tester"
&& user.getAttribute("someAttribute")
&& user.getAttribute("someAttribute").contains("someValue")) {
context.failure(AuthenticationFlowError.INVALID_USER);
return;
}
2019-11-01 12:09:56 +00:00
2019-12-20 10:05:07 +00:00
context.success();
}
----
==== Create a JAR with the scripts to deploy
2019-11-01 12:09:56 +00:00
NOTE: JAR files are regular ZIP files with a `.jar` extension.
In order to make your scripts available to {project_name} you need to deploy them to the server. For that, you should create
a `JAR` file with the following structure:
[source]
----
META-INF/keycloak-scripts.json
my-script-authenticator.js
my-script-policy.js
my-script-mapper.js
----
The `META-INF/keycloak-scripts.json` is a file descriptor that provides metadata information about the scripts you want to deploy. It is a JSON file with the following structure:
```json
{
"authenticators": [
{
"name": "My Authenticator",
2020-01-28 05:05:05 +00:00
"fileName": "my-script-authenticator.js",
2019-11-01 12:09:56 +00:00
"description": "My Authenticator from a JS file"
}
],
"policies": [
{
"name": "My Policy",
2020-01-28 05:05:05 +00:00
"fileName": "my-script-policy.js",
2019-11-01 12:09:56 +00:00
"description": "My Policy from a JS file"
}
],
"mappers": [
{
"name": "My Mapper",
2020-01-28 05:05:05 +00:00
"fileName": "my-script-mapper.js",
2019-11-01 12:09:56 +00:00
"description": "My Mapper from a JS file"
}
2022-09-09 11:47:47 +00:00
],
"saml-mappers": [
{
"name": "My Mapper",
"fileName": "my-script-mapper.js",
"description": "My Mapper from a JS file"
}
2019-11-01 12:09:56 +00:00
]
}
```
This file should reference the different types of script providers that you want to deploy:
* `authenticators`
+
For OpenID Connect Script Authenticators. You can have one or multiple authenticators in the same JAR file
+
* `policies`
+
For JavaScript Policies when using {project_name} Authorization Services. You can have one or multiple policies in the same JAR file
+
* `mappers`
+
For OpenID Connect Script Protocol Mappers. You can have one or multiple mappers in the same JAR file
2022-09-09 11:47:47 +00:00
+
* `saml-mappers`
+
For SAML Script Protocol Mappers. You can have one or multiple mappers in the same JAR file
2019-11-01 12:09:56 +00:00
2021-06-23 18:10:41 +00:00
For each script file in your `JAR` file, you need a corresponding entry in `META-INF/keycloak-scripts.json` that maps your scripts files to a specific provider type. For that you should provide the following properties for each entry:
2019-11-01 12:09:56 +00:00
* `name`
+
A friendly name that will be used to show the scripts through the {project_name} Administration Console. If not provided, the name
of the script file will be used instead
+
* `description`
+
An optional text that better describes the intend of the script file
+
* `fileName`
+
The name of the script file. This property is *mandatory* and should map to a file within the JAR.
2021-06-23 18:10:41 +00:00
==== Deploy the script JAR
2019-11-01 12:09:56 +00:00
2022-02-08 13:07:16 +00:00
Once you have a JAR file with a descriptor and the scripts you want to deploy, you just need to copy the JAR to the {project_name} `providers/` directory, then run `bin/kc.[sh|bat] build`.
2019-11-01 12:09:56 +00:00
2016-05-13 08:28:41 +00:00
=== Available SPIs
2021-06-23 18:10:41 +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.