initial import

This commit is contained in:
Bill Burke 2016-04-18 15:10:32 -04:00
parent d7bafd54fa
commit 8a00945dc2
19 changed files with 1698 additions and 1 deletions

View file

@ -1,5 +1,5 @@
Keycloak Client Applications Guide
Securing Client Applications Guide
======================
image:images/keycloak_logo.png[alt="Keycloak"]

25
SUMMARY.adoc Executable file
View file

@ -0,0 +1,25 @@
== Securing Client Applications Guide
//. link:topics/templates/document-attributes.adoc[]
:imagesdir: images
. link:topics/preface.adoc[Preface]
. link:topics/Overview.adoc[Overview]
. link:topics/oidc.adoc[OpenID Connect Client Adapters]
.. link:topics/jboss-adapter.adoc[JBoss/Wildfly Adapter]
.. link:topics/tomcat-adapter.adoc[Tomcat 6, 7 and 8 Adapters]
.. link:topics/jetty9-adapter.adoc[Jetty 9.x Adapters]
.. link:topics/jetty8-adapter.adoc[Jetty 8.1.x Adapter]
.. link:topics/servlet-filter-adapter.adoc[Java Servlet Filter Adapter]
.. link:topics/fuse-adapter.adoc[JBoss Fuse and Apache Karaf Adapter]
.. link:topics/javascript-adapter.adoc[Javascript Adapter]
.. link:topics/spring-boot-adapter.adoc[Spring Boot Adapter]
.. link:topics/spring-security-adapter.adoc[Spring Security Adapter]
.. link:topics/installed-applications.adoc[Installed Applications]
.. link:topics/logout.adoc[Logout]
.. link:topics/adapter_error_handling.adoc[Error Handling]
.. link:topics/multi-tenancy.adoc[ Multi Tenancy]
.. link:topics/jaas.adoc[JAAS plugin]
. link:topics/saml.adoc[SAML Client Adapters]

BIN
images/keycloak_logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

18
topics/adapter-context.adoc Executable file
View file

@ -0,0 +1,18 @@
= KeycloakSecurityContext
:doctype: book
:sectnums:
:toc: left
:icons: font
:experimental:
:sourcedir: .
The `KeycloakSecurityContext` interface is available if you need to look at the access token directly.
This context is also useful if you need to get the encoded access token so you can make additional REST invocations.
In servlet environments it is available in secured invocations as an attribute in HttpServletRequest.
Or, it is available in secure and insecure requests in the HttpSession for browser apps.
[source]
----
httpServletRequest.getAttribute(KeycloakSecurityContext.class.getName());
httpServletRequest.getSession().getAttribute(KeycloakSecurityContext.class.getName());
----

View file

@ -0,0 +1,60 @@
[[_adapter_error_handling]]
= Error Handling
Keycloak has some error handling facilities for servlet based client adapters.
When an error is encountered in authentication, keycloak will call `HttpServletResponse.sendError()`.
You can set up an error-page within your `web.xml` file to handle the error however you want.
Keycloak may throw 400, 401, 403, and 500 errors.
[source]
----
<error-page>
<error-code>404</error-code>
<location>/ErrorHandler</location>
</error-page>
----
Keycloak also sets an `HttpServletRequest` attribute that you can retrieve.
The attribute name is `org.keycloak.adapters.spi.AuthenticationError`.
Typecast this object to: `org.keycloak.adapters.OIDCAuthenticationError`.
This class can tell you exactly what happened.
If this attribute is not set, then the adapter was not responsible for the error code.
[source]
----
public class OIDCAuthenticationError implements AuthenticationError {
public static enum Reason {
NO_BEARER_TOKEN,
NO_REDIRECT_URI,
INVALID_STATE_COOKIE,
OAUTH_ERROR,
SSL_REQUIRED,
CODE_TO_TOKEN_FAILURE,
INVALID_TOKEN,
STALE_TOKEN,
NO_AUTHORIZATION_HEADER
}
private Reason reason;
private String description;
public OIDCAuthenticationError(Reason reason, String description) {
this.reason = reason;
this.description = description;
}
public Reason getReason() {
return reason;
}
public String getDescription() {
return description;
}
}
----

17
topics/fuse-adapter.adoc Executable file
View file

@ -0,0 +1,17 @@
[[_fuse_adapter]]
= JBoss Fuse and Apache Karaf Adapter
Currently Keycloak supports securing your web applications running inside http://www.jboss.org/products/fuse/overview/[JBoss Fuse] or http://karaf.apache.org/[Apache Karaf] . It leverages <<_jetty8_adapter,Jetty 8 adapter>> as both JBoss Fuse 6.1 and Apache Karaf 3 are bundled with http://eclipse.org/jetty/[Jetty 8.1 server] under the covers and Jetty is used for running various kinds of web applications.
What is supported for Fuse/Karaf is:
* Security for classic WAR applications deployed on Fuse/Karaf with https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+War[Pax Web War Extender].
* Security for servlets deployed on Fuse/Karaf as OSGI services with https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+Whiteboard[Pax Web Whiteboard Extender].
* Security for http://camel.apache.org/[Apache Camel] Jetty endpoints running with http://camel.apache.org/jetty.html[Camel Jetty] component.
* Security for http://cxf.apache.org/[Apache CXF] endpoints running on their own separate http://cxf.apache.org/docs/jetty-configuration.html[Jetty engine].
* Security for http://cxf.apache.org/[Apache CXF] endpoints running on default engine provided by CXF servlet.
* Security for SSH and JMX admin access.
* Security for http://hawt.io/[Hawt.io admin console] .
The best place to start is look at Fuse demo bundled as part of Keycloak examples in directory `examples/fuse` .

View file

@ -0,0 +1,23 @@
= Installed Applications
Keycloak provides two special redirect uris for installed applications.
[[_installed_applications_url]]
== Installed Applications url
http://localhost
This returns the code to a web server on the client as a query parameter.
Any port number is allowed.
This makes it possible to start a web server for the installed application on any free port number without requiring changes in the `Admin Console`.
[[_installed_applications_urn]]
== Installed Applications urn
`urn:ietf:wg:oauth:2.0:oob`
If its not possible to start a web server in the client (or a browser is not available) it is possible to use the special `urn:ietf:wg:oauth:2.0:oob` redirect uri.
When this redirect uri is used Keycloak displays a page with the code in the title and in a box on the page.
The application can either detect that the browser title has changed, or the user can copy/paste the code manually to the application.
With this redirect uri it is also possible for a user to use a different device to obtain a code to paste back to the application.

