Updates (#2)
* More updates * Updated * Update * Updated * Transform image::
|
@ -4,7 +4,7 @@ Keycloak Server Developer Guide
|
||||||
|
|
||||||
image:images/keycloak_logo.png[alt="Keycloak"]
|
image:images/keycloak_logo.png[alt="Keycloak"]
|
||||||
|
|
||||||
*Keycloak* _Documentation_ for {{book.versions.swarm}}
|
*Keycloak* _Documentation_ for {{book.project.version}}
|
||||||
|
|
||||||
http://www.keycloak.org
|
http://www.keycloak.org
|
||||||
|
|
||||||
|
|
18
SUMMARY.adoc
|
@ -1,15 +1,13 @@
|
||||||
== Keycloak Server Developer Guide
|
= {{book.title}}
|
||||||
|
|
||||||
//. link:topics/templates/document-attributes.adoc[]
|
|
||||||
:imagesdir: images
|
|
||||||
|
|
||||||
. link:topics/preface.adoc[Preface]
|
. link:topics/preface.adoc[Preface]
|
||||||
. link:topics/admin-rest-api.adoc[Admin REST API]
|
. link:topics/admin-rest-api.adoc[Admin REST API]
|
||||||
. link:topics/providers.adoc[Providers and SPIs]
|
|
||||||
. link:topics/themes.adoc[Themes]
|
. link:topics/themes.adoc[Themes]
|
||||||
. link:topics/events.adoc[Creating Event Listeners]
|
|
||||||
. link:topics/user-federation.adoc[User Federation SPI and LDAP/AD Integration]
|
|
||||||
. link:topics/custom-attributes.adoc[Custom User Attributes]
|
. link:topics/custom-attributes.adoc[Custom User Attributes]
|
||||||
. link:topics/auth-spi.adoc[Custom Authentication, Registration, and Required Actions]
|
{% if book.community %}
|
||||||
|
. link:topics/providers.adoc[Service Provider Interfaces (SPI)]
|
||||||
|
. link:topics/auth-spi.adoc[Authentication SPI]
|
||||||
|
. link:topics/events.adoc[Event Listener SPI]
|
||||||
|
. link:topics/user-federation.adoc[User Federation SPI]
|
||||||
|
. link:topics/user-federation-mapper.adoc[User Federation Mapper SPI]
|
||||||
|
{% endif %}
|
15
book.json
|
@ -21,6 +21,21 @@
|
||||||
"name": "Keycloak Adminstration Guide",
|
"name": "Keycloak Adminstration Guide",
|
||||||
"link": "https://keycloak.gitbooks.io/server-adminstration-guide/content/"
|
"link": "https://keycloak.gitbooks.io/server-adminstration-guide/content/"
|
||||||
},
|
},
|
||||||
|
"clientguide": {
|
||||||
|
"name": "Securing Client Applications Guide",
|
||||||
|
"link": "https://keycloak.gitbooks.io/securing-client-applications-guide/content/"
|
||||||
|
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"docs": {
|
||||||
|
"name": "keycloak.org/docs",
|
||||||
|
"link": "http://keycloak.org/docs"
|
||||||
|
},
|
||||||
|
"downloads": {
|
||||||
|
"name": "keycloak.org/downloads",
|
||||||
|
"link": "http://keycloak.org/downloads"
|
||||||
|
}
|
||||||
|
},
|
||||||
"project": {
|
"project": {
|
||||||
"name": "Keycloak",
|
"name": "Keycloak",
|
||||||
"version": "1.9.3.Final-SNAPSHOT"
|
"version": "1.9.3.Final-SNAPSHOT"
|
||||||
|
|
|
@ -23,6 +23,7 @@ def applyTransformation(input):
|
||||||
exp = re.compile("[ ]*{% if (.*?) %}(.*?)[ ]*{% endif %}", re.DOTALL)
|
exp = re.compile("[ ]*{% if (.*?) %}(.*?)[ ]*{% endif %}", re.DOTALL)
|
||||||
input = re.sub(exp, "ifeval::[{\g<1>}==true]\g<2>endif::[]", input)
|
input = re.sub(exp, "ifeval::[{\g<1>}==true]\g<2>endif::[]", input)
|
||||||
input = re.sub(r"image:(\.\./)*", "image:", input)
|
input = re.sub(r"image:(\.\./)*", "image:", input)
|
||||||
|
input = re.sub(r"image::(\.\./)*", "image::", input)
|
||||||
return input
|
return input
|
||||||
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 80 KiB |
|
@ -1,52 +1,53 @@
|
||||||
== Admin REST API
|
== Admin REST API
|
||||||
|
|
||||||
Keycloak comes with a fully functional Admin REST API which provides all features provided by the Admin Console.
|
Keycloak comes with a fully functional Admin REST API with all features provided by the Admin Console.
|
||||||
|
|
||||||
To invoke the API you need to obtain an access token with the appropriate permissions. The required permissions are described in <<_admin_permissions>> and
|
To invoke the API you need to obtain an access token with the appropriate permissions. The required permissions are described in
|
||||||
<<_per_realm_admin_permissions>>. A token can be obtained by enabling authenticating to your application with Keycloak, see the <<_client_developer_guide_>> for
|
{{book.adminguide.link}}[{{book.adminguide.name}}].
|
||||||
more details. Alternatively, you can use direct access grant to obtain an access token, see <<_direct_access_grant_>> for more details.
|
|
||||||
|
|
||||||
Full API documentation is available at http://keycloak.org/docs and can also be downloaded from http://keycloak.org/downloads.
|
A token can be obtained by enabling authenticating to your application with Keycloak, see the
|
||||||
|
{{book.clientguide.link}}[{{book.clientguide.name}}]. You can also use direct access grant to obtain an access token.
|
||||||
|
|
||||||
|
Full API documentation is available at {{book.web.docs.link}}[{{book.web.docs.name}}] and can be downloaded from
|
||||||
|
{{book.web.downloads.link}}[{{book.web.downloads.name}}].
|
||||||
|
|
||||||
=== Example using CURL
|
=== Example using CURL
|
||||||
|
|
||||||
Obtain access token for user with username _admin_ and password _password_:
|
Obtain access token for user in the realm `master` with username `admin` and password `password`:
|
||||||
....
|
[source,bash]
|
||||||
DATA="client_id=admin-cli"
|
----
|
||||||
DATA+="&username=admin"
|
curl \
|
||||||
DATA+="&password=password"
|
-d "client_id=admin-cli" \
|
||||||
DATA+="&grant_type=password"
|
-d "username=admin" \
|
||||||
|
-d "password=admin" \
|
||||||
|
-d "grant_type=password" \
|
||||||
|
"http://localhost:8080/auth/realms/master/protocol/openid-connect/token"
|
||||||
|
----
|
||||||
|
|
||||||
URL="http://localhost:8080/auth/realms/master/protocol/openid-connect/token"
|
NOTE: By default this token expires in 1 minute
|
||||||
|
|
||||||
OUTPUT=`curl -s --data $DATA $URL`
|
The result will be a JSON document. To invoke the API you need to extract the value of the `access_token` property. You can then invoke the API by including
|
||||||
|
the value in the `Authorization` header of requests to the API.
|
||||||
|
|
||||||
ACCESS_TOKEN=`echo $OUTPUT | grep -oE '"access_token":"[^"]*"' | cut -d '"' -f 4`
|
The following example shows how to get the details of the master realm:
|
||||||
....
|
|
||||||
|
|
||||||
NOTE: By default this token expires within 1 minutes.
|
[source,bash]
|
||||||
|
----
|
||||||
You can then invoke the API by including the token in the authorization header. The following example shows how to get the details of the master realm:
|
curl \
|
||||||
|
-H "Authorization: bearer eyJhbGciOiJSUz..." \
|
||||||
....
|
"http://localhost:8080/auth/admin/realms/master"
|
||||||
URL="http://localhost:8080/auth/admin/realms/master"
|
----
|
||||||
|
|
||||||
curl -v --header "Authorization: bearer $ACCESS_TOKEN" $URL
|
|
||||||
....
|
|
||||||
|
|
||||||
There are a number of examples that come with the keycloak distribution that show you how to invoke on this REST API. `examples/preconfigured-demo/admin-access-app` shows you how to access this api from java. `examples/cors/angular-product-app` shows you how to invoke on it from Javascript.
|
|
||||||
|
|
||||||
Finally there is example in `example/admin-client`, which contains example for Admin client, that can be used to invoke REST endpoints easily as Java methods.
|
|
||||||
|
|
||||||
{% if book.community %}
|
{% if book.community %}
|
||||||
=== Example using Java
|
=== Example using Java
|
||||||
|
|
||||||
There's a Java client library for the Admin REST API that makes it easy to use from Java. To use it from your application add a dependency on the
|
There's a Java client library for the Admin REST API that makes it easy to use from Java. To use it from your application add a dependency on the
|
||||||
_keycloak-admin-client_ library.
|
`keycloak-admin-client` library.
|
||||||
|
|
||||||
The following example shows how to use the Java client library to get the details of the master realm:
|
The following example shows how to use the Java client library to get the details of the master realm:
|
||||||
|
|
||||||
....
|
[source,java]
|
||||||
|
----
|
||||||
import org.keycloak.admin.client.Keycloak;
|
import org.keycloak.admin.client.Keycloak;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
...
|
...
|
||||||
|
@ -56,8 +57,8 @@ Keycloak keycloak = Keycloak.getInstance(
|
||||||
"admin", "password",
|
"admin", "password",
|
||||||
"admin-cli");
|
"admin-cli");
|
||||||
RealmRepresentation realm = keycloak.realm("master").toRepresentation();
|
RealmRepresentation realm = keycloak.realm("master").toRepresentation();
|
||||||
....
|
----
|
||||||
|
|
||||||
Complete JavaDocs for the admin client is available at http://keycloak.org/docs and can also be downloaded from http://keycloak.org/downloads. From the downloads
|
Complete Javadoc for the admin client is available at {{book.web.docs.link}}[{{book.web.docs.name}}]. From {{book.web.downloads.link}}[{{book.web.downloads.name}}]
|
||||||
you can also get the examples distribution that includes `example/admin-client` showing how to use the Java client.
|
you can get the examples distribution with the `admin-client` example that shows how to use the Java admin client.
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -1,5 +1,5 @@
|
||||||
[[_auth_spi]]
|
[[_auth_spi]]
|
||||||
= Custom Authentication, Registration, and Required Actions
|
== Authentication SPI
|
||||||
|
|
||||||
Keycloak comes out of the box with a bunch of different authentication mechanisms: kerberos, password, and otp.
|
Keycloak comes out of the box with a bunch of different authentication mechanisms: kerberos, password, and otp.
|
||||||
These mechanisms may not meet all of your requirements and you may want to plug in your own custom ones.
|
These mechanisms may not meet all of your requirements and you may want to plug in your own custom ones.
|
||||||
|
@ -17,7 +17,7 @@ After the action is performed successfully, the user doesn't have to perform the
|
||||||
Keycloak comes with some built in required actions like "reset password". This action forces the user to change their password after they have logged in.
|
Keycloak comes with some built in required actions like "reset password". This action forces the user to change their password after they have logged in.
|
||||||
You can write and plug in your own required actions.
|
You can write and plug in your own required actions.
|
||||||
|
|
||||||
== Terms
|
=== Terms
|
||||||
|
|
||||||
To first learn about the Authentication SPI, let's go over some of the terms used to describe it.
|
To first learn about the Authentication SPI, let's go over some of the terms used to describe it.
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ Required Action::
|
||||||
After authentication completes, the user might have one or more one-time actions he must complete before he is allowed to login.
|
After authentication completes, the user might have one or more one-time actions he must complete before he is allowed to login.
|
||||||
The user might be required to set up an OTP token generator or reset an expired password or even accept a Terms and Conditions document.
|
The user might be required to set up an OTP token generator or reset an expired password or even accept a Terms and Conditions document.
|
||||||
|
|
||||||
== Algorithm Overview
|
=== Algorithm Overview
|
||||||
|
|
||||||
Let's talk about how this all works for browser login.
|
Let's talk about how this all works for browser login.
|
||||||
Let's assume the following flows, executions and sub flows.
|
Let's assume the following flows, executions and sub flows.
|
||||||
|
@ -127,7 +127,7 @@ Let's walk through the steps from when a client first redirects to keycloak to a
|
||||||
. After all required actions have been resolved, the user is finally logged in.
|
. After all required actions have been resolved, the user is finally logged in.
|
||||||
|
|
||||||
[[_auth_spi_walkthrough]]
|
[[_auth_spi_walkthrough]]
|
||||||
== Authenticator SPI Walk Through
|
=== Authenticator SPI Walk Through
|
||||||
|
|
||||||
In this section, we'll take a look at the Authenticator interface.
|
In this section, we'll take a look at the Authenticator interface.
|
||||||
For this, we are going to implement an authenticator that requires that a user enter in the answer to a secret question like "What is your mother's maiden name?". This example is fully implemented and contained in the examples/providers/authenticator directory of the demo distribution of Keycloak.
|
For this, we are going to implement an authenticator that requires that a user enter in the answer to a secret question like "What is your mother's maiden name?". This example is fully implemented and contained in the examples/providers/authenticator directory of the demo distribution of Keycloak.
|
||||||
|
@ -137,7 +137,7 @@ The Authenticator interface defines the logic.
|
||||||
The AuthenticatorFactory is responsible for creating instances of an Authenticator.
|
The AuthenticatorFactory is responsible for creating instances of an Authenticator.
|
||||||
They both extend a more generic Provider and ProviderFactory set of interfaces that other Keycloak components like User Federation do.
|
They both extend a more generic Provider and ProviderFactory set of interfaces that other Keycloak components like User Federation do.
|
||||||
|
|
||||||
=== Packaging Classes and Deployment
|
==== Packaging Classes and Deployment
|
||||||
|
|
||||||
You will package your classes within a single jar.
|
You will package your classes within a single jar.
|
||||||
This jar must contain a file named `org.keycloak.authentication.AuthenticatorFactory` and must be contained in the `META-INF/services/` directory of your jar.
|
This jar must contain a file named `org.keycloak.authentication.AuthenticatorFactory` and must be contained in the `META-INF/services/` directory of your jar.
|
||||||
|
@ -154,7 +154,7 @@ This services/ file is used by Keycloak to scan the providers it has to load int
|
||||||
|
|
||||||
To deploy this jar, just copy it to the standalone/configuration/providers directory.
|
To deploy this jar, just copy it to the standalone/configuration/providers directory.
|
||||||
|
|
||||||
=== Implementing an Authenticator
|
==== Implementing an Authenticator
|
||||||
|
|
||||||
When implementing the Authenticator interface, the first method that needs to be implemented is the requiresUser() method.
|
When implementing the Authenticator interface, the first method that needs to be implemented is the requiresUser() method.
|
||||||
For our example, this method must return true as we need to validate the secret question associated with the user.
|
For our example, this method must return true as we need to validate the secret question associated with the user.
|
||||||
|
@ -274,7 +274,7 @@ If configuration exists we pull the max age config out of it.
|
||||||
We will see how we can define what should be configured when we talk about the AuthenticatorFactory implementation.
|
We will see how we can define what should be configured when we talk about the AuthenticatorFactory implementation.
|
||||||
The config values can be defined within the admin console if you set up config definitions in your AuthenticatorFactory implementation.
|
The config values can be defined within the admin console if you set up config definitions in your AuthenticatorFactory implementation.
|
||||||
|
|
||||||
=== Implementing an AuthenticatorFactory
|
==== Implementing an AuthenticatorFactory
|
||||||
|
|
||||||
The next step in this process is to implement an AuthenticatorFactory.
|
The next step in this process is to implement an AuthenticatorFactory.
|
||||||
This factory is responsible for instantiating an Authenticator.
|
This factory is responsible for instantiating an Authenticator.
|
||||||
|
@ -371,7 +371,7 @@ getHelpText() is the tooltip text that will be shown when you are picking the Au
|
||||||
getDisplayType() is what text that will be shown in the admin console when listing the Authenticator.
|
getDisplayType() is what text that will be shown in the admin console when listing the Authenticator.
|
||||||
getReferenceCategory() is just a category the Authenticator belongs to.
|
getReferenceCategory() is just a category the Authenticator belongs to.
|
||||||
|
|
||||||
=== Adding Authenticator Form
|
==== Adding Authenticator Form
|
||||||
|
|
||||||
Keycloak comes with a Freemarker <<_themes,theme and template engine>>.
|
Keycloak comes with a Freemarker <<_themes,theme and template engine>>.
|
||||||
The createForm() method you called within authenticate() of your Authenticator class, builds an HTML page from a file within your login theme: secret-question.ftl.
|
The createForm() method you called within authenticate() of your Authenticator class, builds an HTML page from a file within your login theme: secret-question.ftl.
|
||||||
|
@ -410,7 +410,7 @@ If you called, `LoginFormsProvider.setAttribute("foo", "bar")`, the value of "fo
|
||||||
The value of an attribute can be any Java bean as well.
|
The value of an attribute can be any Java bean as well.
|
||||||
|
|
||||||
[[_adding_authenticator]]
|
[[_adding_authenticator]]
|
||||||
=== Adding Authenticator to a Flow
|
==== Adding Authenticator to a Flow
|
||||||
|
|
||||||
Adding an Authenticator to a flow must be done in the admin console.
|
Adding an Authenticator to a flow must be done in the admin console.
|
||||||
If you go to the Authentication menu item and go to the Flow tab, you will be able to view the currently defined flows.
|
If you go to the Authentication menu item and go to the Flow tab, you will be able to view the currently defined flows.
|
||||||
|
@ -420,13 +420,13 @@ I'm hoping the UI is intuitive enough so that you can figure out for yourself ho
|
||||||
After you've created your flow, you have to bind it to the login action you want to bind it to.
|
After you've created your flow, you have to bind it to the login action you want to bind it to.
|
||||||
If you go to the Authentication menu and go to the Bindings tab you will see options to bind a flow to the browser, registration, or direct grant flow.
|
If you go to the Authentication menu and go to the Bindings tab you will see options to bind a flow to the browser, registration, or direct grant flow.
|
||||||
|
|
||||||
== Required Action Walkthrough
|
=== Required Action Walkthrough
|
||||||
|
|
||||||
In this section we will discuss how to define a required action.
|
In this section we will discuss how to define a required action.
|
||||||
In the Authenticator section you may have wondered, "How will we get the user's answer to the secret question entered into the system?". As we showed in the example, if the answer is not set up, a required action will be triggered.
|
In the Authenticator section you may have wondered, "How will we get the user's answer to the secret question entered into the system?". As we showed in the example, if the answer is not set up, a required action will be triggered.
|
||||||
This section discusses how to implement the required action for the Secret Question Authenticator.
|
This section discusses how to implement the required action for the Secret Question Authenticator.
|
||||||
|
|
||||||
=== Packaging Classes and Deployment
|
==== Packaging Classes and Deployment
|
||||||
|
|
||||||
You will package your classes within a single jar.
|
You will package your classes within a single jar.
|
||||||
This jar does not have to be separate from other provider classes but it must contain a file named `org.keycloak.authentication.RequiredActionFactory` and must be contained in the `META-INF/services/` directory of your jar.
|
This jar does not have to be separate from other provider classes but it must contain a file named `org.keycloak.authentication.RequiredActionFactory` and must be contained in the `META-INF/services/` directory of your jar.
|
||||||
|
@ -442,7 +442,7 @@ This services/ file is used by Keycloak to scan the providers it has to load int
|
||||||
|
|
||||||
To deploy this jar, just copy it to the standalone/configuration/providers directory.
|
To deploy this jar, just copy it to the standalone/configuration/providers directory.
|
||||||
|
|
||||||
=== Implement the RequiredActionProvider
|
==== Implement the RequiredActionProvider
|
||||||
|
|
||||||
Required actions must first implement the RequiredActionProvider interface.
|
Required actions must first implement the RequiredActionProvider interface.
|
||||||
The RequiredActionProvider.requiredActionChallenge() is the initial call by the flow manager into the required action.
|
The RequiredActionProvider.requiredActionChallenge() is the initial call by the flow manager into the required action.
|
||||||
|
@ -489,7 +489,7 @@ A UserCredentialValueModel is created and the type and value of the credential a
|
||||||
Then UserModel.updateCredentialDirectly() is invoked.
|
Then UserModel.updateCredentialDirectly() is invoked.
|
||||||
Finally, RequiredActionContext.success() notifies the container that the required action was successful.
|
Finally, RequiredActionContext.success() notifies the container that the required action was successful.
|
||||||
|
|
||||||
=== Implement the RequiredActionFactory
|
==== Implement the RequiredActionFactory
|
||||||
|
|
||||||
This class is really simple.
|
This class is really simple.
|
||||||
It is just responsible for creating the required actin provider instance.
|
It is just responsible for creating the required actin provider instance.
|
||||||
|
@ -520,7 +520,7 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor
|
||||||
|
|
||||||
The getDisplayText() method is just for the admin console when it wants to display a friendly name for the required action.
|
The getDisplayText() method is just for the admin console when it wants to display a friendly name for the required action.
|
||||||
|
|
||||||
=== Enable Required Action
|
==== Enable Required Action
|
||||||
|
|
||||||
The final thing you have to do is go into the admin console.
|
The final thing you have to do is go into the admin console.
|
||||||
Click on the Authentication left menu.
|
Click on the Authentication left menu.
|
||||||
|
@ -528,7 +528,7 @@ Click on the Required Actions tab.
|
||||||
Click on the Register button and choose your new Required Action.
|
Click on the Register button and choose your new Required Action.
|
||||||
Your new required action should now be displayed and enabled in the required actions list.
|
Your new required action should now be displayed and enabled in the required actions list.
|
||||||
|
|
||||||
== Modifying/Extending the Registration Form
|
=== Modifying/Extending the Registration Form
|
||||||
|
|
||||||
It is entirely possible for you to implement your own flow with a set of Authenticators to totally change how regisration is done in Keycloak.
|
It is entirely possible for you to implement your own flow with a set of Authenticators to totally change how regisration is done in Keycloak.
|
||||||
But what you'll usually want to do is just add a little bit of validation to the out of the box registration page.
|
But what you'll usually want to do is just add a little bit of validation to the out of the box registration page.
|
||||||
|
@ -536,7 +536,7 @@ An additional SPI was created to be able to do this.
|
||||||
It basically allows you to add validation of form elements on the page as well as to initialize UserModel attributes and data after the user has been registered.
|
It basically allows you to add validation of form elements on the page as well as to initialize UserModel attributes and data after the user has been registered.
|
||||||
We'll look at both the implementation of the user profile registration processing as well as the registration Google Recaptcha plugin.
|
We'll look at both the implementation of the user profile registration processing as well as the registration Google Recaptcha plugin.
|
||||||
|
|
||||||
=== Implementation FormAction Interface
|
==== Implementation FormAction Interface
|
||||||
|
|
||||||
The core interface you have to implement is the FormAction interface.
|
The core interface you have to implement is the FormAction interface.
|
||||||
A FormAction is responsible for rendering and processing a portion of the page.
|
A FormAction is responsible for rendering and processing a portion of the page.
|
||||||
|
@ -697,7 +697,7 @@ The appropriate methods are called to initialize UserModel data.
|
||||||
Finally, you are also required to define a FormActionFactory class.
|
Finally, you are also required to define a FormActionFactory class.
|
||||||
This class is implemented similarly to AuthenticatorFactory, so we won't go over it.
|
This class is implemented similarly to AuthenticatorFactory, so we won't go over it.
|
||||||
|
|
||||||
=== Packaging the Action
|
==== Packaging the Action
|
||||||
|
|
||||||
You will package your classes within a single jar.
|
You will package your classes within a single jar.
|
||||||
This jar must contain a file named `org.keycloak.authentication.FormActionFactory` and must be contained in the `META-INF/services/` directory of your jar.
|
This jar must contain a file named `org.keycloak.authentication.FormActionFactory` and must be contained in the `META-INF/services/` directory of your jar.
|
||||||
|
@ -715,7 +715,7 @@ This services/ file is used by Keycloak to scan the providers it has to load int
|
||||||
|
|
||||||
To deploy this jar, just copy it to the standalone/configuration/providers directory.
|
To deploy this jar, just copy it to the standalone/configuration/providers directory.
|
||||||
|
|
||||||
=== Adding FormAction to the Registration Flow
|
==== Adding FormAction to the Registration Flow
|
||||||
|
|
||||||
Adding an FormAction to a registration page flow must be done in the admin console.
|
Adding an FormAction to a registration page flow must be done in the admin console.
|
||||||
If you go to the Authentication menu item and go to the Flow tab, you will be able to view the currently defined flows.
|
If you go to the Authentication menu item and go to the Flow tab, you will be able to view the currently defined flows.
|
||||||
|
@ -730,7 +730,7 @@ Make sure your FormAction comes after "Registration User Creation" by using the
|
||||||
After you've created your flow, you have to bind it to registration.
|
After you've created your flow, you have to bind it to registration.
|
||||||
If you go to the Authentication menu and go to the Bindings tab you will see options to bind a flow to the browser, registration, or direct grant flow.
|
If you go to the Authentication menu and go to the Bindings tab you will see options to bind a flow to the browser, registration, or direct grant flow.
|
||||||
|
|
||||||
== Modifying Forgot Password/Credential Flow
|
=== Modifying Forgot Password/Credential Flow
|
||||||
|
|
||||||
Keycloak also has a specific authentication flow for forgot password, or rather credential reset initiated by a user.
|
Keycloak also has a specific authentication flow for forgot password, or rather credential reset initiated by a user.
|
||||||
If you go to the admin console flows page, there is a "reset credentials" flow.
|
If you go to the admin console flows page, there is a "reset credentials" flow.
|
||||||
|
@ -751,19 +751,19 @@ So, if AuthenticationFlowContext.getUser() returns null, you should proceed with
|
||||||
I suggest that if you want to add secret questions to this flow, you should ask these questions after the email is sent.
|
I suggest that if you want to add secret questions to this flow, you should ask these questions after the email is sent.
|
||||||
In other words, add your custom authenticator after the "Send Reset Email" authenticator.
|
In other words, add your custom authenticator after the "Send Reset Email" authenticator.
|
||||||
|
|
||||||
== Modifying First Broker Login Flow
|
=== Modifying First Broker Login Flow
|
||||||
|
|
||||||
First Broker Login flow is used during first login with some identity provider.
|
First Broker Login flow is used during first login with some identity provider.
|
||||||
Term `First Login` means that there is not yet existing Keycloak account linked with the particular authenticated identity provider account.
|
Term `First Login` means that there is not yet existing Keycloak account linked with the particular authenticated identity provider account.
|
||||||
More details about this flow are in the <<_identity_broker_first_login,Identity provider chapter>>.
|
More details about this flow are in the <<_identity_broker_first_login,Identity provider chapter>>.
|
||||||
|
|
||||||
[[_client_authentication]]
|
[[_client_authentication]]
|
||||||
== Authentication of clients
|
=== Authentication of clients
|
||||||
|
|
||||||
Keycloak actually supports pluggable authentication for http://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect] client applications.
|
Keycloak actually supports pluggable authentication for http://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect] client applications.
|
||||||
Authentication of client (application) is used under the hood by the <<_adapter_config,Keycloak adapter>> during sending any backchannel requests to the Keycloak server (like the request for exchange code to access token after successful authentication or request to refresh token). But the client authentication can be also used directly by you during <<_direct_access_grants,Direct Access grants>> or during <<_service_accounts,Service account>> authentication.
|
Authentication of client (application) is used under the hood by the <<_adapter_config,Keycloak adapter>> during sending any backchannel requests to the Keycloak server (like the request for exchange code to access token after successful authentication or request to refresh token). But the client authentication can be also used directly by you during <<_direct_access_grants,Direct Access grants>> or during <<_service_accounts,Service account>> authentication.
|
||||||
|
|
||||||
=== Default implementations
|
==== Default implementations
|
||||||
|
|
||||||
Actually Keycloak has 2 builtin implementations of client authentication:
|
Actually Keycloak has 2 builtin implementations of client authentication:
|
||||||
|
|
||||||
|
@ -778,7 +778,7 @@ Authentication with signed JWT::
|
||||||
|
|
||||||
See the demo example and especially the `examples/preconfigured-demo/service-account` for the example application showing service accounts authentication with both clientId+clientSecret and with signed JWT.
|
See the demo example and especially the `examples/preconfigured-demo/service-account` for the example application showing service accounts authentication with both clientId+clientSecret and with signed JWT.
|
||||||
|
|
||||||
=== Implement your own client authenticator
|
==== Implement your own client authenticator
|
||||||
|
|
||||||
For plug your own client authenticator, you need to implement few interfaces on both client (adapter) and server side.
|
For plug your own client authenticator, you need to implement few interfaces on both client (adapter) and server side.
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
= Custom User Attributes
|
== Custom User Attributes
|
||||||
|
|
||||||
If you have custom user data you want to store and manage in the admin console, registration page, and user account service, you can easily add support for it by extending and modifying various Keycloak <<_themes,themes>>.
|
If you have custom user data you want to store and manage in the admin console, registration page, and user account service, you can easily add support for it by extending and modifying various Keycloak <<_themes,themes>>.
|
||||||
|
|
||||||
== In admin console
|
=== In admin console
|
||||||
|
|
||||||
To be able to enter custom attributes in the admin console, take the following steps
|
To be able to enter custom attributes in the admin console, take the following steps
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ import=common/keycloak
|
||||||
. Change the theme for the admin console. Save it, then refresh your browser, and you should
|
. Change the theme for the admin console. Save it, then refresh your browser, and you should
|
||||||
now see these fields in the User detail page for any user.
|
now see these fields in the User detail page for any user.
|
||||||
|
|
||||||
== In registration page
|
=== In registration page
|
||||||
|
|
||||||
To be able to enter custom attributes in the registration page, take the following steps
|
To be able to enter custom attributes in the registration page, take the following steps
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ styles= ../patternfly/lib/patternfly/css/patternfly.css ../patternfly/css/login.
|
||||||
. Change the theme for the login to your new theme. Save it, then refresh your browser, and you should
|
. Change the theme for the login to your new theme. Save it, then refresh your browser, and you should
|
||||||
now see these fields in the registration.
|
now see these fields in the registration.
|
||||||
|
|
||||||
== In user account profile page
|
=== In user account profile page
|
||||||
|
|
||||||
To be able to manage custom attributes in the user account profile page, take the following steps
|
To be able to manage custom attributes in the user account profile page, take the following steps
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
= Creating Event LIsteners
|
[[_events]]
|
||||||
|
== Event Listener SPI
|
||||||
|
|
||||||
|
Writing a Event Listener Provider starts by implementing the `EventListenerProvider` and `EventListenerProviderFactory` interfaces. Please see the Javadoc
|
||||||
|
and examples for complete details on how to do this.
|
||||||
|
|
||||||
|
For details on how to package and deploy a custom provider refer to the <<providers.adoc#providers,Service Provider Interfaces>> chapter.
|
||||||
|
|
||||||
Keycloak provides an Events SPI that makes it possible to register listeners for user related events, for example user logins.
|
|
||||||
There are two interfaces that can be implemented, the first is a pure listener, the second is a events store which listens for events, but is also required to store events.
|
|
||||||
An events store provides a way for the admin and account management consoles to view events.
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
= Preface
|
== Preface
|
||||||
|
|
||||||
In some of the example listings, what is meant to be displayed on one line does not fit inside the available page width.These lines have been broken up. A '\' at the end of a line means that a break has been introduced to fit in the page, with the following lines indented.
|
In some of the example listings, what is meant to be displayed on one line does not fit inside the available page width.These lines have been broken up. A '\' at the end of a line means that a break has been introduced to fit in the page, with the following lines indented.
|
||||||
So:
|
So:
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
[[_providers]]
|
[[_providers]]
|
||||||
|
|
||||||
= Providers and SPIs
|
== Service Provider Interfaces (SPI)
|
||||||
|
|
||||||
Keycloak is designed to cover most use-cases without requiring custom code, but we also want it to be customizable.
|
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.
|
To achive this Keycloak has a number of Service Provider Interfaces (SPI) which you can implement your own providers for.
|
||||||
|
|
||||||
== Implementing a SPI
|
=== Implementing a SPI
|
||||||
|
|
||||||
To implement an SPI you need to implement it's ProviderFactory and Provider interfaces.
|
To implement an SPI you need to implement it's ProviderFactory and Provider interfaces. You also need to create a service configuration file.
|
||||||
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 `META-INF/services/org.keycloak.events.EventListenerProviderFactory`
|
|
||||||
|
|
||||||
For example to implement the Event Listener SPI you start by implementing EventListenerProviderFactory:
|
For example to implement the Event Listener SPI you need to implement EventListenerProviderFactory and EventListenerProvider and also provide the file
|
||||||
|
`META-INF/services/org.keycloak.events.EventListenerProviderFactory`.
|
||||||
|
|
||||||
[source]
|
Example EventListenerProviderFactory:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
----
|
----
|
||||||
package org.acme.provider;
|
package org.acme.provider;
|
||||||
|
|
||||||
|
@ -28,8 +29,7 @@ public class MyEventListenerProviderFactory implements EventListenerProviderFact
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
int max = config.getInt("max");
|
events = new LinkedList();
|
||||||
events = new MaxList(max);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventListenerProvider create(KeycloakSession session) {
|
public EventListenerProvider create(KeycloakSession session) {
|
||||||
|
@ -37,20 +37,16 @@ public class MyEventListenerProviderFactory implements EventListenerProviderFact
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
events = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
The example uses an imagined MaxList which has a maximum size and is concurrency safe.
|
NOTE: Keycloak creates a single instance of `EventListenerProviderFactory` which makes it possible to store state for multiple requests.
|
||||||
When the maximum size is reached and new entries are added the oldest entry is removed.
|
`EventListenerProvider` instances are created by calling create on the factory for each requests so these should be light-weight object.
|
||||||
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.
|
|
||||||
|
|
||||||
Next you would implement `EventListenerProvider`:
|
Example EventListenerProvider:
|
||||||
|
|
||||||
[source]
|
[source,java]
|
||||||
----
|
----
|
||||||
package org.acme.provider;
|
package org.acme.provider;
|
||||||
|
|
||||||
|
@ -73,45 +69,82 @@ public class MyEventListenerProvider implements EventListenerProvider {
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
The file `META-INF/services/org.keycloak.events.EventListenerProviderFactory` should contain the full name of your ProviderFactory implementation:
|
Example service configuration file (`META-INF/services/org.keycloak.events.EventListenerProviderFactory`):
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
org.acme.provider.MyEventListenerProviderFactory
|
org.acme.provider.MyEventListenerProviderFactory
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Show info from you SPI implementation in Keycloak admin console
|
You can configure your provider through `keycloak-server.json`. For example by adding the following to `keycloak-server.json`:
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
To show info from your provider it is enough to implement `org.keycloak.provider.ServerInfoAwareProviderFactory` interface in your `ProviderFactory`.
|
|
||||||
Example implementation for `MyEventListenerProviderFactory` from previous example:
|
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
|
"eventsListener": {
|
||||||
|
"my-event-listener" {
|
||||||
|
"aNumber": 10,
|
||||||
|
"aString": "Foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Then you can retrieve the config in the `ProviderFactory` init method:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
Integer aNumber = config.getInt("aNumber");
|
||||||
|
String aString = config.get("aString");
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Your provider can also lookup other providers if needed. For example:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
public class MyEventListenerProvider implements EventListenerProvider {
|
||||||
|
|
||||||
|
private KeycloakSession session;
|
||||||
|
private List<Event> events;
|
||||||
|
|
||||||
|
public MyEventListenerProvider(KeycloakSession session, List<Event> events) {
|
||||||
|
this.session = session;
|
||||||
|
this.events = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onEvent(Event event) {
|
||||||
|
RealmModel realm = session.realms().getRealm(event.getRealmId());
|
||||||
|
UserModel user = session.users().getUserById(event.getUserId(), realm);
|
||||||
|
|
||||||
|
EmailSenderProvider emailSender = session.getProvider(EmailSenderProvider.class);
|
||||||
|
emailSender.send(realm, user, "Hello", "Hello plain text", "<h1>Hello html</h1>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
==== Show info from you SPI implementation in Keycloak admin console
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
To show info from your provider it is enough to implement `org.keycloak.provider.ServerInfoAwareProviderFactory` interface in your `ProviderFactory`.
|
||||||
|
|
||||||
|
Example implementation for `MyEventListenerProviderFactory` from previous example:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
package org.acme.provider;
|
package org.acme.provider;
|
||||||
|
|
||||||
import ...
|
import ...
|
||||||
|
|
||||||
public class MyEventListenerProviderFactory implements EventListenerProviderFactory, ServerInfoAwareProviderFactory {
|
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
|
@Override
|
||||||
public Map<String, String> getOperationalInfo() {
|
public Map<String, String> getOperationalInfo() {
|
||||||
|
@ -121,17 +154,16 @@ public class MyEventListenerProviderFactory implements EventListenerProviderFact
|
||||||
ret.put("listSizeCurrent", events.size() + "");
|
ret.put("listSizeCurrent", events.size() + "");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
== Registering provider implementations
|
=== Registering provider implementations
|
||||||
|
|
||||||
Keycloak can load provider implementations from JBoss Modules or directly from the file-system.
|
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.
|
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.
|
Any providers loaded from the file-system uses a classloader with the Keycloak classloader as its parent.
|
||||||
|
|
||||||
=== Register a provider using Modules
|
==== Register a provider using Modules
|
||||||
|
|
||||||
To register a provider using Modules first create a module.
|
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`.
|
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`.
|
||||||
|
@ -172,7 +204,7 @@ This is done by editing keycloak-server.json and adding it to the providers:
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Register a provider using file-system
|
==== Register a provider using file-system
|
||||||
|
|
||||||
To register your provider simply copy the JAR including the ProviderFactory and Provider classes and the provider configuration file to server's root `providers` directory.
|
To register your provider simply copy the JAR including the ProviderFactory and Provider classes and the provider configuration file to server's root `providers` directory.
|
||||||
|
|
||||||
|
@ -203,7 +235,7 @@ Wildcard is also supported allowing loading all jars (files with .jar or .JAR ex
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Configuring a provider
|
==== Configuring a provider
|
||||||
|
|
||||||
You can pass configuration options to your provider by setting them in `keycloak-server.json`.
|
You can pass configuration options to your provider by setting them in `keycloak-server.json`.
|
||||||
For example to set the max value for `my-event-listener` add:
|
For example to set the max value for `my-event-listener` add:
|
||||||
|
@ -219,7 +251,7 @@ For example to set the max value for `my-event-listener` add:
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Disabling a provider
|
==== Disabling a provider
|
||||||
|
|
||||||
You can disable a provider by setting the enabled field for the provider to false in `keycloak-server.json`.
|
You can disable a provider by setting the enabled field for the provider to false in `keycloak-server.json`.
|
||||||
For example to disable the Infinispan user cache provider add:
|
For example to disable the Infinispan user cache provider add:
|
||||||
|
@ -235,47 +267,25 @@ For example to disable the Infinispan user cache provider add:
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
== Available SPIs
|
=== Available SPIs
|
||||||
|
|
||||||
Here's a list of the available SPIs and a brief description.
|
Here's a list of the available SPIs and a brief description. For more details on each SPI refer to individual sections.
|
||||||
For more details on each SPI refer to individual sections.
|
|
||||||
|
|
||||||
Account::
|
|===
|
||||||
|
|SPI|Description
|
||||||
|
|
||||||
Connections Infinispan::
|
|Connections Infinispan|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
|
||||||
|
|Connections Jpa|Loads and configures Jpa connections. The default implementation can load datasources from WildFly/EAP, or alternatively can be manually configured in keycloak-server.json
|
||||||
Connections Jpa::
|
|Connections Mongo|Loads and configures MongoDB connections. The default implementation is configured in keycloak-server.json
|
||||||
|
|Email Sender|Sends email. The default implementation uses JavaMail
|
||||||
Connections Jpa Updater::
|
|Email Template|Format email and uses Email Sender to send the email. The default implementation uses FreeMarker templates
|
||||||
|
|Events Listener|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
|
||||||
Connections Mongo::
|
|Login Protocol|Provides protocols. Keycloak provides implementations of OpenID Connect and SAML 2.0
|
||||||
|
|Realm|Provides realm and application meta-data. Keycloak provides implementations for Relational Databases and MongoDB
|
||||||
Email::
|
|Realm Cache|Caches realm and application meta-data to improve performance. Default implementation uses Infinispan
|
||||||
|
|Timer|Executes scheduled tasks. Keycloak provides a basic implementation based on java.util.Timer
|
||||||
Events Listener::
|
|User|Provides users and role-mappings. Keycloak provides implementations for Relational Databases and MongoDB
|
||||||
|
|User Cache|Caches users to improve performance. Default implementation uses Infinispan
|
||||||
Events Store::
|
|User Federation|Support syncing users from an external source. Keycloak provides implementations for LDAP and Active Directory
|
||||||
|
|User Sessions|Provides users session information. Keycloak provides implementations for basic in-memory, Infinispan, Relational Databases and MongoDB
|
||||||
Export::
|
|===
|
||||||
|
|
||||||
Import::
|
|
||||||
|
|
||||||
Login::
|
|
||||||
|
|
||||||
Login Protocol::
|
|
||||||
|
|
||||||
Realm::
|
|
||||||
|
|
||||||
Realm Cache::
|
|
||||||
|
|
||||||
Theme::
|
|
||||||
|
|
||||||
Timer::
|
|
||||||
|
|
||||||
User::
|
|
||||||
|
|
||||||
User Cache::
|
|
||||||
|
|
||||||
User Federation::
|
|
||||||
|
|
||||||
User Sessions::
|
|
|
@ -1,15 +1,14 @@
|
||||||
= Themes
|
[[_themes]]
|
||||||
|
== Themes
|
||||||
|
|
||||||
Keycloak provides theme support for web pages and emails. This allows customizing the look and feel of end-user facing pages so they can be integrated with
|
{{book.project.name}} provides theme support for web pages and emails. This allows customizing the look and feel of end-user facing pages so they can be
|
||||||
your applications.
|
integrated with your applications.
|
||||||
|
|
||||||
[[img-sunset]]
|
image::../../images/login-sunrise.png[caption="",title="Login page with sunrise example theme"]
|
||||||
.Login page with sunrise example theme
|
|
||||||
image:../../images/login-sunrise.png[]
|
|
||||||
|
|
||||||
== Theme types
|
=== Theme Types
|
||||||
|
|
||||||
A theme can support several types to customize different aspects of Keycloak. The types currently available are:
|
A theme can provide one or more types to customize different aspects of {{book.project.name}}. The types available are:
|
||||||
|
|
||||||
* Account - Account management
|
* Account - Account management
|
||||||
* Admin - Admin console
|
* Admin - Admin console
|
||||||
|
@ -17,234 +16,333 @@ A theme can support several types to customize different aspects of Keycloak. Th
|
||||||
* Login - Login forms
|
* Login - Login forms
|
||||||
* Welcome - Welcome page
|
* Welcome - Welcome page
|
||||||
|
|
||||||
== Configure theme
|
=== Configure Theme
|
||||||
|
|
||||||
All theme types, except welcome, is configured through `Keycloak Admin Console`. To change the theme used for a realm open the `Keycloak Admin Console`, select
|
All theme types, except welcome, is configured through the `Admin Console`. To change the theme used for a realm open the `Admin Console`, select
|
||||||
your realm from the drop-down box in the top left corner. Under `Settings` click on `Theme`.
|
your realm from the drop-down box in the top left corner. Under `Settings` click on `Theme`.
|
||||||
|
|
||||||
To set the theme for the `master` Keycloak admin console set the admin console theme for the `master` realm.
|
NOTE: To set the theme for the `master` admin console you need to set the admin console theme for the `master` realm
|
||||||
To set the theme for per realm admin access control set the admin console theme for the corresponding realm.
|
|
||||||
|
|
||||||
To change the welcome theme you need to edit `standalone/configuration/keycloak-server.json` and add `welcomeTheme` to the theme element, for example:
|
To change the welcome theme you need to edit `standalone/configuration/keycloak-server.json` and add `welcomeTheme` to the theme element, for example:
|
||||||
|
|
||||||
[source]
|
[source,json]
|
||||||
----
|
----
|
||||||
|
|
||||||
"theme": {
|
"theme": {
|
||||||
...
|
...
|
||||||
"welcomeTheme": "custom-theme"
|
"welcomeTheme": "custom-theme",
|
||||||
|
...
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
== Default themes
|
=== Default Themes
|
||||||
|
|
||||||
Keycloak comes bundled with default themes in the server's root `themes` directory.
|
{{book.project.name}} comes bundled with default themes in the server's root `themes` directory. To simplify upgrading you should not edit the bundled themes
|
||||||
You should not edit the bundled themes directly.
|
directly. Instead create your own theme that extends one of the bundle themes.
|
||||||
Instead create a new theme that extends a bundled theme.
|
|
||||||
|
|
||||||
== Creating a theme
|
=== Creating a Theme
|
||||||
|
|
||||||
A theme consists of:
|
A theme consists of:
|
||||||
|
|
||||||
* FreeMarker
|
* HTML templates (http://freemarker.org[Freemarker Templates])
|
||||||
* Stylesheets
|
|
||||||
* Scripts
|
|
||||||
* Images
|
* Images
|
||||||
* Message bundles
|
* Message bundles
|
||||||
* Theme properties
|
* Stylesheets
|
||||||
|
* Scripts
|
||||||
|
* Theme properties
|
||||||
|
|
||||||
A theme can extend another theme.
|
Unless you plan to replace every single page you should extend another theme. Most likely you will want to extend the {{book.project.name}} theme, but you could also
|
||||||
When extending a theme you can override individual files (templates, stylesheets, etc.). The recommended way to create a theme is to extend the base theme.
|
consider extending the base theme if you are significantly changing the look and feel of the pages. The base theme primarily consists of HTML templates and
|
||||||
The base theme provides templates and a default message bundle.
|
message bundles, while the {{book.project.name}} theme primarily contains images and stylesheets.
|
||||||
If you decide to override templates bear in mind that you may need to update your templates when upgrading to a new release to include any changes made to the original template.
|
|
||||||
|
|
||||||
Before creating a theme it's a good idea to disable caching as this makes it possible to edit theme resources without restarting the server.
|
When extending a theme you can override individual resources (templates, stylesheets, etc.). If you decide to override HTML templates bear in mind that you may
|
||||||
To do this open `../standalone/configuration/keycloak-server.json` for `theme` set `staticMaxAge` to `-1` and `cacheTemplates` and `cacheThemes` to `false`.
|
need to update your custom template when upgrading to a new release.
|
||||||
For example:
|
|
||||||
|
|
||||||
[source]
|
While creating a theme it's a good idea to disable caching as this makes it possible to edit theme resources directly from the `themes` directory without
|
||||||
|
restarting {{book.project.name}}. To do this edit `standalone/configuration/keycloak-server.json` for `theme` set `staticMaxAge` to `-1` and both
|
||||||
|
`cacheTemplates` and `cacheThemes` to `false`:
|
||||||
|
|
||||||
|
[source,json]
|
||||||
----
|
----
|
||||||
[
|
|
||||||
"theme": {
|
"theme": {
|
||||||
"default": "keycloak",
|
...
|
||||||
"staticMaxAge": -1,
|
"staticMaxAge": -1,
|
||||||
"cacheTemplates": false,
|
"cacheTemplates": false,
|
||||||
"cacheThemes": false,
|
"cacheThemes": false,
|
||||||
"folder": {
|
...
|
||||||
"dir": "${jboss.home.dir}/themes"
|
}
|
||||||
}
|
----
|
||||||
},
|
|
||||||
----
|
|
||||||
Remember to re-enable caching in production as it will significantly impact performance.
|
Remember to re-enable caching in production as it will significantly impact performance.
|
||||||
|
|
||||||
To create a new theme create a directory for the theme in the server's root `themes`.
|
To create a new theme start by creating a new directory in the `themes` directory. The name of the directory becomes the name of the theme. For example to
|
||||||
The name of the directory should be the name of the theme.
|
create a theme called `mytheme` create the directory `themes/mytheme`.
|
||||||
For example to create a theme called `example-theme` create the directory `themes/example-theme`.
|
|
||||||
Inside the theme directory you then need to create a directory for each of the types your theme is going to provide.
|
|
||||||
For example to add the login type to the `example-theme` theme create the directory `themes/example-theme/login`.
|
|
||||||
|
|
||||||
For each type create a file `theme.properties` which allows setting some configuration for the theme, for example what theme it overrides and if it should import any themes.
|
Inside the theme directory create a directory for each of the types your theme is going to provide. For example to add the login type to the `mytheme`
|
||||||
For the above example we want to override the base theme and import common resources from the Keycloak theme.
|
theme create the directory `themes/mytheme/login`.
|
||||||
To do this create the file `themes/example-theme/login/theme.properties` with following contents:
|
|
||||||
|
For each type create a file `theme.properties` which allows setting some configuration for the theme, for example what theme it overrides and if it should
|
||||||
|
import any themes. For the above example we want to override the base theme and import common resources from the Keycloak theme.
|
||||||
|
|
||||||
|
To do this create the file `themes/mytheme/login/theme.properties` with following contents:
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
[
|
|
||||||
parent=base
|
parent=base
|
||||||
import=common/keycloak
|
import=common/keycloak
|
||||||
----
|
----
|
||||||
|
|
||||||
|
You have now created a theme with support for the login type. To check that it works open the admin console. Select your realm and click on `Themes`.
|
||||||
|
For `Login Theme` select `mytheme` and click `Save`. Then open the login page for the realm.
|
||||||
|
|
||||||
You have now created a theme with support for the login type.
|
|
||||||
To check that it works open the admin console.
|
|
||||||
Select your realm and click on `Themes`.
|
|
||||||
For `Login Theme` select `example-theme` and click `Save`.
|
|
||||||
Then open the login page for the realm.
|
|
||||||
You can do this either by login through your application or by opening `http://localhost:8080/realms/<realm name>/account`.
|
You can do this either by login through your application or by opening `http://localhost:8080/realms/<realm name>/account`.
|
||||||
|
|
||||||
To see the effect of changing the parent theme, set `parent=keycloak` in `theme.properties` and refresh the login page.
|
To see the effect of changing the parent theme, set `parent=keycloak` in `theme.properties` and refresh the login page.
|
||||||
To follow the rest of the documentation set it back to `parent=base` before continuing.
|
|
||||||
|
|
||||||
=== Stylesheets
|
==== Theme Properties
|
||||||
|
|
||||||
A theme can have one or more stylesheets, to add a stylesheet create a file inside `resources/css` (for example `resources/css/styles.css`) inside your theme folder.
|
Theme properties are set in the file `<THEME TYPE>/theme.properties` in the theme directory.
|
||||||
Then registering it in `theme.properties` by adding:
|
|
||||||
|
|
||||||
[source]
|
* parent - Parent theme to extend
|
||||||
|
* import - Import resources from another theme
|
||||||
|
* styles - Space-separated list of styles to include
|
||||||
|
* locales - Comma-separated list of supported locales
|
||||||
|
|
||||||
|
There are a list of properties that can be used to change the css class used for certain element types. Refer to the theme.properties file in the
|
||||||
|
{{book.project.name}} theme for these.
|
||||||
|
|
||||||
|
You can also add your own custom properties and use them from custom templates.
|
||||||
|
|
||||||
|
==== Stylesheets
|
||||||
|
|
||||||
|
A theme can have one or more stylesheets, to add a stylesheet create a file in the `<THEME TYPE>/resources/css` directory of your theme. Then add it to the `styles`
|
||||||
|
property in `theme.properties`.
|
||||||
|
|
||||||
|
For example to add `styles.css` to the `mytheme` create `themes/mytheme/login/resources/css/styles.css` with the following content:
|
||||||
|
|
||||||
|
[source,css]
|
||||||
----
|
----
|
||||||
styles=css/styles.css
|
.login-pf body {
|
||||||
----
|
background: DimGrey none;
|
||||||
|
|
||||||
The `styles` property supports a space separated list so you can add as many as you want.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
styles=css/styles.css css/more-styles.css
|
|
||||||
----
|
|
||||||
`example-theme/login/resources/css/styles.css`
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
#kc-form {
|
|
||||||
background-color: #000;
|
|
||||||
color: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
`example-theme/login/theme.properties`
|
|
||||||
|
Then edit `themes/mytheme/login/theme.properties` and add:
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
styles=css/styles.css
|
styles=css/styles.css
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Scripts
|
To see the changes open the login page for your realm. You will notice that the only styles being applied are those from your custom stylesheet. To include the
|
||||||
|
styles from the parent theme you need to load the styles from that theme as well. Do this by editing `themes/mytheme/login/theme.properties` and changing `styles`
|
||||||
|
to:
|
||||||
|
|
||||||
A theme can have one or more scripts, to add a script create a file inside `resources/js` (for example `resources/js/script.js`) inside your theme folder.
|
[source]
|
||||||
Then registering it in `theme.properties` by adding:
|
----
|
||||||
|
styles=lib/patternfly/css/patternfly.css lib/zocial/zocial.css css/login.css css/styles.css
|
||||||
|
----
|
||||||
|
|
||||||
|
NOTE: To override styles from the parent stylesheets it's important that your stylesheet is listed last.
|
||||||
|
|
||||||
|
==== Scripts
|
||||||
|
|
||||||
|
A theme can have one or more scripts, to add a script create a file in the `<THEME TYPE>/resources/js` directory of your theme. Then add it to the `scripts`
|
||||||
|
property in `theme.properties`.
|
||||||
|
|
||||||
|
For example to add `script.js` to the `mytheme` create `themes/mytheme/login/resources/js/script.js` with the following content:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
alert('Hello');
|
||||||
|
----
|
||||||
|
|
||||||
|
Then edit `themes/mytheme/login/theme.properties` and add:
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
scripts=js/script.js
|
scripts=js/script.js
|
||||||
----
|
----
|
||||||
|
|
||||||
The `scripts` property supports a space separated list so you can add as many as you want.
|
==== Images
|
||||||
For example:
|
|
||||||
|
|
||||||
[source]
|
To make images available to the theme add them to the `<THEME TYPE>/resources/img` directory of your theme. These can be used from within stylesheets or
|
||||||
----
|
directly in HTML templates.
|
||||||
scripts=js/script.js js/more-script.js
|
|
||||||
----
|
|
||||||
|
|
||||||
=== Images
|
For example to add an image to the `mytheme` copy an image to `themes/mytheme/login/resources/img/image.jpg`.
|
||||||
|
|
||||||
To make images available to the theme add them to `resources/img`.
|
You can then use this image from within a custom stylesheet with:
|
||||||
They can then be used through stylesheets.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
[source]
|
[source,css]
|
||||||
----
|
----
|
||||||
body {
|
body {
|
||||||
background-image: url('../img/image.jpg');
|
background-image: url('../img/image.jpg');
|
||||||
|
background-size: cover;
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
Or in templates, for example:
|
Or to use directly in HTML templates add the following to a custom HTML template:
|
||||||
|
|
||||||
[source]
|
[source,html]
|
||||||
----
|
----
|
||||||
<img src="${url.resourcesPath}/img/image.jpg">
|
<img src="${url.resourcesPath}/img/image.jpg">
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Messages
|
==== Messages
|
||||||
|
|
||||||
Text in the templates are loaded from message bundles.
|
Text in the templates are loaded from message bundles. A theme that extends another theme will inherit all messages from the parents message bundle and you can
|
||||||
A theme that extends another theme will inherit all messages from the parents message bundle, but can override individual messages.
|
override individual messages by adding `<THEME TYPE>/messages/messages_en.properties` to your theme.
|
||||||
For example to replace `Username` on the login form with `Your Username` create the file `messages/messages.properties` inside your theme folder and add the following content:
|
|
||||||
|
For example to replace `Username` on the login form with `Your Username` for the `mytheme` create the file
|
||||||
|
`themes/mytheme/login/messages/messages_en.properties` with the following content:
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
username=Your Username
|
usernameOrEmail=Your Username
|
||||||
----
|
----
|
||||||
|
|
||||||
For the admin console, there is a second resource bundle named `admin-messages.properties`.
|
==== Internationalization
|
||||||
This resource bundle is converted to JSON and shipped to the console to be processed by angular-translate.
|
|
||||||
It is found in the same directory as messages.properties and can be overridden in the same way as described above.
|
|
||||||
|
|
||||||
=== Modifying HTML
|
{{book.project.name}} supports internationalization. To enable internationalization for a realm see {{book.adminguide.link}}[{{book.adminguide.name}}]. This
|
||||||
|
section will describe how you can add your own language.
|
||||||
|
|
||||||
Keycloak uses http://freemarker.org[Freemarker Templates] in order to generate HTML.
|
To add a new language create the file `<THEME TYPE>/messages/messages_<LOCALE>` in the directory of your theme. Then add it to the `locales` property in
|
||||||
These templates are defined in `.ftl` files and can be overriden from the base theme.
|
`<THEME TYPE>/theme.properties`. For a language to be available to users the realms `login`, `account` and `email` theme has to support the language, so you
|
||||||
Check out the Freemarker website on how to form a template file.
|
need to add your language for those theme types.
|
||||||
To override the login template for the `example-theme` copy `themes/base/login/login.ftl` to `themes/example-theme/login` and open it in an editor.
|
|
||||||
After the first line (<#import ...>) add `<h1>HELLO WORLD!</h1>` then refresh the page.
|
|
||||||
|
|
||||||
== Deploying themes
|
For example to add Norwegian translations to the `mytheme` theme create the file `themes/mytheme/login/messages/messages_no.properties` with the
|
||||||
|
following content:
|
||||||
|
|
||||||
Themes can be deployed to Keycloak by copying the theme directory to `themes` or it can be deployed as a module.
|
[source]
|
||||||
For a single server or during development just copying the theme is fine, but in a cluster or domain it's recommended to deploy as a module.
|
----
|
||||||
|
usernameOrEmail=Brukernavn
|
||||||
|
password=Passord
|
||||||
|
----
|
||||||
|
|
||||||
To deploy a theme as a module you need to create an jar (it's basically just a zip with jar extension) with the theme resources and a file `META/keycloak-themes.json` that describes the themes contained in the archive.
|
All messages you don't provide a translation for will use the default English translation.
|
||||||
For example `example-theme.jar` with the contents:
|
|
||||||
|
Then edit `themes/mytheme/login/theme.properties` and add:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
locales=en,no
|
||||||
|
----
|
||||||
|
|
||||||
|
You also need to do the same for the `account` and `email` theme types. To do this create `themes/mytheme/account/messages/messages_no.properties` and
|
||||||
|
`themes/mytheme/email/messages/messages_no.properties`. Leaving these files empty will result in the English messages being used. Then copy
|
||||||
|
`themes/mytheme/login/theme.properties` to `themes/mytheme/account/theme.properties` and `themes/mytheme/email/theme.properties`.
|
||||||
|
|
||||||
|
Finally you need to add a translation for the language selector. This is done by adding a message to the English translation. To do this add the following to
|
||||||
|
`themes/mytheme/account/messages/messages_en.properties` and `themes/mytheme/login/messages/messages_en.properties`:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
locale_no=Norsk
|
||||||
|
----
|
||||||
|
|
||||||
|
==== HTML Templates
|
||||||
|
|
||||||
|
{{book.project.name}} uses http://freemarker.org[Freemarker Templates] in order to generate HTML. You can override individual templates in your own theme by
|
||||||
|
creating `<THEME TYPE>/<TEMPLATE>.ftl`. For a list of templates used see `themes/base/<THEME TYPE>`.
|
||||||
|
|
||||||
|
When creating a custom template it is a good idea to copy the template from the base theme to your own theme, then applying the modifications you need. Bear in
|
||||||
|
mind when upgrading to a new version of {{book.project.name}} you may need to update your custom templates to apply changes to the original template if
|
||||||
|
applicable.
|
||||||
|
|
||||||
|
For example to create a custom login form for the `mytheme` theme copy `themes/base/login/login.ftl` to `themes/mytheme/login` and open it in an editor.
|
||||||
|
After the first line (<#import ...>) add `<h1>HELLO WORLD!</h1>` like so:
|
||||||
|
|
||||||
|
[source,html]
|
||||||
|
----
|
||||||
|
<#import "template.ftl" as layout>
|
||||||
|
<h1>HELLO WORLD!</h1>
|
||||||
|
...
|
||||||
|
----
|
||||||
|
|
||||||
|
Check out the http://freemarker.org/docs/index.html[FreeMarker Manual] for more details on how to edit templates.
|
||||||
|
|
||||||
|
==== Emails
|
||||||
|
|
||||||
|
To edit the subject and contents for emails, for example password recovery email, add a message bundle to the `email` type of your theme. Each email used three
|
||||||
|
messages. One for the subject, one for the plain text body and one for the html body.
|
||||||
|
|
||||||
|
To see all emails available take a look at `themes/base/email/messages/messages_en.properties`.
|
||||||
|
|
||||||
|
For example to change the password recovery email for the `mytheme` theme create `themes/mytheme/email/messages/messages_en.properties` with the following
|
||||||
|
content:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
passwordResetSubject=My password recovery
|
||||||
|
passwordResetBody=Reset password link: {0}
|
||||||
|
passwordResetBodyHtml=<a href="{0}">Reset password</a>
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Deploying Themes
|
||||||
|
|
||||||
|
Themes can be deployed to {{book.project.name}} by copying the theme directory to `themes` or it can be deployed as an archive. During development copying the
|
||||||
|
theme to the `themes` directory, but in production you may want to consider using an `archive`. An `archive` makes it simpler to have a versioned copy of
|
||||||
|
the theme, especially when you have multiple instances of {{book.project.name}} for example with clustering.
|
||||||
|
|
||||||
|
To deploy a theme as an archive you need to create a ZIP archive with the theme resources. You also need to add a file `META/keycloak-themes.json` to the
|
||||||
|
archive that lists the available themes in the archive as well as what types each theme provides.
|
||||||
|
|
||||||
|
For example for the `mytheme` theme create `mytheme.zip` with the contents:
|
||||||
|
|
||||||
* META-INF/keycloak-themes.json
|
* META-INF/keycloak-themes.json
|
||||||
* theme/example-theme/login/theme.properties
|
* theme/mytheme/login/theme.properties
|
||||||
* theme/example-theme/login/login.ftl
|
* theme/mytheme/login/login.ftl
|
||||||
* theme/example-theme/login/resources/css/styles.css
|
* theme/mytheme/login/resources/css/styles.css
|
||||||
|
* theme/mytheme/login/resources/img/image.png
|
||||||
|
* theme/mytheme/login/messages/messages_en.properties
|
||||||
|
* theme/mytheme/email/messages/messages_en.properties
|
||||||
|
|
||||||
The contents of META-INF/keycloak-themes.json in this case would be:
|
The contents of `META-INF/keycloak-themes.json` in this case would be:
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
[
|
|
||||||
{
|
{
|
||||||
"themes": [{
|
"themes": [{
|
||||||
"name" : "example-theme",
|
"name" : "mytheme",
|
||||||
"types": [ "login" ]
|
"types": [ "login", "email" ]
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
A single archive can contain multiple themes and each theme can support one or more types.
|
||||||
|
|
||||||
|
The deploy the archive to {{book.project.name}} you can either manually create a module in `modules` or use the `jboss-cli` command. It's simplest to use
|
||||||
|
`jboss-cli` as it creates the required directories and module descriptor for you.
|
||||||
|
|
||||||
|
To deploy `mytheme.zip` on Linux run:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
bin/jboss-cli.sh --command="module add --name=org.example.mytheme --resources=mytheme.zip"
|
||||||
----
|
----
|
||||||
As you can see a single jar can contain multiple themes and each theme can support one or more types.
|
|
||||||
|
|
||||||
The deploy the jar as a module to Keycloak you can either manually create the module or use `jboss-cli`.
|
On Windows run:
|
||||||
It's simplest to use `jboss-cli` as it creates the required directories and module descriptor for you.
|
|
||||||
|
|
||||||
To deploy the above jar `jboss-cli` run:
|
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
[
|
bin\jboss-cli.bat --command="module add --name=org.example.mytheme --resources=mytheme.zip"
|
||||||
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.example.exampletheme --resources=example-theme.jar"
|
----
|
||||||
----
|
|
||||||
If you're on windows run
|
|
||||||
|
|
||||||
[source]
|
This command creates `modules/org/example/mytheme/main` directory with the `mytheme.zip` archive and `module.xml`.
|
||||||
|
|
||||||
|
To manually create the module create the directory `modules/org/keycloak/example/themes/main`, copy `mytheme.zip` to this directory and create the file
|
||||||
|
`modules/org/keycloak/example/themes/main/module.xml` with the contents:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
----
|
----
|
||||||
KEYCLOAK_HOME/bin/jboss-cli.bat
|
<?xml version="1.0" ?>
|
||||||
|
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.example.themes">
|
||||||
|
<resources>
|
||||||
|
<resource-root path="theme.zip"/>
|
||||||
|
</resources>
|
||||||
|
</module>
|
||||||
----
|
----
|
||||||
This command creates `modules/org/example/exampletheme/main` containing `example-theme.jar` and `module.xml`.
|
|
||||||
Once you've created the module you need to register it with Keycloak do this by editing `../standalone/configuration/keycloak-server.json` and adding the module to `theme/module/modules`.
|
You also need to register the module with {{book.project.name}}. This is done by editing `standalone/configuration/keycloak-server.json` and adding the module
|
||||||
For example:
|
to `theme/module/modules`. For example:
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
----
|
----
|
||||||
|
@ -252,43 +350,9 @@ For example:
|
||||||
"theme": {
|
"theme": {
|
||||||
...
|
...
|
||||||
"module": {
|
"module": {
|
||||||
"modules": [ "org.example.exampletheme" ]
|
"modules": [ "org.example.mytheme" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
If a theme is deployed to `themes` and as a module the first is used.
|
NOTE: If the same theme is deployed to both the `themes` directory and as a module the version in the `themes` directory is used.
|
||||||
|
|
||||||
== SPIs
|
|
||||||
|
|
||||||
For full control of login forms and account management Keycloak provides a number of SPIs.
|
|
||||||
|
|
||||||
=== Account SPI
|
|
||||||
|
|
||||||
The Account SPI allows implementing the account management pages using whatever web framework or templating engine you want.
|
|
||||||
To create an Account provider implement `org.keycloak.account.AccountProviderFactory` and `org.keycloak.account.AccountProvider`.
|
|
||||||
|
|
||||||
Once you have deployed your account provider to Keycloak you need to configure `keycloak-server.json` to specify which provider should be used:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
|
|
||||||
"account": {
|
|
||||||
"provider": "custom-provider"
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
=== Login SPI
|
|
||||||
|
|
||||||
The Login SPI allows implementing the login forms using whatever web framework or templating engine you want.
|
|
||||||
To create a Login forms provider implement `org.keycloak.login.LoginFormsProviderFactory` and `org.keycloak.login.LoginFormsProvider` in `forms/login-api`.
|
|
||||||
|
|
||||||
Once you have deployed your account provider to Keycloak you need to configure `keycloak-server.json` to specify which provider should be used:
|
|
||||||
|
|
||||||
[source]
|
|
||||||
----
|
|
||||||
|
|
||||||
"login": {
|
|
||||||
"provider": "custom-provider"
|
|
||||||
}
|
|
||||||
----
|
|
15
topics/user-federation-mapper.adoc
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
[[_user_federation_mapper]]
|
||||||
|
== User Federation Mapper SPI
|
||||||
|
|
||||||
|
=== LDAP Mapper
|
||||||
|
|
||||||
|
For the more advanced usecases, you have the possibility to create your own implementation of LDAP mapper or just subclass from some already existing mapper
|
||||||
|
implementation. You will need to implement `UserFederationMapperFactory` interface.
|
||||||
|
|
||||||
|
In most cases, instead of creating `UserFederationMapperFactory` from scratch, you can create subclasses of `AbstractLDAPFederationMapperFactory`, which itself
|
||||||
|
implements `UserFederationMapperFactory`.
|
||||||
|
|
||||||
|
Then you need to create mapper implementation, which will be subclass of `AbstractLDAPFederationMapper` (this mapper implementation will be returned by
|
||||||
|
`YourAbstractLDAPFederationMapperFactorySubclass.createMapper` method).
|
||||||
|
|
||||||
|
For details on how to package and deploy a custom provider refer to the <<providers.adoc#providers,Service Provider Interfaces>> chapter.
|
|
@ -1,178 +1,15 @@
|
||||||
[[_user_federation]]
|
[[_user_federation]]
|
||||||
= User Federation SPI and LDAP/AD Integration
|
== User Federation SPI
|
||||||
|
|
||||||
Keycloak can federate external user databases.
|
The keycloak examples directory contains an example of a simple User Federation Provider backed by a simple properties file. See `providers/federation-provider`
|
||||||
Out of the box we have support for LDAP and Active Directory.
|
in the examples distribution. Most of how to create a federation provider is explained directly within the example code, but some information is here too.
|
||||||
Before you dive into this, you should understand how Keycloak does federation.
|
|
||||||
|
|
||||||
Keycloak performs federation a bit differently than other products/projects.
|
Writing a User Federation Provider starts by implementing the `UserFederationProvider` and `UserFederationProviderFactory` interfaces. Please see the Javadoc
|
||||||
The vision of Keycloak is that it is an out of the box solution that should provide a core set of feature irregardless of the backend user storage you want to use.
|
and example for complete details on how to do this.
|
||||||
Because of this requirement/vision, Keycloak has a set data model that all of its services use.
|
|
||||||
Most of the time when you want to federate an external user store, much of the metadata that would be needed to provide this complete feature set does not exist in that external store.
|
|
||||||
For example your LDAP server may only provide password validation, but not support TOTP or user role mappings.
|
|
||||||
The Keycloak User Federation SPI was written to support these completely variable configurations.
|
|
||||||
|
|
||||||
The way user federation works is that Keycloak will import your federated users on demand to its local storage.
|
Some important methods of note: getUserByUsername() and getUserByEmail() require that you query your federated storage and if the user exists create and import
|
||||||
How much metadata that is imported depends on the underlying federation plugin and how that plugin is configured.
|
the user into Keycloak storage. How much metadata you import is fully up to you. This import is done by invoking methods on the object returned
|
||||||
Some federation plugins may only import the username into Keycloak storage, others might import everything from name, address, and phone number, to user role mappings.
|
`KeycloakSession.userStorage()` to add and import user information. The proxy() method will be called whenever Keycloak has found an imported UserModel.
|
||||||
Some plugins might want to import credentials directly into Keycloak storage and let Keycloak handle credential validation.
|
|
||||||
Others might want to handle credential validation themselves.
|
|
||||||
The goal of the Federation SPI is to support all of these scenarios.
|
|
||||||
|
|
||||||
== LDAP and Active Directory Plugin
|
|
||||||
|
|
||||||
Keycloak comes with a built-in LDAP/AD plugin.
|
|
||||||
By default, it is set up only to import username, email, first and last name, but you are free to configure <<_ldap_mappers,mappers>> and add more attributes or delete default ones.
|
|
||||||
It supports password validation via LDAP/AD protocols and different user metadata synchronization modes.
|
|
||||||
To configure a federated LDAP store go to the admin console.
|
|
||||||
Click on the `Users` menu option to get you to the user management page.
|
|
||||||
Then click on the `Federation` submenu option.
|
|
||||||
When you get to this page there is an "Add Provider" select box.
|
|
||||||
You should see "ldap" within this list.
|
|
||||||
Selecting "ldap" will bring you to the ldap configuration page.
|
|
||||||
|
|
||||||
=== Edit Mode
|
|
||||||
|
|
||||||
Edit mode defines various synchronization options with your LDAP store depending on what privileges you have.
|
|
||||||
|
|
||||||
READONLY::
|
|
||||||
Username, email, first and last name and other mapped attributes will be unchangeable.
|
|
||||||
Keycloak will show an error anytime anybody tries to update these fields.
|
|
||||||
Also, password updates will not be supported.
|
|
||||||
|
|
||||||
WRITABLE::
|
|
||||||
Username, email, first and last name, other mapped attributes and passwords can all be updated and will be synchronized automatically with your LDAP store.
|
|
||||||
|
|
||||||
UNSYNCED::
|
|
||||||
Any changes to username, email, first and last name, and passwords will be stored in Keycloak local storage.
|
|
||||||
It is up to you to figure out how to synchronize back to LDAP.
|
|
||||||
|
|
||||||
=== Other config options
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Display Name::
|
|
||||||
Name used when this provider is referenced in the admin console
|
|
||||||
|
|
||||||
Priority::
|
|
||||||
The priority of this provider when looking up users or for adding registrations.
|
|
||||||
|
|
||||||
Sync Registrations::
|
|
||||||
If a new user is added through a registration page or admin console, should the user be eligible to be synchronized to this provider.
|
|
||||||
|
|
||||||
Allow Kerberos authentication::
|
|
||||||
Enable Kerberos/SPNEGO authentication in realm with users data provisioned from LDAP.
|
|
||||||
More info in <<_kerberos,Kerberos section>>.
|
|
||||||
|
|
||||||
Other options::
|
|
||||||
The rest of the configuration options should be self explanatory.
|
|
||||||
You can use tooltips in admin console to see some more details about them.
|
|
||||||
|
|
||||||
=== Connect to LDAP over SSL
|
|
||||||
|
|
||||||
When you configure secured connection URL to LDAP (for example `ldaps://myhost.com:636` ) the Keycloak will use SSL for the communication with LDAP server.
|
|
||||||
The important thing is to properly configure truststore on the Keycloak server side, because SSL won't work if Keycloak can't trust the SSL connection with LDAP (Keycloak acts as the `client` here, when LDAP acts as server).
|
|
||||||
|
|
||||||
The global truststore for the Keycloak can be configured with Truststore SPI in the `keycloak-server.json` file and it's described in the details <<_truststore,here>>.
|
|
||||||
If you don't configure truststore SPI, the truststore will fallback to the default mechanism provided by Java (either the file provided by system property `javax.net.ssl.trustStore` or finally the cacerts file from JDK if even the system property is not set).
|
|
||||||
|
|
||||||
There is configuration property `Use Truststore SPI` in the LDAP federation provider configuration, where you can choose whether Truststore SPI is used.
|
|
||||||
By default, the value is `ldaps only`, which is fine for most of deployments, because attempt to use Truststore SPI is done just if connection to LDAP starts with `ldaps` .
|
|
||||||
|
|
||||||
== Sync of LDAP users to Keycloak
|
|
||||||
|
|
||||||
LDAP Federation Provider will automatically take care of synchronization (import) of needed LDAP users into Keycloak database.
|
|
||||||
For example once you first authenticate LDAP user `john` from Keycloak UI, LDAP Federation provider will first import this LDAP user into Keycloak database and then authenticate against LDAP password.
|
|
||||||
|
|
||||||
Federation Provider imports just requested users by default, so if you click to `View all users` in Keycloak admin console, you will see just those LDAP users, which were already authenticated/requested by Keycloak.
|
|
||||||
|
|
||||||
If you want to sync all LDAP users into Keycloak database, you may configure and enable Sync, which is in admin console on same page like the configuration of Federation provider itself.
|
|
||||||
There are 2 types of sync:
|
|
||||||
|
|
||||||
Full sync::
|
|
||||||
This will synchronize all LDAP users into Keycloak DB.
|
|
||||||
Those LDAP users, which already exist in Keycloak and were changed in LDAP directly will be updated in Keycloak DB (For example if user `Mary Kelly` was changed in LDAP to `Mary Doe`).
|
|
||||||
|
|
||||||
Changed users sync::
|
|
||||||
This will check LDAP and it will sync into Keycloak just those users, which were created or updated in LDAP from the time of last sync.
|
|
||||||
|
|
||||||
In usual cases you may want to trigger full sync at the beginning, so you will import all LDAP users to Keycloak just once.
|
|
||||||
Then you may setup periodic sync of changed users, so Keycloak will periodically ask LDAP server for newly created or updated users and backport them to Keycloak DB.
|
|
||||||
Also you may want to trigger full sync again after some longer time or setup periodic full sync as well.
|
|
||||||
|
|
||||||
In admin console, you can trigger sync directly or you can enable periodic changed or full sync.
|
|
||||||
|
|
||||||
[[_ldap_mappers]]
|
|
||||||
== LDAP/Federation mappers
|
|
||||||
|
|
||||||
LDAP mappers are `listeners`, which are triggered by LDAP Federation provider at various points and provide another extension point to LDAP integration.
|
|
||||||
They are triggered during import LDAP user into Keycloak, registration Keycloak user back to LDAP or when querying LDAP user from Keycloak.
|
|
||||||
When you create LDAP Federation provider, Keycloak will automatically provide set of builtin `mappers` for this provider.
|
|
||||||
You are free to change this set and create new mapper or update/delete existing ones.
|
|
||||||
|
|
||||||
By default, we have those implementation of LDAP federation mapper:
|
|
||||||
|
|
||||||
User Attribute Mapper::
|
|
||||||
This allows to specify which LDAP attribute is mapped to which attribute of Keycloak User.
|
|
||||||
So for example you can configure that LDAP attribute `mail` is supposed to be mapped to the UserModel attribute `email` in Keycloak database.
|
|
||||||
For this mapper implementation, there is always one-to-one mapping (one LDAP attribute mapped to one Keycloak UserModel attribute)
|
|
||||||
|
|
||||||
FullName Mapper::
|
|
||||||
This allows to specify that fullname of user, which is saved in some LDAP attribute (usualy `cn` ) will be mapped to `firstName` and `lastname` attributes of UserModel.
|
|
||||||
Having `cn` to contain full name of user is common case for some LDAP deployments.
|
|
||||||
|
|
||||||
Role Mapper::
|
|
||||||
This allows to configure role mappings from LDAP into Keycloak role mappings.
|
|
||||||
One Role mapper can be used to map LDAP roles (usually groups from particular branch of LDAP tree) into roles corresponding to either realm roles or client roles of specified client.
|
|
||||||
It's not a problem to configure more Role mappers for same LDAP provider.
|
|
||||||
So for example you can specify that role mappings from groups under `ou=main,dc=example,dc=org` will be mapped to realm role mappings and role mappings from groups under `ou=finance,dc=example,dc=org` will be mapped to client role mappings of client `finance` .
|
|
||||||
|
|
||||||
Hardcoded Role Mapper::
|
|
||||||
This mapper will grant specified Keycloak role to each Keycloak user linked with LDAP.
|
|
||||||
|
|
||||||
Group Mapper::
|
|
||||||
This allows to configure group mappings from LDAP into Keycloak group mappings.
|
|
||||||
Group mapper can be used to map LDAP groups from particular branch of LDAP tree into groups in Keycloak.
|
|
||||||
And it will also propagate user-group mappings from LDAP into user-group mappings in Keycloak.
|
|
||||||
|
|
||||||
MSAD User Account Mapper::
|
|
||||||
Mapper specific to Microsoft Active Directory (MSAD). It's able to tightly integrate the MSAD user account state into Keycloak account state (account enabled, password is expired etc). It's using `userAccountControl` and `pwdLastSet` LDAP attributes for that (both are specific to MSAD and are not LDAP standard). For example if pwdLastSet is 0, the Keycloak user is required to update password (there will be UPDATE_PASSWORD required action added to him in Keycloak). Or if userAccountControl is 514 (disabled account) the Keycloak user is disabled as well etc.
|
|
||||||
|
|
||||||
By default, there is set of User Attribute mappers to map basic UserModel attributes username, first name, lastname and email to corresponding LDAP attributes.
|
|
||||||
You are free to extend this and provide more attribute mappings (For example to street, postalCode etc), delete firstName/lastname mapper and put fullName mapper instead, add role mappers etc.
|
|
||||||
Admin console provides tooltips, which should help on how to configure corresponding mappers.
|
|
||||||
|
|
||||||
We have an example, which is showing LDAP integration and set of base mappers and sample mappers (mappers for street and postalCode) . It's in `examples/ldap` in the Keycloak example distribution or demo distribution download.
|
|
||||||
You can also check the example sources directly https://github.com/keycloak/keycloak/blob/master/examples/ldap[here] .
|
|
||||||
|
|
||||||
=== Writing your own LDAP Mapper
|
|
||||||
|
|
||||||
For the more advanced usecases, you have the possibility to create your own implementation of LDAP mapper or just subclass from some already existing mapper implementation.
|
|
||||||
You will need to implement `UserFederationMapperFactory` interface.
|
|
||||||
In most cases, instead of creating `UserFederationMapperFactory` from scratch, you can create subclasses of `AbstractLDAPFederationMapperFactory`, which itself implements `UserFederationMapperFactory`.
|
|
||||||
Then you need to create mapper implementation, which will be subclass of `AbstractLDAPFederationMapper` (this mapper implementation will be returned by `YourAbstractLDAPFederationMapperFactorySubclass.createMapper` method).
|
|
||||||
|
|
||||||
After your code is written you must package up all your classes within a JAR file.
|
|
||||||
This jar file must contain a file called `org.keycloak.mappers.UserFederationMapperFactory` within the `META-INF/services directory` of the JAR.
|
|
||||||
This file is a list of fully qualified classnames of all implementations of `UserFederationMapperFactory`.
|
|
||||||
For more details, look at section for <<_write_federation_provider,Write your own federation provider>> and at <<_providers,Providers and SPI>> section.
|
|
||||||
|
|
||||||
[[_write_federation_provider]]
|
|
||||||
== Writing your own User Federation Provider
|
|
||||||
|
|
||||||
The keycloak examples directory contains an example of a simple User Federation Provider backed by a simple properties file.
|
|
||||||
See `examples/providers/federation-provider`.
|
|
||||||
Most of how to create a federation provider is explained directly within the example code, but some information is here too.
|
|
||||||
|
|
||||||
Writing a User Federation Provider starts by implementing the `UserFederationProvider` and `UserFederationProviderFactory` interfaces.
|
|
||||||
Please see the Javadoc and example for complete details on how to do this.
|
|
||||||
Some important methods of note: getUserByUsername() and getUserByEmail() require that you query your federated storage and if the user exists create and import the user into Keycloak storage.
|
|
||||||
How much metadata you import is fully up to you.
|
|
||||||
This import is done by invoking methods on the object returned `KeycloakSession.userStorage()` to add and import user information.
|
|
||||||
The proxy() method will be called whenever Keycloak has found an imported UserModel.
|
|
||||||
This allows the federation provider to proxy the UserModel which is useful if you want to support external storage updates on demand.
|
This allows the federation provider to proxy the UserModel which is useful if you want to support external storage updates on demand.
|
||||||
|
|
||||||
After your code is written you must package up all your classes within a JAR file.
|
For details on how to package and deploy a custom provider refer to the <<providers.adoc#providers,Service Provider Interfaces>> chapter.
|
||||||
This jar file must contain a file called `org.keycloak.models.UserFederationProviderFactory` within the `META-INF/services` directory of the JAR.
|
|
||||||
This file is a list of fully qualified classnames of all implementations of `UserFederationProviderFactory`.
|
|
||||||
For more details on writing provider implementations and how to deploy to Keycloak refer to the <<_providers,providers>> section.
|
|
||||||
|
|