Tuesday, 26 September 2017

WildFly Elytron - Add Kerberos Authentication To Existing Web Application

When using WildFly Elytron to secure a web application it is possible to customise the authentication policy without modifying the web application, this is something that could be very useful as an application transitions though different environments such as development, testing, staging, and production - in each of these environments a custom authentication policy can be used without requiring a custom build of the web application.

This blog post is going to take a simple web application secured by HTTP BASIC authentication and convert it to use WildFly Elytron using SPNEGO authentication with fallback to BASIC without repackaging the application.

Example Application

This blog post have been written using the following example web application: -


This application contains a single welcome page with a link to a secured servlet that requires the caller have the role 'Users', the resulting deployment contains a web.xml that specifies authentication should use HTTP BASIC authentication.

The example application contains no JBoss or WidFly specific descriptors or annotations and instead depends on the default configuration within the Undertow subsystem using the 'other' security domain which pulls the user information from the application-users.properties and application-roles.properties.

Test User

Within my test Kerberos environment the main user I test with is 'testuser', before proceeding to configure the server I will ensure there is an entry for this user in the properties files with the following command: -

  ./add-user.sh -a -u testuser -p password -g Users

Configuring the Server

These instructions are written using a recent build of WildFly 11 however it should be possible to use them against either the CR1 release or once available the Final release.

Although they are not in use by default the default server configuration of WildFly 11 contains many Elytron components already configured to work with the legacy security properties, the steps here will make use of those as much as is possible and just define new components to achieve SPNEGO authentication as well.

The next set of commands are all executed within the JBoss CLI.

When working with Kerberos in a test environment I tend to find it easier to define the realm and KDC information as system properties: -


The next command is to define the server's Kerberos identity.