27
topics/jaas.adoc Executable file
View file

@ -0,0 +1,27 @@
[[_jaas_adapter]]
= JAAS plugin
It's generally not needed to use JAAS for most of the applications, especially if they are HTTP based, but directly choose one of our adapters.
However some applications and systems may still rely on pure legacy JAAS solution.
Keycloak provides couple of login modules to help with such use cases.
Some login modules provided by Keycloak are:
org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule::
This login module allows to authenticate with username/password from Keycloak database.
It's using <<_direct_access_grants,Direct Access Grants>> Keycloak endpoint to validate on Keycloak side if provided username/password is valid.
It's useful especially for non-web based systems, which need to rely on JAAS and want to use Keycloak credentials, but can't use classic browser based authentication flow due to their non-web nature.
Example of such application could be messaging application or SSH system.
org.keycloak.adapters.jaas.BearerTokenLoginModule::
This login module allows to authenticate with Keycloak access token passed to it through CallbackHandler as password.
It may be useful for example in case, when you have Keycloak access token from classic web based authentication flow and your web application then needs to talk to external non-web based system, which rely on JAAS.
For example to JMS/messaging system.
Both login modules have configuration property `keycloak-config-file` where you need to provide location of keycloak.json configuration file.
It could be either provided from filesystem or from classpath (in that case you may need value like `classpath:/folder-on-classpath/keycloak.json` ).
Second property `role-principal-class` allows to specify alternative class for Role principals attached to JAAS Subject.
Default value for Role principal is `org.keycloak.adapters.jaas.RolePrincipal` . Note that class should have constructor with single String argument.

363
topics/javascript-adapter.adoc Executable file
View file

