JBoss.orgCommunity Documentation
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.
Each adapter supported by Keycloak can be configured by a simple JSON text file. This is what one might look like:
{ "realm" : "demo", "resource" : "customer-portal", "realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB", "auth-server-url" : "https://localhost:8443/auth", "ssl-required" : "external", "user-resource-role-mappings" : false, "enable-cors" : true, "cors-max-age" : 1000, "cors-allowed-methods" : [ "POST", "PUT", "DELETE", "GET" ], "bearer-only" : 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 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:
Name of the realm representing the users of your distributed applications and services. This is REQUIRED.
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.
PEM format of public key. You can obtain this from the administration console. This is REQUIRED.
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.
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'.
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.
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.
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.
If CORS is enabled, this sets the value of the
Access-Control-Allow-Methods
header. This should be a JSON list of strings.
This is OPTIONAL. If not set, this header is not returned in CORS
responses.
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.
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.
Specify the credentials of the application. This is an object notation where the key
is the credential type and the value if the value of the credential type. Currently only
password
is supported.
This is REQUIRED.
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
.
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
.
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
.
This setting is for Java adapters. This is the file path to a Java keystore file.
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
. The default value isfalse.
Password for the truststore keystore.
This is
REQUIRED
if
truststore
is set.
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.
Not supported yet, but we will support in future versions.
Password for the client keystore.
This is
REQUIRED
if
client-keystore
is set.
Not supported yet, but we will support in future versions.
Password for the client's key.
This is
REQUIRED
if
client-keystore
is set.
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.
This is a adapter zip file for AS7, EAP, and Wildfly in the adapters/
directory in the Keycloak
distribution.
Install on Wildfly:
$ cd $WILDFLY_HOME $ unzip keycloak-wildfly-adapter-dist.zip
Install on JBoss EAP 6.x:
$ cd $JBOSS_HOME $ unzip keycloak-eap6-adapter-dist.zip
Install on JBoss AS 7.1.1:
$ 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
.
For Wildfly:
<server xmlns="urn:jboss:domain:1.4"> <extensions> <extension module="org.keycloak.keycloak-wildfly-subsystem"/> ... </extensions> <profile> <subsystem xmlns="urn:jboss:domain:keycloak:1.0"/> ... </profile>
For JBoss AS 7.1.1 and EAP 6.x:
<server xmlns="urn:jboss:domain:1.4"> <extensions> <extension module="org.keycloak.keycloak-as7-subsystem"/> ... </extensions> <profile> <subsystem xmlns="urn:jboss:domain:keycloak:1.0"/> ... </profile>
Finally, for both AS7, EAP 6.x, and Wildfly installations you must specify a shared keycloak security domain. This 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.
<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:
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.
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 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.
<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> </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> </security-constraint> <security-constraint> <web-resource-collection> <url-pattern>/*</url-pattern> </web-resource-collection> <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>
You do not have to crack open a WAR to secure it with Keycloak. Alternatively, you can externally secure
it via the Keycloak 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.
<server xmlns="urn:jboss:domain:1.4"> <profile> <subsystem xmlns="urn:jboss:domain:keycloak:1.0"> <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 security-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 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.
<subsystem xmlns="urn:jboss:domain:keycloak:1.0"> <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>
The Keycloak Server comes with a Javascript library you can use to secure pure HTML/Javascript applications. 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 end up having a non-confidential, public client. This can be mitigated by registering valid redirect URLs. You are still vulnerable if somebody hijacks the IP/DNS name of your pure HTML/Javascript application though.
To use this adapter, you must first configure an application (or client) through the Keycloak Admin Console
.
You should select public
for the Client Type
field. As public clients can't
be verified with a client secret you are required to configure one or more valid redirect uris as well.
Once you've configured the application click on the Installation
tab and download the keycloak.json
file. This file should be hosted in your web-server at the same root as your HTML pages. Alternatively you can either
specify the URL for this file, or manually configure the adapter.
Next you have to initialize the adapter in your application. An example on how to do this is shown below.
<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:
var keycloak = Keycloak('http://localhost:8080/myapp/keycloak.json'));
Or finally to manually configure the adapter:
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 redirect to the login form on the server, while check-sso will redirect to the auth server to check
if the user is already logged in to the realm. For example:
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.
<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="loadData()">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.onValidAccessToken()
passing in the loadData()
and
loadFailure()
callbacks. The keycloak.onValidAcessToken()
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 if it's 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.
keycloak.updateToken(30).success(function() { // send request with valid token }).error(function() { alert('failed to refresh token'); );
By default the JavaScript adapter creates a non-visible iframe that is used to detect if a single-sign out has occured.
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.
new Keycloak(); new Keycloak('http://localhost/keycloak.json'); new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp' });
Authorization
header in requests to servicesCalled to initialize the adapter.
Options is an Object, where:
Returns promise to set functions to be invoked on success or error.
Redirects to login form on (options is an optional object with redirectUri and/or prompt fields)
Options is an Object, where:
Returns the url to login form on (options is an optional object with redirectUri and/or prompt fields)
Options is an Object, where:
Redirects to logout
Options is an Object, where:
Returns logout out
Options is an Object, where:
Returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used)
Loads the users profile
Returns promise to set functions to be invoked on success or error.
Returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used)
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:
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'); });
The adapter supports setting callback listeners for certain events. For example:
keycloak.onAuthSuccess = function() { alert('authenticated'); }
Keycloak provides two special redirect uris for installed applications.
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
.
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.
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.