JBoss.orgCommunity Documentation

Chapter 7. Adapters

7.1. General Adapter Config
7.2. JBoss/Wildfly Adapter
7.2.1. Adapter Installation
7.2.2. Per WAR Configuration
7.2.3. Securing WARs via Keycloak Subsystem
7.3. Pure Client Javascript Adapter
7.3.1. Session status iframe
7.3.2. JavaScript Adapter reference
7.4. Installed Applications
7.4.1. http://localhost
7.4.2. urn:ietf:wg:oauth:2.0:oob

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-not-required" : false,
  "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:

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-not-required

Ensures that all communication to and from the Keycloak server from the adapter is over HTTPS. This is OPTIONAL. The default value is false meaning that HTTPS is required by default.

user-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.

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 JSON list of strings. 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.

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 if 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. 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-not-required is false or disable-trust-manager is true. The default value isfalse.

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.

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-not-required>true</ssl-not-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-not-required>true</ssl-not-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');
);

Keycloak provides two special redirect uris for installed applications.