@ -0,0 +1,363 @@
= Javascript Adapter
The Keycloak Server comes with a Javascript library you can use to secure HTML/Javascript applications.
This library is referenceable directly from the keycloak server.
You can also download the adapter from Keycloak's download site if you want a static copy.
It works in the same way as other application adapters except that your browser is driving the OAuth redirect protocol rather than the server.
The disadvantage of using this approach is that you have a non-confidential, public client.
This makes it more important that you register valid redirect URLs and make sure your domain name is secured.
To use this adapter, you must first configure an application (or client) through the `Keycloak Admin Console`.
You should select `public` for the `Access Type` field.
As public clients can't be verified with a client secret, you are required to configure one or more valid redirect uris.
Once you've configured the application, click on the `Installation` tab and download the `keycloak.json` file.
This file should be hosted on your web-server at the same root as your HTML pages.
Alternatively, you can manually configure the adapter and specify the URL for this file.
Next, you have to initialize the adapter in your application.
An example is shown below.
[source,html]
----
<head>
<script src="http://<keycloak server>/auth/js/keycloak.js"></script>
<script>
var keycloak = Keycloak();
keycloak.init().success(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).error(function() {
alert('failed to initialize');
});
</script>
</head>
----
To specify the location of the keycloak.json file:
[source]
----
var keycloak = Keycloak('http://localhost:8080/myapp/keycloak.json'));
----
Or finally to manually configure the adapter:
[source]
----
var keycloak = Keycloak({
url: 'http://keycloak-server/auth',
realm: 'myrealm',
clientId: 'myapp'
});
----
You can also pass `login-required` or `check-sso` to the init function.
Login required will cause a redirect to the login form on the server, while check-sso will simply redirect to the auth server to check if the user is already logged in to the realm.
For example:
[source]
----
keycloak.init({ onLoad: 'login-required' })
----
After you login, your application will be able to make REST calls using bearer token authentication.
Here's an example pulled from the `customer-portal-js` example that comes with the distribution.
[source]
----
<script>
var loadData = function () {
document.getElementById('username').innerText = keycloak.username;
var url = 'http://localhost:8080/database/customers';
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
var users = JSON.parse(req.responseText);
var html = '';
for (var i = 0; i < users.length; i++) {
html += '<p>' + users[i] + '</p>';
}
document.getElementById('customers').innerHTML = html;
console.log('finished loading data');
}
}
}
req.send();
};
var loadFailure = function () {
document.getElementById('customers').innerHTML = '<b>Failed to load data. Check console log</b>';
};
var reloadData = function () {
keycloak.updateToken().success(loadData).error(loadFailure);
}
</script>
<button onclick="reloadData()">Submit</button>
----
The `loadData()` method builds an HTTP request setting the `Authorization` header to a bearer token.
The `keycloak.token` points to the access token the browser obtained when it logged you in.
The `loadFailure()` method is invoked on a failure.
The `reloadData()` function calls `keycloak.updateToken()` passing in the `loadData()` and `loadFailure()` callbacks.
The `keycloak.updateToken()` method checks to see if the access token hasn't expired.
If it hasn't, and your oauth login returned a refresh token, this method will refresh the access token.
Finally, if successful, it will invoke the success callback, which in this case is the `loadData()` method.
To refresh the token when it is expired, call the `updateToken` method.
This method returns a promise object, which can be used to invoke a function on success or failure.
This method can be used to wrap functions that should only be called with a valid token.
For example, the following method will refresh the token if it expires within 30 seconds, and then invoke the specified function.
If the token is valid for more than 30 seconds it will just call the specified function.
[source]
----
keycloak.updateToken(30).success(function() {
// send request with valid token
}).error(function() {
alert('failed to refresh token');
);
----
== Session status iframe
By default, the JavaScript adapter creates a non-visible iframe that is used to detect if a single-sign out has occurred.
This does not require any network traffic, instead the status is retrieved from a special status cookie.
This feature can be disabled by setting `checkLoginIframe: false` in the options passed to the `init` method.
[[_javascript_implicit_flow]]
== Implicit and Hybrid Flow
By default, the JavaScript adapter uses http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[OpenID Connect standard (Authorization code) flow], which means that after authentication, the Keycloak server redirects the user back to your application, where the JavaScript adapter will exchange the `code` for an access token and a refresh token.
However, Keycloak also supports http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth[OpenID Connect Implicit flow] where an access token is sent immediately after successful authentication with Keycloak (there is no additional request for exchange code). This could have better performance than standard flow, as there is no additional request to exchange the code for tokens.
However, sending the access token in the URL fragment could pose a security issue in some environments (access logs might expose tokens located in the URL).
To enable implicit flow, you need to enable the `Implicit Flow Enabled` flag for the client in the Keycloak admin console.
You also need to pass the parameter `flow` with value `implicit` to `init` method.
An example is below:
[source]
----
keycloak.init({ flow: 'implicit' })
----
Note that with implicit flow, you are not given a refresh token after authentication.
This makes it harder for your application to periodically update the access token in background (without browser redirection). It's recommended that you implement an `onTokenExpired` callback method on the keycloak object, so you are notified after the token is expired (For example you can call keycloak.login, which will redirect browser to Keycloak login screen and it will immediately redirect you back if the SSO session is still valid and the user is still logged.
However, make sure to save the application state before performing a redirect.)
Keycloak also has support for http://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth[OpenID Connect Hybrid flow].
This requires the client to have both the `Standard Flow Enabled` and `Implicit Flow Enabled` flags enabled in the admin console.
The Keycloak server will then send both the code and tokens to your application.
The access token can be used immediately while the code can be exchanged for access and refresh tokens.
Similar to the implicit flow, the hybrid flow is good for performance because the access token is available immediately.
But, the token is still sent in the URL, and security risks might still apply.
However, one advantage over the implicit flow is that a refresh token is made available to the application (after the code-to-token request is finished).
For hybrid flow, you need to pass the parameter `flow` with value `hybrid` to `init` method.
== Older browsers
The JavaScript adapter depends on Base64 (window.btoa and window.atob) and HTML5 History API.
If you need to support browsers that don't provide those (for example IE9) you'll need to add polyfillers.
Example polyfill libraries:
* https://github.com/davidchambers/Base64.js
* https://github.com/devote/HTML5-History-API
== JavaScript Adapter reference
=== Constructor
[source]
----
new Keycloak();
new Keycloak('http://localhost/keycloak.json');
new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp' });
----
=== Properties
* authenticated - true if the user is authenticated
* Authorization
* tokenParsed - the parsed token
* subject - the user id
* idToken - the id token if claims is enabled for the application, null otherwise
* idTokenParsed - the parsed id token
* realmAccess - the realm roles associated with the token
* resourceAccess - the resource roles assocaited with the token
* refreshToken - the base64 encoded token that can be used to retrieve a new token
* refreshTokenParsed - the parsed refresh token
* timeSkew - estimated skew between local time and Keycloak server in seconds
* fragment
* Implicit flow
* flow
=== Methods
==== init(options)
Called to initialize the adapter.
Options is an Object, where:
* onLoad - specifies an action to do on load, can be either 'login-required' or 'check-sso'
* token - set an initial value for the token
* refreshToken - set an initial value for the refresh token
* idToken - set an initial value for the id token (only together with token or refreshToken)
* timeSkew - set an initial value for skew between local time and Keycloak server in seconds (only together with token or refreshToken)
* checkLoginIframe - set to enable/disable monitoring login state (default is true)
* checkLoginIframeInterval - set the interval to check login state (default is 5 seconds)
* query
+`fragment`
+`fragment`
+`query`
* standard
+`implicit`
+`hybrid`<<_javascript_implicit_flow,+Implicit flow>>
Returns promise to set functions to be invoked on success or error.
==== login(options)
Redirects to login form on (options is an optional object with redirectUri and/or prompt fields)
Options is an Object, where:
* redirectUri - specifies the uri to redirect to after login
* prompt - can be set to 'none' to check if the user is logged in already (if not logged in, a login form is not displayed)
* loginHint - used to pre-fill the username/email field on the login form
* action - if value is 'register' then user is redirected to registration page, otherwise to login page
* locale - specifies the desired locale for the UI
==== createLoginUrl(options)
Returns the url to login form on (options is an optional object with redirectUri and/or prompt fields)
Options is an Object, where:
* redirectUri - specifies the uri to redirect to after login
* prompt - can be set to 'none' to check if the user is logged in already (if not logged in, a login form is not displayed)
==== logout(options)
Redirects to logout
Options is an Object, where:
* redirectUri - specifies the uri to redirect to after logout
==== createLogoutUrl(options)
Returns logout out
Options is an Object, where:
* redirectUri - specifies the uri to redirect to after logout
==== register(options)
Redirects to registration form.
It's a shortcut for doing login with option action = 'register'
Options are same as login method but 'action' is overwritten to 'register'
==== createRegisterUrl(options)
Returns the url to registration page.
It's a shortcut for doing createRegisterUrl with option action = 'register'
Options are same as createLoginUrl method but 'action' is overwritten to 'register'
==== accountManagement()
Redirects to account management
==== createAccountUrl()
Returns the url to account management
==== hasRealmRole(role)
Returns true if the token has the given realm role
==== hasResourceRole(role, resource)
Returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used)
==== loadUserProfile()
Loads the users profile
Returns promise to set functions to be invoked on success or error.
==== isTokenExpired(minValidity)
Returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used)
==== updateToken(minValidity)
If the token expires within minValidity seconds (minValidity is optional, if not specified 0 is used) the token is refreshed.
If the session status iframe is enabled, the session status is also checked.
Returns promise to set functions that can be invoked if the token is still valid, or if the token is no longer valid.
For example:
[source]
----
keycloak.updateToken(5).success(function(refreshed) {
if (refreshed) {
alert('token was successfully refreshed');
} else {
alert('token is still valid');
}
}).error(function() {
alert('failed to refresh the token, or the session has expired');
});
----
==== clearToken()
Clear authentication state, including tokens.
This can be useful if application has detected the session has expired, for example if updating token fails.
Invoking this results in onAuthLogout callback listener being invoked.
[source]
----
keycloak.updateToken(5).error(function() {
keycloak.clearToken();
});
----
=== Callback Events
The adapter supports setting callback listeners for certain events.
For example:
[source]
----
keycloak.onAuthSuccess = function() { alert('authenticated'); }
----
* onReady(authenticated) - called when the adapter is initialized
* onAuthSuccess - called when a user is successfully authenticated
* onAuthError - called if there was an error during authentication
* onAuthRefreshSuccess - called when the token is refreshed
* onAuthRefreshError - called if there was an error while trying to refresh the token
* onAuthLogout - called if the user is logged out (will only be called if the session status iframe is enabled, or in Cordova mode)
* onTokenExpired - called when access token expired. When this happens you can for example refresh token, or if refresh not available (ie. with implicit flow) you can redirect to login screen

