HAL - SSO with Keycloak

Version 4

    Overview

    This document describes how to add SSO support in HAL using Keycloak and Elytron. The focus is on the client side and what it takes to support SSO in HAL. It’s based on an PoC by Pedro Igor (psilva).

     

    Server Side

    SSO is enabled by Keycloak and the OpenID Connect Protocol. To use SSO both WildFly and Keycloak need to be configured accordingly.

     

    Keycloak

    In Keycloak there has to be a realm with two clients:

    • a client representing the management interface
    • a client representing HAL

     

    The client representing the management interface provides all the necessary configuration to protect the management interface and, specially, the Domain API. In OpenID Connect terminology, the management interface will be acting as a resource server whose resources are protected by a bearer token. In this case, an access token that must be sent along with every single request to the Domain API in order to authorize access to its resources. In Keycloak, the configuration for this client must respect the following restrictions:

    • the client must be a confidential client
    • the client must be configured to only accept bearer tokens

     

    The client representing HAL provides all the necessary configuration to allow HAL to authenticate against a Keycloak Server and to obtain the necessary tokens in order to invoke the Domain API. In OpenID Connect terminology, HAL is a public client responsible for obtaining tokens from an Authorization Server (e.g.: Keycloak) and use these tokens to access the resources protected by a resource server (e.g.: Domain API). In Keycloak, the configuration for this client must respect the following restrictions:

    • the client must be a public client
    • the client must have a valid Redirect URI in order to be able to redirect users to Keycloak for authentication as well receive authentication responses from a Keycloak Server after a successful authentication
    • the client must have CORS configured in order to invoke Keycloak endpoints during the authentication process

     

    WildFly

    In WildFly the Keycloak and Elytron subsystems need to be enabled and configured. In Keycloak Subsystem, it is necessary to define a configuration similar to the following:

     

    <subsystem xmlns="urn:jboss:domain:keycloak:1.1">
        <realm name="wildfly">
            <realm-public-key>${REALM_PUBLIC_KEY}</realm-public-key>
            <auth-server-url>http://localhost:8080/auth</auth-server-url>
            <ssl-required>none</ssl-required>
        </realm>
        <secure-deployment name="wildfly-management">
            <realm>wildfly</realm>
            <resource>wildfly-management</resource>
            <bearer-only>true</bearer-only>
        </secure-deployment>
        <secure-server name="wildfly-console">
            <realm>wildfly</realm>
            <resource>wildfly-console</resource>
            <public-client>true</public-client>             
        </secure-server>
    </subsystem>
    

     

    The configuration above defines a realm and two secure-deployment resources. The wildfly realm defines some relevant information about an existing realm in Keycloak, where the two secure-deployment resources provide the configuration for the clients in Keycloak representing both management interface and HAL.

     

    As you may notice, the wildfly-console configuration above is using the publish-to-management-interface switch to expose the configuration through the management interface. When enabled, the configuration can be accessed from the following endpoint:

     

    https://{host}:{jboss.management.http.port:9990}/keycloak/adapter/wildfly-console

     

    HAL relies on this endpoint to obtain the Keycloak adapter configuration and be able to connect with a Keycloak Server.

     

    In Elytron Subsystem, it is necessary to define a configuration similar to the following:

     

    <subsystem xmlns="urn:wildfly:elytron:1.0">
        <security-domains> 
            <security-domain name="KeycloakSecurityDomain" default-realm="JwtRealm" permission-mapper="login-permission-mapper" role-mapper="combined-role-mapper">
                       <realm name="JwtRealm" role-decoder="keycloak-role-decoder"/>
                   </security-domain>
               </security-domains>
               <security-realms>
            <token-realm name="JwtRealm" principal-claim="preferred_username">
               <jwt/>
           </token-realm>
       </security-realms>
       <http>                
           <http-authentication-factory name="keycloak-management-http-authentication" http-server-mechanism-factory="wildfly-management" security-domain="KeycloakSecurityDomain">
               <mechanism-configuration>
                   <mechanism mechanism-name="KEYCLOAK"/>
               </mechanism-configuration>
           </http-authentication-factory>
           <aggregate-http-server-mechanism-factory name="global">
               <http-server-mechanism-factory name="default"/>
               <http-server-mechanism-factory name="keycloak"/>
           </aggregate-http-server-mechanism-factory>
           <provider-http-server-mechanism-factory name="default"/>
           <service-loader-http-server-mechanism-factory name="keycloak" module="org.keycloak.keycloak-wildfly-elytron-oidc-adapter"/>
       </http>            
    </subsystem>
    

    The configuration above defines three main things:

    • A KeycloakSecurityDomain security-domain with a single JwtRealm that is able to perform authentication based on JWT tokens.
    • A keycloak-management-http-authentication http-authentication-factory that references the wildfly-management security-deployment previously defined in Keycloak Subsystem with the configuration we need to protect the management interface.

     

    For last, in order to actually protect the management interface with Keycloak, it is necessary to change the management-interfaces/http-interface as follows:

     

    <management-interfaces>
        <http-interface http-authentication-factory="keycloak-management-http-authentication">
            <http-upgrade enabled="true"/>
           <socket-binding http="management-http"/>
        </http-interface>
    </management-interfaces>
    

     

    In the configuration above, we are using the keycloak-management-http-authentication http-authentication-factory defined in Elytron Subsystem to actually protect the management interface.

     

    SSO configuration in Wildfly using Keycloak is solely based on specific capabilities exposed by both Keycloak and Elytron subsystems. From Keycloak Subsystem, each secure-deployment resource definition exposes a org.wildfly.security.http-server-mechanism-factory capability, which in turn is used by Elytron Subsystem to expose a org.wildfly.security.http-authentication-factory capability to actually protect the management interface.

     

    Client Side

    SSO is an alternative to the current authentication mechanism (digest authentication). It's important that both mechanisms work side by side. Furthermore HAL can be run standalone and connect to any management endpoint. This needs to work also when using SSO.

     

    Implementation

    When the console starts we need to check how the console was loaded and whether SSO is available or not:

    1. Ping /management
      1. status == 404: HAL was launched as a standalone console, the user needs to select a management endpoint.
      2. status != 404: HAL is part of a WildFly instance.
    2. Ping /keycloak/adapter/wildfly-console
      1. status == 404: No SSO configured. Use digest authentication
      2. status != 404: SSO is available. Execute the following steps:
        1. Parse the response as JSON and read the aut server URL
        2. Load and run /js/keycloak.js form the auth server
        3. Execute Keycloak JavaScript initialization code
    3. Execute remaining bootstrap code (read operation mode, current user, …)
    4. Load the console

     

    Implications

    When HAL uses SSO the UI needs to reflect that:

    • The user, group and role management is done in Keycloak. The UI in HAL should be disabled.
    • The header should contain a link to the user self management in Keycloak and a link to the user management (if role == Administrator || SuperUser)
    • The logout link in the header links to Keycloak

     

    Notes

    The initial ping to /management (see above) does not trigger a redirection to the Keycloak login page. HAL is going to receive a HTTP response as follows:

     

    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Bearer realm="wildfly"
    

     

    The endpoint /keycloak/adapter/wildfly-console is fixed as HAL relies on it to obtain the Keycloak config from the server.

     

    Recommend use of TLS/SSL when accessing the console and management interface

    Both clients for HAL and management interface must belong to the same realm in Keycloak

    RBAC support is also transparent for HAL. RBAC works in the same way as before, but roles are now obtained from Keycloak

     

    Resources

    Client side setup: https://gist.github.com/pedroigor/ae3af320b2a94c4e3d90e07b65002d10

    Prepared distribution containing Keycloak and WildFly: https://drive.google.com/open?id=0BwjsrPoH8khWWHl3TVlzdnBjNjQ

     

    Issue Metadata

    EAP7-288: https://issues.jboss.org/browse/EAP7-288