./subsystem=elytron/kerberos-security-factory=test-server:add( \
    path=/home/darranl/src/kerberos/test-server.keytab, \
    principal=HTTP/test-server.elytron.org@ELYTRON.ORG, \

The existing 'ApplicationDomain' and the realm it already references are going to be used.

SPNEGO authentication by default results in an identities name containing the Kerberos realm as a suffix so will now define a principal transformer to strip the realm.

 ./subsystem=elytron/regex-principal-transformer= \
    realm-stripper:add( \
    pattern="@ELYTRON.ORG", replacement="")

The final step within the Elytron subsystem is to define a HTTP authentication factory that supports both SPNEGO and BASIC authentication.

./subsystem=elytron/http-authentication-factory=spnego-http-authentication:add(security-domain=ApplicationDomain, \
    http-server-mechanism-factory=global, \
    mechanism-configurations=[ \
    {mechanism-name=SPNEGO, \
    credential-security-factory=test-server, \
    pre-realm-principal-transformer=realm-stripper}, \
    {mechanism-name=BASIC} \

The ordering of the mechanism names was important here as we want SPNEGO to be the preferred authentication mechanism with fallback to BASIC if the web browser can not respond. 

For this specific example we will be overriding the mechanisms defined within the web.xml so it is important to only list the mechanisms we want to actually use.

The final configuration step is within the Undertow susbystem to configure any deployments that use the 'other' security domain to instead use the HTTP authentication factory defined here and override any defined authentication mechanisms.

/subsystem=undertow/application-security-domain=other:add( \
    http-authentication-factory=spnego-http-authentication, \

Deploy Example Application

If the web application has already been deployed the server should be reloaded / restarted for the new security policy to apply to it, alternatively it can be deployed using the WildFly Maven plug-in.

mvn wildfly:deploy

Access Web Application

For my Kerberos test environment the server is test-server.elytron.org so I can access the web application through the following URL: -


At this point if I access the web application after obtaining a Kerberos ticket for 'testuser' and subsequently click on the 'Access Secured Servlet' link SPNEGO authentication will silently take place for me as I have already configured my web browser to enable SPNEGO authentication.

If I don't have a local Kerberos ticket or if my web browser does not have SPNEGO authentication enabled I will receive the BASIC authentication prompt and will be able to use the username and password I defined at the start of this blog post.

One point to keep in mind, SPNEGO authentication is cached by default against the HTTP session - if switching from having a ticket to not having a ticket restart either the browser or the server.

Friday, 8 September 2017

Using WildFly Elytron with Undertow Standalone

The WildFly Elytron project has been developed to provide the security requirements of the WildFly application server, however the development of WildFly Elytron has produced a security framework which does not depend on the application and can be used outside of the application server.

This means that it is possible to use WildFly Elytron in situations outside of the application server, this blog post demonstrates how WildFly Elytron can be used to secure an embedded Undertow server.

If you visit the main page for Undertow towards the bottom of the page is a code example showing how to start a simple HelloWorld server using Async IO.


This blog post goes one step further and illustrates how WildFly Elytron can be added to the example to secure access to the HttpHandler using HTTP Basic authentication.

The components demonstrated in this example are the same components we use within the application server, the difference being that they are programatically configured and wired together rather than using the subsystem for configuration.

The code for the example can be found in the project called 'undertow-standalone' in the Git repository https://github.com/wildfly-security-incubator/elytron-examples.


The example project has the following key dependencies although these will pull in some additional dependencies.

This is the main dependency on Undertow, this example is only demonstrating with the core Undertow APIs so the servlet dependency is not required.

This is the dependency on the WildFly Elytron security framework.

The Undertow project does not have a dependency on WildFly Elytron and the Elytron project does not have a dependency on Undertow, the Elytron Web project acts as an intermediate project to join the two together.

In future releases of Elytron Web we may also be able to look into integration with other servers following a similar pattern to how we have integrated with Undertow.


The initial example is very similar to the example on undertow.io, the main difference being the call to a method 'wrap' to wrap the HttpHandler.

    public static void main(String[] args) throws Exception {
        final SecurityDomain securityDomain = createSecurityDomain();

        Undertow server = Undertow.builder()
                .addHttpListener(8080, "localhost")
                .setHandler(wrap(new HttpHandler() {

          public void handleRequest(HttpServerExchange exchange) 
               throws Exception {
  put(Headers.CONTENT_TYPE, "text/plain");                       
  send("Hello " + securityDomain.getCurrentSecurityIdentity().getPrincipal().getName());
                 }, securityDomain)).build();

The first thing to happen during wrapping is an Elytron security domain is assembled, this domain contains a single user 'elytron' with a password of 'Coleoptera'.

    private static SecurityDomain createSecurityDomain() throws Exception {
        PasswordFactory passwordFactory = PasswordFactory.getInstance(ALGORITHM_CLEAR, elytronProvider);

        Map<String, SimpleRealmEntry> passwordMap = new HashMap<>();
        passwordMap.put("elytron", new SimpleRealmEntry(Collections.singletonList(new PasswordCredential(passwordFactory.generatePassword(new ClearPasswordSpec("Coleoptera".toCharArray()))))));

        SimpleMapBackedSecurityRealm simpleRealm = new SimpleMapBackedSecurityRealm(() -> new Provider[] { elytronProvider });

        SecurityDomain.Builder builder = SecurityDomain.builder()

        builder.addRealm("TestRealm", simpleRealm).build();
        builder.setPermissionMapper((principal, roles) -> PermissionVerifier.from(new LoginPermission()));

        return builder.build();

In this example a simple in memory security realm backed by a Map is used, however any of the other Elytron security realms could be used or even a custom security realm implementation if desired.

After assembling the security domain the next step is creating a HttpAuthenticationFactory, the HttpAuthenticationFactory is the overall authentication policy that makes authentication mechanisms backed by the security domain available.

    private static HttpAuthenticationFactory createHttpAuthenticationFactory(final SecurityDomain securityDomain) {
        HttpServerAuthenticationMechanismFactory providerFactory = new SecurityProviderServerMechanismFactory(() -> new Provider[] {elytronProvider});
        HttpServerAuthenticationMechanismFactory httpServerMechanismFactory = new FilterServerMechanismFactory(providerFactory, true, "BASIC");

        return HttpAuthenticationFactory.builder()
                                .addMechanismRealm(MechanismRealmConfiguration.builder().setRealmName("Elytron Realm").build())

At this stage we can see how the original HttpHandler is wrapped using the resulting HttpAuthenticationFactory and some additional Elytron and Undertow APIs to enable security.

    private static HttpHandler wrap(final HttpHandler toWrap, final SecurityDomain securityDomain) {
        HttpAuthenticationFactory httpAuthenticationFactory = createHttpAuthenticationFactory(securityDomain);

        HttpHandler rootHandler = new ElytronRunAsHandler(toWrap);

        rootHandler = new AuthenticationCallHandler(rootHandler);
        rootHandler = new AuthenticationConstraintHandler(rootHandler);

        return ElytronContextAssociationHandler.builder()
                .setMechanismSupplier(() -> {
                    try {
                        return Collections.singletonList(httpAuthenticationFactory.createMechanism("BASIC"));
                    } catch (HttpAuthenticationException e) {
                        throw new RuntimeException(e);

Build and Run

The example project is a standard Maven project so provided Maven is installed along with Java 8 the project can be built using 'mvn install'.

Once built the server can be started using the exec plug-in 'mvn exec:exec'.  This should result in the server starting and listening on port 8080 for incoming requests.

Note: As an example project there is not a lot of output from the project to the console, feel free to add more output if desired to see each stage as it occurs.

After starting the project you should be able to access the server using curl.

undertow-standalone]$ curl -v
*   Trying
* Connected to ( port 8080 (#0)
> GET / HTTP/1.1
> Host:
> User-Agent: curl/7.53.1
> Accept: */*
< HTTP/1.1 401 Unauthorized
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Elytron Realm"
< Content-Length: 0
< Date: Fri, 08 Sep 2017 15:16:30 GMT
* Connection #0 to host left intact

At this stage curl was not supplied with any user details so we can see the HTTP Basic authentication challenge and the request ends.

If we now provide a username and enter the password when prompted we see a full HTTP exchange.

undertow-standalone]$ curl -v --user elytron
Enter host password for user 'elytron':
*   Trying
* Connected to ( port 8080 (#0)
* Server auth using Basic with user 'elytron'
> GET / HTTP/1.1
> Host:
> Authorization: Basic ZWx5dHJvbjpDb2xlb3B0ZXJh
> User-Agent: curl/7.53.1
> Accept: */*
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: text/plain
< Content-Length: 13
< Date: Fri, 08 Sep 2017 15:17:45 GMT
* Connection #0 to host left intact
Hello elytron

The 'Hello elytron' returned at the end is the message from the HttpHandler where 'elytron' is the name of the authenticated principal calling the HttpHandler.