277
topics/jboss-adapter.adoc Executable file
View file

@ -0,0 +1,277 @@
[[_jboss_adapter]]
= JBoss/Wildfly Adapter
To be able to secure WAR apps deployed on JBoss AS 7.1.1, JBoss EAP 6.x, or Wildfly, you must install and configure the Keycloak Subsystem.
You then have two options to secure your WARs.
You can provide a keycloak config file in your WAR and change the auth-method to KEYCLOAK within web.xml.
Alternatively, you don't have to crack open your WARs at all and can apply Keycloak via the Keycloak Subsystem configuration in standalone.xml.
Both methods are described in this section.
[[_jboss_adapter_installation]]
== Adapter Installation
Adapters are no longer included with the appliance or war distribution.
Each adapter is a separate download on the Keycloak download site.
They are also available as a maven artifact.
Install on Wildfly 9 or 10:
[source]
----
$ cd $WILDFLY_HOME
$ unzip keycloak-wildfly-adapter-dist.zip
----
Install on Wildfly 8:
[source]
----
$ cd $WILDFLY_HOME
$ unzip keycloak-wf8-adapter-dist.zip
----
Install on JBoss EAP 6.x:
[source]
----
$ cd $JBOSS_HOME
$ unzip keycloak-eap6-adapter-dist.zip
----
Install on JBoss AS 7.1.1:
[source]
----
$ cd $JBOSS_HOME
$ unzip keycloak-as7-adapter-dist.zip
----
This zip file creates new JBoss Modules specific to the Wildfly Keycloak Adapter within your Wildfly distro.
After adding the Keycloak modules, you must then enable the Keycloak Subsystem within your app server's server configuration: `domain.xml` or `standalone.xml`.
There is a CLI script that will help you modify your server configuration.
Start the server and run the script from the server's bin directory:
[source]
----
$ cd $JBOSS_HOME/bin
$ jboss-cli.sh -c --file=adapter-install.cli
----
The script will add the extension, subsystem, and optional security-domain as described below.
For more recent versions of WildFly there's also a offline CLI script that can be used to install the adapter while the server is not running:
[source]
----
$ cd $JBOSS_HOME/bin
$ jboss-cli.sh -c --file=adapter-install-offline.cli
----
[source]
----
<server xmlns="urn:jboss:domain:1.4">
<extensions>
<extension module="org.keycloak.keycloak-adapter-subsystem"/>
...
</extensions>
<profile>
<subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
...
</profile>
----
The keycloak security domain should be used with EJBs and other components when you need the security context created in the secured web tier to be propagated to the EJBs (other EE component) you are invoking.
Otherwise this configuration is optional.
[source]
----
<server xmlns="urn:jboss:domain:1.4">
<subsystem xmlns="urn:jboss:domain:security:1.2">
<security-domains>
...
<security-domain name="keycloak">
<authentication>
<login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule"
flag="required"/>
</authentication>
</security-domain>
</security-domains>
----
For example, if you have a JAX-RS service that is an EJB within your WEB-INF/classes directory, you'll want to annotate it with the @SecurityDomain annotation as follows:
[source]
----
import org.jboss.ejb3.annotation.SecurityDomain;
import org.jboss.resteasy.annotations.cache.NoCache;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.util.ArrayList;
import java.util.List;
@Path("customers")
@Stateless
@SecurityDomain("keycloak")
public class CustomerService {
@EJB
CustomerDB db;
@GET
@Produces("application/json")
@NoCache
@RolesAllowed("db_user")
public List<String> getCustomers() {
return db.getCustomers();
}
}
----
We hope to improve our integration in the future so that you don't have to specify the @SecurityDomain annotation when you want to propagate a keycloak security context to the EJB tier.
== Required Per WAR Configuration
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
The first thing you must do is create a `keycloak.json` adapter config file within the `WEB-INF` directory of your WAR.
The format of this config file is describe in the <<_adapter_config,general adapter configuration>> section.
Next you must set the `auth-method` to `KEYCLOAK` in `web.xml`.
You also have to use standard servlet security to specify role-base constraints on your URLs.
Here's an example pulled from one of the examples that comes distributed with Keycloak.
[source]
----
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<module-name>customer-portal</module-name>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admins</web-resource-name>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Customers</web-resource-name>
<url-pattern>/customers/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>KEYCLOAK</auth-method>
<realm-name>this is ignored currently</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>
----
== Securing WARs via Keycloak Subsystem
You do not have to crack open a WAR to secure it with Keycloak.
Alternatively, you can externally secure it via the Keycloak Adapter Subsystem.
While you don't have to specify KEYCLOAK as an `auth-method`, you still have to define the `security-constraints` in `web.xml`.
You do not, however, have to create a `WEB-INF/keycloak.json` file.
This metadata is instead defined within XML in your server's `domain.xml` or `standalone.xml` subsystem configuration section.
[source]
----
<extensions>
<extension module="org.keycloak.keycloak-adapter-subsystem"/>
</extensions>
<profile>
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
<secure-deployment name="WAR MODULE NAME.war">
<realm>demo</realm>
<realm-public-key>MIGfMA0GCSqGSIb3DQEBAQUAA</realm-public-key>
<auth-server-url>http://localhost:8081/auth</auth-server-url>
<ssl-required>external</ssl-required>
<resource>customer-portal</resource>
<credential name="secret">password</credential>
</secure-deployment>
</subsystem>
</profile>
----
The `secure-deployment` `name` attribute identifies the WAR you want to secure.
Its value is the `module-name` defined in `web.xml` with `.war` appended.
The rest of the configuration corresponds pretty much one to one with the `keycloak.json` configuration options defined in <<_adapter_config,general adapter configuration>>.
The exception is the `credential` element.
To make it easier for you, you can go to the Keycloak Adminstration Console and go to the Application/Installation tab of the application this WAR is aligned with.
It provides an example XML file you can cut and paste.
There is an additional convenience format for this XML if you have multiple WARs you are deployment that are secured by the same domain.
This format allows you to define common configuration items in one place under the `realm` element.
[source]
----
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
<realm name="demo">
<realm-public-key>MIGfMA0GCSqGSIb3DQEBA</realm-public-key>
<auth-server-url>http://localhost:8080/auth</auth-server-url>
<ssl-required>external</ssl-required>
</realm>
<secure-deployment name="customer-portal.war">
<realm>demo</realm>
<resource>customer-portal</resource>
<credential name="secret">password</credential>
</secure-deployment>
<secure-deployment name="product-portal.war">
<realm>demo</realm>
<resource>product-portal</resource>
<credential name="secret">password</credential>
</secure-deployment>
<secure-deployment name="database.war">
<realm>demo</realm>
<resource>database-service</resource>
<bearer-only>true</bearer-only>
</secure-deployment>
</subsystem>
----

47
topics/jetty8-adapter.adoc Executable file
View file

@ -0,0 +1,47 @@
[[_jetty8_adapter]]
= Jetty 8.1.x Adapter
Keycloak has a separate adapter for Jetty 8.1.x that you will have to install into your Jetty installation.
You then have to provide some extra configuration in each WAR you deploy to Jetty.
Let's go over these steps.
[[_jetty8_adapter_installation]]
== Adapter Installation
Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on the Keycloak download site.
They are also available as a maven artifact.
You must unzip the Jetty 8.1.x distro into Jetty 8.1.x's root directory.
Including adapter's jars within your WEB-INF/lib directory will not work!
[source]
----
$ cd $JETTY_HOME
$ unzip keycloak-jetty81-adapter-dist.zip
----
Next, you will have to enable the keycloak option.
Edit start.ini and add keycloak to the options
[source]
----
#===========================================================
# Start classpath OPTIONS.
# These control what classes are on the classpath
# for a full listing do
# java -jar start.jar --list-options
#-----------------------------------------------------------
OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations,keycloak
----
== Required Per WAR Configuration
Enabling Keycloak for your WARs is the same as the Jetty 9.x adapter.
Our 8.1.x adapter supports both keycloak.json and the jboss-web.xml advanced configuration.
See <<_jetty9_per_war,Required Per WAR Configuration>>

148
topics/jetty9-adapter.adoc Executable file
View file

@ -0,0 +1,148 @@
[[_jetty9_adapter]]
= Jetty 9.x Adapters
Keycloak has a separate adapter for Jetty 9.1.x and Jetty 9.2.x that you will have to install into your Jetty installation.
You then have to provide some extra configuration in each WAR you deploy to Jetty.
Let's go over these steps.
[[_jetty9_adapter_installation]]
== Adapter Installation
Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on the Keycloak download site.
They are also available as a maven artifact.
You must unzip the Jetty 9.x distro into Jetty 9.x's root directory.
Including adapter's jars within your WEB-INF/lib directory will not work!
[source]
----
$ cd $JETTY_HOME
$ unzip keycloak-jetty92-adapter-dist.zip
----
Next, you will have to enable the keycloak module for your jetty.base.
[source]
----
$ cd your-base
$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
----
[[_jetty9_per_war]]
== Required Per WAR Configuration
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
The first thing you must do is create a `WEB-INF/jetty-web.xml` file in your WAR package.
This is a Jetty specific config file and you must define a Keycloak specific authenticator within it.
[source]
----
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Get name="securityHandler">
<Set name="authenticator">
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
</New>
</Set>
</Get>
</Configure>
----
Next you must create a `keycloak.json` adapter config file within the `WEB-INF` directory of your WAR.
The format of this config file is describe in the <<_adapter_config,general adapter configuration>> section.
WARNING: The Jetty 9.1.x adapter will not be able to find the `keycloak.json` file.
You will have to define all adapter settings within the `jetty-web.xml` file as described below.
Instead of using keycloak.json, you can define everything within the `jetty-web.xml`.
You'll just have to figure out how the json settings match to the `org.keycloak.representations.adapters.config.AdapterConfig` class.
[source]
----
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Get name="securityHandler">
<Set name="authenticator">
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
<Set name="adapterConfig">
<New class="org.keycloak.representations.adapters.config.AdapterConfig">
<Set name="realm">tomcat</Set>
<Set name="resource">customer-portal</Set>
<Set name="authServerUrl">http://localhost:8081/auth</Set>
<Set name="sslRequired">external</Set>
<Set name="credentials">
<Map>
<Entry>
<Item>secret</Item>
<Item>password</Item>
</Entry>
</Map>
</Set>
<Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4</Set>
</New>
</Set>
</New>
</Set>
</Get>
</Configure>
----
You do not have to crack open your WAR to secure it with keycloak.
Instead create the jetty-web.xml file in your webapps directory with the name of yourwar.xml.
Jetty should pick it up.
In this mode, you'll have to declare keycloak.json configuration directly within the xml file.
Finally you must specify both a `login-config` and use standard servlet security to specify role-base constraints on your URLs.
Here's an example:
[source]
----
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<module-name>customer-portal</module-name>
<security-constraint>
<web-resource-collection>
<web-resource-name>Customers</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>this is ignored currently</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>
----

6
topics/logout.adoc Executable file
View file

@ -0,0 +1,6 @@
= Logout
There are multiple ways you can logout from a web application.
For Java EE servlet containers, you can call HttpServletRequest.logout(). For any other browser application, you can point the browser at the url `http://auth-server/auth/realms/{realm-name}/tokens/logout?redirect_uri=encodedRedirectUri`.
This will log you out if you have an SSO session with your browser.

28
topics/multi-tenancy.adoc Executable file
View file

@ -0,0 +1,28 @@
= Multi Tenancy
Multi Tenancy, in our context, means that one single target application (WAR) can be secured by a single (or clustered) Keycloak server, authenticating its users against different realms.
In practice, this means that one application needs to use different `keycloak.json` files.
For this case, there are two possible solutions:
* The same WAR file deployed under two different names, each with its own Keycloak configuration (probably via the Keycloak Subsystem).
This scenario is suitable when the number of realms is known in advance or when there's a dynamic provision of application instances.
One example would be a service provider that dynamically creates servers/deployments for their clients, like a PaaS.
* client1.acme.com
+`client2.acme.com`
+`/app/client1/`
+`/app/client2/` This chapter of the reference guide focus on this second scenario.
Keycloak provides an extension point for applications that need to evaluate the realm on a request basis.
During the authentication and authorization phase of the incoming request, Keycloak queries the application via this extension point and expects the application to return a complete representation of the realm.
With this, Keycloak then proceeds the authentication and authorization process, accepting or refusing the request based on the incoming credentials and on the returned realm.
For this scenario, an application needs to:
* web.xml
+`keycloak.config.resolver`
+`org.keycloak.adapters.KeycloakConfigResolver`
* org.keycloak.adapters.KeycloakConfigResolver
+`resolve(org.keycloak.adapters.spi.HttpFacade.Request)`
+`org.keycloak.adapters.KeycloakDeployment`
An implementation of this feature can be found in the examples.

220
topics/oidc.adoc Executable file
View file

@ -0,0 +1,220 @@
[[_adapter_config]]
= Adapters
Keycloak can secure a wide variety of application types.
This section defines which application types are supported and how to configure and install them so that you can use Keycloak to secure your applications.
These client adapters use an extension of the OpenID Connect protocol (a derivate of OAuth 2.0). This extension provides support for clustering, backchannel logout, and other non-standard adminstrative functions.
The Keycloak project also provides a separate, standalone, generic, SAML client adapter.
But that is describe in a separate document and has a different download.
== General Adapter Config
Each adapter supported by Keycloak can be configured by a simple JSON text file.
This is what one might look like:
[source]
----
{
"realm" : "demo",
"resource" : "customer-portal",
"realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB",
"auth-server-url" : "https://localhost:8443/auth",
"ssl-required" : "external",
"use-resource-role-mappings" : false,
"enable-cors" : true,
"cors-max-age" : 1000,
"cors-allowed-methods" : "POST, PUT, DELETE, GET",
"bearer-only" : false,
"enable-basic-auth" : false,
"expose-token" : true,
"credentials" : {
"secret" : "234234-234234-234234"
},
"connection-pool-size" : 20,
"disable-trust-manager": false,
"allow-any-hostname" : false,
"truststore" : "path/to/truststore.jks",
"truststore-password" : "geheim",
"client-keystore" : "path/to/client-keystore.jks",
"client-keystore-password" : "geheim",
"client-key-password" : "geheim"
}
----
Some of these configuration switches may be adapter specific and some are common across all adapters.
For Java adapters you can use `${...}` enclosure as System property replacement.
For example `${jboss.server.config.dir}`.
Also, you can obtain a template for this config file from the admin console.
Go to the realm and select the application you want a template for.
Go to the `Installation` tab and this will provide you with a template that includes the public key of the realm.
Here is a description of each item:
realm::
Name of the realm representing the users of your distributed applications and services.
This is _REQUIRED._
resource::
Username of the application.
Each application has a username that is used when the application connects with the Keycloak server to turn an access code into an access token (part of the OAuth 2.0 protocol). This is _REQUIRED._
realm-public-key::
PEM format of public key.
You can obtain this from the administration console.
This is _REQUIRED._
auth-server-url::
The base URL of the Keycloak Server.
All other Keycloak pages and REST services are derived from this.
It is usually of the form `https://host:port/auth` This is _REQUIRED._
ssl-required::
Ensures that all communication to and from the Keycloak server from the adapter is over HTTPS.
This is _OPTIONAL_.
The default value is _external_ meaning that HTTPS is required by default for external requests.
Valid values are 'all', 'external' and 'none'.
use-resource-role-mappings::
If set to true, the adapter will look inside the token for application level role mappings for the user.
If false, it will look at the realm level for user role mappings.
This is _OPTIONAL_.
The default value is _false_.
public-client::
If set to true, the adapter will not send credentials for the client to Keycloak.
The default value is _false_.
enable-cors::
This enables CORS support.
It will handle CORS preflight requests.
It will also look into the access token to determine valid origins.
This is _OPTIONAL_.
The default value is _false_.
cors-max-age::
If CORS is enabled, this sets the value of the `Access-Control-Max-Age` header.
This is _OPTIONAL_.
If not set, this header is not returned in CORS responses.
cors-allowed-methods::
If CORS is enabled, this sets the value of the `Access-Control-Allow-Methods` header.
This should be a comma-separated string.
This is _OPTIONAL_.
If not set, this header is not returned in CORS responses.
cors-allowed-headers::
If CORS is enabled, this sets the value of the `Access-Control-Allow-Headers` header.
This should be a comma-separated string.
This is _OPTIONAL_.
If not set, this header is not returned in CORS responses.
bearer-only::
This tells the adapter to only do bearer token authentication.
That is, it will not do OAuth 2.0 redirects, but only accept bearer tokens through the `Authorization` header.
This is _OPTIONAL_.
The default value is _false_.
enable-basic-auth::
This tells the adapter to also support basic authentication.
If this option is enabled, then _secret_ must also be provided.
This is _OPTIONAL_.
The default value is _false_.
expose-token::
If `true`, an authenticated browser client (via a Javascript HTTP invocation) can obtain the signed access token via the URL `root/k_query_bearer_token`.
This is _OPTIONAL_.
The default value is _false_.
credentials::
Specify the credentials of the application.
This is an object notation where the key is the credential type and the value is the value of the credential type.
Currently only `password` is supported.
This is _REQUIRED_.
connection-pool-size::
Adapters will make separate HTTP invocations to the Keycloak Server to turn an access code into an access token.
This config option defines how many connections to the Keycloak Server should be pooled.
This is _OPTIONAL_.
The default value is `20`.
disable-trust-manager::
If the Keycloak Server requires HTTPS and this config option is set to `true` you do not have to specify a truststore.
While convenient, this setting is not recommended as you will not be verifying the host name of the Keycloak Server.
This is _OPTIONAL_.
The default value is `false`.
allow-any-hostname::
If the Keycloak Server requires HTTPS and this config option is set to `true` the Keycloak Server's certificate is validated via the truststore, but host name validation is not done.
This is not a recommended.
This seting may be useful in test environments This is _OPTIONAL_.
The default value is `false`.
truststore::
This setting is for Java adapters.
The value is the file path to a Java keystore file.
If you prefix the path with `classpath:`, then the truststore will be obtained from the deployment's classpath instead.
Used for outgoing HTTPS communications to the Keycloak server.
Client making HTTPS requests need a way to verify the host of the server they are talking to.
This is what the trustore does.
The keystore contains one or more trusted host certificates or certificate authorities.
You can create this truststore by extracting the public certificate of the Keycloak server's SSL keystore.
This is _OPTIONAL_ if `ssl-required` is `none` or `disable-trust-manager` is `true`.
truststore-password::
Password for the truststore keystore.
This is _REQUIRED_ if `truststore` is set.
client-keystore::
_Not supported yet, but we will support in future versions._ This setting is for Java adapters.
This is the file path to a Java keystore file.
This keystore contains client certificate for two-way SSL when the adapter makes HTTPS requests to the Keycloak server.
This is _OPTIONAL_.
client-keystore-password::
_Not supported yet, but we will support in future versions._ Password for the client keystore.
This is _REQUIRED_ if `client-keystore` is set.
client-key-password::
_Not supported yet, but we will support in future versions._ Password for the client's key.
This is _REQUIRED_ if `client-keystore` is set.
auth-server-url-for-backend-requests::
Alternative location of auth-server-url used just for backend requests.
It must be absolute URI.
Useful especially in cluster (see <<_relative_uri_optimization,Relative URI Optimization>>) or if you would like to use _https_ for browser requests but stick with _http_ for backend requests etc.
always-refresh-token::
If _true_, Keycloak will refresh token in every request.
More info in <<_refresh_token_each_req,Refresh token in each request>> .
register-node-at-startup::
If _true_, then adapter will send registration request to Keycloak.
It's _false_ by default and useful just in cluster (See <<_registration_app_nodes,Registration of application nodes to Keycloak>>)
register-node-period::
Period for re-registration adapter to Keycloak.
Useful in cluster.
See <<_registration_app_nodes,Registration of application nodes to Keycloak>> for details.
token-store::
Possible values are _session_ and _cookie_.
Default is _session_, which means that adapter stores account info in HTTP Session.
Alternative _cookie_ means storage of info in cookie.
See <<_stateless_token_store,Stateless token store>> for details.
principal-attribute::
OpenID Connection ID Token attribute to populate the UserPrincipal name with.
If token attribute is null, defaults to `sub`.
Possible values are `sub`, `preferred_username`, `email`, `name`, `nickname`, `given_name`, `family_name`.
turn-off-change-session-id-on-login::
The session id is changed by default on a successful login on some platforms to plug a security attack vector (Tomcat 8, Jetty9, Undertow/Wildfly). Change this to true if you want to turn this off This is _OPTIONAL_.
The default value is _false_.

20
topics/preface.adoc Executable file
View file

@ -0,0 +1,20 @@
= 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.
So:
[source]
----
Let's pretend to have an extremely \
long line that \
does not fit
This one is short
----
Is really:
[source]
----
Let's pretend to have an extremely long line that does not fit
This one is short
----

67
topics/spring-boot-adapter.adoc Executable file
View file

@ -0,0 +1,67 @@
= Spring Boot Adapter
To be able to secure Spring Boot apps you must add the Keycloak Spring Boot adapter JAR to your app.
You then have to provide some extra configuration via normal Spring Boot configuration (`application.properties`). Let's go over these steps.
[[_spring_boot_adapter_installation]]
== Adapter Installation
The Keycloak Spring Boot adapter takes advantage of Spring Boot's autoconfiguration so all you need to do is add the Keycloak Spring Boot adapter JAR to your project.
Depending on what container you are using with Spring Boot, you also need to add the appropriate Keycloak container adapter.
If you are using Maven, add the following to your pom.xml (using Tomcat as an example):
[source]
----
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-adapter</artifactId>
<version>&project.version;</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat8-adapter</artifactId>
<version>&project.version;</version>
</dependency>
----
[[_spring_boot_adapter_configuration]]
== Required Spring Boot Adapter Configuration
This section describes how to configure your Spring Boot app to use Keycloak.
Instead of a `keycloak.json` file, you configure the realm for the Spring Boot Keycloak adapter via the normal Spring Boot configuration.
For example:
[source]
----
keycloak.realm = demorealm
keycloak.realmKey = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLCWYuxXmsmfV+Xc9Ik8QET8lD4wuHrJAXbbutS2O/eMjQQLNK7QDX/k/XhOkhxP0YBEypqeXeGaeQJjCxDhFjJXQuewUEMlmSja3IpoJ9/hFn4Cns4m7NGO+rtvnfnwgVfsEOS5EmZhRddp+40KBPPJfTH6Vgu6KjQwuFPj6DTwIDAQAB
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.ssl-required = external
keycloak.resource = demoapp
keycloak.credentials.secret = 11111111-1111-1111-1111-111111111111
keycloak.use-resource-role-mappings = true
----
You also need to specify the J2EE security config that would normally go in the `web.xml`.
Here's an example configuration:
[source]
----
keycloak.securityConstraints[0].securityCollections[0].name = insecure stuff
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = admin
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = user
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /insecure
keycloak.securityConstraints[0].securityCollections[1].name = admin stuff
keycloak.securityConstraints[0].securityCollections[1].authRoles[0] = admin
keycloak.securityConstraints[0].securityCollections[1].patterns[0] = /admin
----

View file

@ -0,0 +1,264 @@
= Spring Security Adapter
To secure an application with Spring Security and Keycloak, add this adapter as a dependency to your project.
You then have to provide some extra beans in your Spring Security configuration file and add the Keycloak security filter to your pipeline.
Unlike the other Keycloak Adapters, you should not configure your security in web.xml.
However, keycloak.json is still required.
== Adapter Installation
Add Keycloak Spring Security adapter as a dependency to your Maven POM or Gradle build.
[source]
----
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
<version>&project.version;</version>
</dependency>
----
== Spring Security Configuration
The Keycloak Spring Security adapter takes advantage of Spring Security's flexible security configuration syntax.
=== Java Configuration
Keycloak provides a KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating a http://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/web/WebSecurityConfigurer.html[WebSecurityConfigurer] instance.
The implementation allows customization by overriding methods.
While its use is not required, it greatly simplifies your security context configuration.
[source]
----
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
/**
* Defines the session authentication strategy.
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.authorizeRequests()
.antMatchers("/customers*").hasRole("USER")
.antMatchers("/admin*").hasRole("ADMIN")
.anyRequest().permitAll();
}
}
----
You must provide a session authentication strategy bean which should be of type `RegisterSessionAuthenticationStrategy` for public or confidential applications and `NullAuthenticatedSessionStrategy` for bearer-only applications.
Spring Security's `SessionFixationProtectionStrategy` is currently not supported because it changes the session identifier after login via Keycloak.
If the session identifier changes, universal log out will not work because Keycloak is unaware of the new session identifier.
=== XML Configuration
While Spring Security's XML namespace simplifies configuration, customizing the configuration can be a bit verbose.
[source]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<context:component-scan base-package="org.keycloak.adapters.springsecurity" />
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="keycloakAuthenticationProvider" />
</security:authentication-manager>
<bean id="adapterDeploymentContext" class="org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean">
<constructor-arg value="/WEB-INF/keycloak.json" />
</bean>
<bean id="keycloakAuthenticationEntryPoint" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint" />
<bean id="keycloakAuthenticationProvider" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider" />
<bean id="keycloakPreAuthActionsFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter" />
<bean id="keycloakAuthenticationProcessingFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter">
<constructor-arg name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="keycloakLogoutHandler" class="org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler">
<constructor-arg ref="adapterDeploymentContext" />
</bean>
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg name="logoutSuccessUrl" value="/" />
<constructor-arg name="handlers">
<list>
<ref bean="keycloakLogoutHandler" />
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</list>
</constructor-arg>
<property name="logoutRequestMatcher">
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg name="pattern" value="/sso/logout**" />
<constructor-arg name="httpMethod" value="GET" />
</bean>
</property>
</bean>
<security:http auto-config="false" entry-point-ref="keycloakAuthenticationEntryPoint">
<security:custom-filter ref="keycloakPreAuthActionsFilter" before="LOGOUT_FILTER" />
<security:custom-filter ref="keycloakAuthenticationProcessingFilter" before="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/customers**" access="ROLE_USER" />
<security:intercept-url pattern="/admin**" access="ROLE_ADMIN" />
<security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER" />
</security:http>
</beans>
----
== Multi Tenancy
The Keycloak Spring Security adapter also supports multi tenancy.
Instead of injecting `AdapterDeploymentContextFactoryBean` with the path to `keycloak.json` you can inject an implementation of the `KeycloakConfigResolver` interface.
More details on how to implement the `KeycloakConfigResolver` can be found in <<_multi_tenancy>>.
== Naming Security Roles
Spring Security, when using role-based authentication, requires that role names start with `ROLE_`.
For example, an administrator role must be declared in Keycloak as `ROLE_ADMIN` or similar, not simply `ADMIN`.
The class `org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider` supports an optional `org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper` which can be used to map roles coming from Keycloak to roles recognized by Spring Security.
Use, for example, `org.springframework.security.core.authority.mapping.SimpleAuthorityMapper` to insert the `ROLE_` prefix and convert the role name to upper case.
The class is part of Spring Security Core module.
== Client to Client Support
To simplify communication between clients, Keycloak provides an extension of Spring's `RestTemplate` that handles bearer token authentication for you.
To enable this feature your security configuration must add the `KeycloakRestTemplate` bean.
Note that it must be scoped as a prototype to function correctly.
For Java configuration:
[source]
----
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
...
@Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
...
}
----
For XML configuration:
[source]
----
<bean id="keycloakRestTemplate" class="org.keycloak.adapters.springsecurity.client.KeycloakRestTemplate" scope="prototype">
<constructor-arg name="factory" ref="keycloakClientRequestFactory" />
</bean>
----
Your application code can then use `KeycloakRestTemplate` any time it needs to make a call to another client.
For example:
[source]
----
@Service
public class RemoteProductService implements ProductService {
@Autowired
private KeycloakRestTemplate template;
private String endpoint;
@Override
public List<String> getProducts() {
ResponseEntity<String[]> response = template.getForEntity(endpoint, String[].class);
return Arrays.asList(response.getBody());
}
}
----
== Spring Boot Configuration
Spring Boot attempts to eagerly register filter beans with the web application context.
Therefore, when running the Keycloak Spring Security adapter in a Spring Boot environment, it may be necessary to add two ``FilterRegistrationBean``s to your security configuration to prevent the Keycloak filters from being registered twice.
[source]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
...
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
...
}
----

87
topics/tomcat-adapter.adoc Executable file
View file

@ -0,0 +1,87 @@
[[_tomcat_adapter]]
= Tomcat 6, 7 and 8 Adapters
To be able to secure WAR apps deployed on Tomcat 6, 7 and 8 you must install the Keycloak Tomcat 6, 7 or 8 adapter into your Tomcat installation.
You then have to provide some extra configuration in each WAR you deploy to Tomcat.
Let's go over these steps.
[[_tomcat_adapter_installation]]
== Adapter Installation
Adapters are no longer included with the appliance or war distribution.
Each adapter is a separate download on the Keycloak download site.
They are also available as a maven artifact.
You must unzip the adapter distro into Tomcat's `lib/` directory.
Including adapter's jars within your WEB-INF/lib directory will not work! The Keycloak adapter is implemented as a Valve and valve code must reside in Tomcat's main lib/ directory.
[source]
----
$ cd $TOMCAT_HOME/lib
$ unzip keycloak-tomcat6-adapter-dist.zip
or
$ unzip keycloak-tomcat7-adapter-dist.zip
or
$ unzip keycloak-tomcat8-adapter-dist.zip
----
== Required Per WAR Configuration
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
The first thing you must do is create a `META-INF/context.xml` file in your WAR package.
This is a Tomcat specific config file and you must define a Keycloak specific Valve.
[source]
----
<Context path="/your-context-path">
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>
----
Next you must create a `keycloak.json` adapter config file within the `WEB-INF` directory of your WAR.
The format of this config file is describe in the <<_adapter_config,general adapter configuration>> section.
Finally you must specify both a `login-config` and use standard servlet security to specify role-base constraints on your URLs.
Here's an example:
[source]
----
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<module-name>customer-portal</module-name>
<security-constraint>
<web-resource-collection>
<web-resource-name>Customers</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>this is ignored currently</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>
----