keycloak-aplcache

Changes

integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/AuthServerConfig.java 237(+0 -237)

Details

diff --git a/core/src/main/java/org/keycloak/RealmConfiguration.java b/core/src/main/java/org/keycloak/RealmConfiguration.java
index 5e4a7a4..90275fe 100755
--- a/core/src/main/java/org/keycloak/RealmConfiguration.java
+++ b/core/src/main/java/org/keycloak/RealmConfiguration.java
@@ -2,6 +2,7 @@ package org.keycloak;
 
 import org.jboss.resteasy.client.jaxrs.ResteasyClient;
 import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+import org.keycloak.adapters.config.ManagedResourceConfig;
 
 import javax.ws.rs.core.Form;
 import javax.ws.rs.core.UriBuilder;
@@ -19,6 +20,12 @@ public class RealmConfiguration {
     protected boolean sslRequired = true;
     protected String stateCookieName = "OAuth_Token_Request_State";
 
+    public RealmConfiguration() {
+    }
+
+    public RealmConfiguration(ManagedResourceConfig config) {
+    }
+
     public ResourceMetadata getMetadata() {
         return metadata;
     }
diff --git a/examples/wildfly-demo/customer-app/pom.xml b/examples/wildfly-demo/customer-app/pom.xml
new file mode 100755
index 0000000..5465e55
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.keycloak.example.wildfly.demo</groupId>
+    <artifactId>customer-portal-example</artifactId>
+    <packaging>war</packaging>
+    <name>Customer Portal - Secured via Undertow</name>
+    <description/>
+
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <name>jboss repo</name>
+            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-undertow-adapter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>customer-portal</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <version>7.4.Final</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/wildfly-demo/customer-app/src/main/java/org/jboss/resteasy/example/oauth/CustomerDatabaseClient.java b/examples/wildfly-demo/customer-app/src/main/java/org/jboss/resteasy/example/oauth/CustomerDatabaseClient.java
new file mode 100755
index 0000000..2da2c84
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/java/org/jboss/resteasy/example/oauth/CustomerDatabaseClient.java
@@ -0,0 +1,36 @@
+package org.jboss.resteasy.example.oauth;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.keycloak.SkeletonKeySession;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CustomerDatabaseClient
+{
+   public static List<String> getCustomers(HttpServletRequest request)
+   {
+      SkeletonKeySession session = (SkeletonKeySession)request.getAttribute(SkeletonKeySession.class.getName());
+      ResteasyClient client = new ResteasyClientBuilder()
+                 .trustStore(session.getMetadata().getTruststore())
+                 .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY).build();
+      try
+      {
+         Response response = client.target("http://localhost:8080/database/customers").request()
+                 .header(HttpHeaders.AUTHORIZATION, "Bearer " + session.getTokenString()).get();
+         return response.readEntity(new GenericType<List<String>>(){});
+      }
+      finally
+      {
+         client.close();
+      }
+   }
+}
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/admin/admin.jsp b/examples/wildfly-demo/customer-app/src/main/webapp/admin/admin.jsp
new file mode 100644
index 0000000..e132e37
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/admin/admin.jsp
@@ -0,0 +1,11 @@
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
+ pageEncoding="ISO-8859-1"%>
+<html>
+<head>
+    <title>Customer Admin Iterface</title>
+</head>
+<body bgcolor="#E3F6CE">
+<h1>Customer Admin Interface</h1>
+User <b><%=request.getUserPrincipal().getName()%></b> made this request.
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/customers/cors-test.html b/examples/wildfly-demo/customer-app/src/main/webapp/customers/cors-test.html
new file mode 100755
index 0000000..254b2ae
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/customers/cors-test.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html lang="en">
+
+<body>
+
+<script type="text/javascript">
+    console.log('here!!!!!');
+    var xhr1 = new XMLHttpRequest();
+    xhr1.open('GET', '/customer-portal/K_QUERY_BEARER_TOKEN');
+    xhr1.onreadystatechange = function () {
+        console.log('got here');
+        if (this.status == 200 && this.readyState == 4) {
+            var token = this.responseText;
+            console.log('Access token: ' + token);
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', 'http://localhost:8080/database/customers');
+            xhr.withCredentials = true;
+            xhr.setRequestHeader('Authorization', 'Bearer ' + token);
+            xhr.onreadystatechange = function () {
+                console.log('got auth success');
+                if (this.status == 200 && this.readyState == 4) {
+                    console.log('db response: ' + this.responseText);
+                } else if (this.status != 200) {
+                    console.log('there was an error:' + this.status);
+                }
+            };
+            xhr.send();
+        } else if (this.status != 200) {
+            console.log('there was an error on get bearer token:' + this.status);
+        }
+    };
+    xhr1.send();
+
+
+</script>
+
+</body>
+</html>
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/customers/view.jsp b/examples/wildfly-demo/customer-app/src/main/webapp/customers/view.jsp
new file mode 100755
index 0000000..6e825f7
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/customers/view.jsp
@@ -0,0 +1,28 @@
+<%@ page import="javax.ws.rs.core.UriBuilder" language="java" contentType="text/html; charset=ISO-8859-1"
+ pageEncoding="ISO-8859-1"%>
+<html>
+<head>
+    <title>Customer View Page</title>
+</head>
+<body bgcolor="#E3F6CE">
+<%
+
+   String logoutUri = UriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/tokens/logout")
+                                     .queryParam("redirect_uri", "http://localhost:8080/customer-portal").build().toString();
+%>
+<p>Goto: <a href="http://localhost:8080/product-portal">products</a> | <a href="<%=logoutUri%>">logout</a></p>
+User <b><%=request.getUserPrincipal().getName()%></b> made this request.
+<h2>Customer Listing</h2>
+<%
+java.util.List<String> list = org.jboss.resteasy.example.oauth.CustomerDatabaseClient.getCustomers(request);
+for (String cust : list)
+{
+   out.print("<p>");
+   out.print(cust);
+   out.println("</p>");
+
+}
+%>
+<br><br>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/index.html b/examples/wildfly-demo/customer-app/src/main/webapp/index.html
new file mode 100644
index 0000000..7b164df
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/index.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <title></title>
+</head>
+<body bgcolor="#E3F6CE">
+<h1>Customer Portal</h1>
+
+<p><a href="customers/view.jsp">Customer Listing</a></p>
+<p><a href="admin/admin.html">Customer Admin Interface</a></p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100755
index 0000000..1469973
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,11 @@
+<jboss-deployment-structure>
+    <deployment>
+        <!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
+        <dependencies>
+            <module name="org.bouncycastle"/>
+            <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
+            <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
+            <module name="org.jboss.resteasy.jose-jwt" />
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/jboss-web.xml b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/jboss-web.xml
new file mode 100755
index 0000000..3cec19c
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/jboss-web.xml
@@ -0,0 +1,5 @@
+<jboss-web>
+    <valve>
+        <class-name>org.keycloak.adapters.as7.OAuthManagedResourceValve</class-name>
+    </valve>
+</jboss-web>
\ No newline at end of file
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/keycloak.json b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/keycloak.json
new file mode 100755
index 0000000..9be971b
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,12 @@
+{
+  "realm" : "demo",
+  "resource" : "customer-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/login",
+  "code-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/access/codes",
+  "ssl-not-required" : true,
+  "expose-token" : true,
+   "credentials" : {
+      "password" : "password"
+   }
+}
diff --git a/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/web.xml b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..ac37d2e
--- /dev/null
+++ b/examples/wildfly-demo/customer-app/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>commerce</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/examples/wildfly-demo/database-service/pom.xml b/examples/wildfly-demo/database-service/pom.xml
new file mode 100755
index 0000000..be20e37
--- /dev/null
+++ b/examples/wildfly-demo/database-service/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.keycloak.example.wildfly.demo</groupId>
+    <artifactId>database-service</artifactId>
+    <packaging>war</packaging>
+    <name>JAX-RS Database Service Using OAuth Bearer Tokens</name>
+    <description/>
+    <url>http://maven.apache.org</url>
+
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <name>jboss repo</name>
+            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-undertow-adapter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>database</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <version>7.5.Final</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/CustomerService.java b/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/CustomerService.java
new file mode 100644
index 0000000..c6a0efc
--- /dev/null
+++ b/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/CustomerService.java
@@ -0,0 +1,26 @@
+package org.jboss.resteasy.example.oauth;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("customers")
+public class CustomerService
+{
+   @GET
+   @Produces("application/json")
+   public List<String> getCustomers()
+   {
+      ArrayList<String> rtn = new ArrayList<String>();
+      rtn.add("Bill Burke");
+      rtn.add("Ron Sigal");
+      rtn.add("Weinan Li");
+      return rtn;
+   }
+}
diff --git a/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/DataApplication.java b/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/DataApplication.java
new file mode 100644
index 0000000..673ad16
--- /dev/null
+++ b/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/DataApplication.java
@@ -0,0 +1,13 @@
+package org.jboss.resteasy.example.oauth;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@ApplicationPath("/")
+public class DataApplication extends Application
+{
+}
diff --git a/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/ProductService.java b/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/ProductService.java
new file mode 100644
index 0000000..8515dfe
--- /dev/null
+++ b/examples/wildfly-demo/database-service/src/main/java/org/jboss/resteasy/example/oauth/ProductService.java
@@ -0,0 +1,26 @@
+package org.jboss.resteasy.example.oauth;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("products")
+public class ProductService
+{
+   @GET
+   @Produces("application/json")
+   public List<String> getProducts()
+   {
+      ArrayList<String> rtn = new ArrayList<String>();
+      rtn.add("iphone");
+      rtn.add("ipad");
+      rtn.add("ipod");
+      return rtn;
+   }
+}
diff --git a/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100755
index 0000000..f1f1ffa
--- /dev/null
+++ b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,9 @@
+<jboss-deployment-structure>
+    <deployment>
+        <!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
+        <dependencies>
+            <module name="org.bouncycastle"/>
+             <module name="org.jboss.resteasy.jose-jwt" />
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/jboss-web.xml b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/jboss-web.xml
new file mode 100755
index 0000000..d1ca393
--- /dev/null
+++ b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/jboss-web.xml
@@ -0,0 +1,5 @@
+<jboss-web>
+    <valve>
+        <class-name>org.keycloak.adapters.as7.BearerTokenAuthenticatorValve</class-name>
+    </valve>
+</jboss-web>
\ No newline at end of file
diff --git a/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/keycloak.json b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/keycloak.json
new file mode 100755
index 0000000..6b707d9
--- /dev/null
+++ b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,8 @@
+{
+  "realm" : "demo",
+  "resource" : "database-service",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "enable-cors" : true,
+  "bearer-only" : true
+
+}
diff --git a/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/web.xml b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..4ef7108
--- /dev/null
+++ b/examples/wildfly-demo/database-service/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>database</module-name>
+	
+    <security-constraint>
+        <web-resource-collection>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+<!--        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>  -->
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>KEYCLOAK</auth-method>
+        <realm-name>commerce</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/examples/wildfly-demo/pom.xml b/examples/wildfly-demo/pom.xml
new file mode 100755
index 0000000..3b13f42
--- /dev/null
+++ b/examples/wildfly-demo/pom.xml
@@ -0,0 +1,43 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <name>Examples</name>
+    <description/>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.keycloak</groupId>
+    <artifactId>wildfly-demo-pom</artifactId>
+    <packaging>pom</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <version>7.5.Final</version>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <modules>
+        <module>server</module>
+        <module>customer-app</module>
+        <module>product-app</module>
+        <module>database-service</module>
+        <module>third-party</module>
+    </modules>
+</project>
diff --git a/examples/wildfly-demo/product-app/pom.xml b/examples/wildfly-demo/product-app/pom.xml
new file mode 100755
index 0000000..7e6e482
--- /dev/null
+++ b/examples/wildfly-demo/product-app/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.keycloak.example.wildfly.demo</groupId>
+    <artifactId>product-portal-example</artifactId>
+    <packaging>war</packaging>
+    <name>Product Portal - Secured via Undertow</name>
+    <description/>
+
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <name>jboss repo</name>
+            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-undertow-adapter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>product-portal</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <version>7.5.Final</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/wildfly-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java b/examples/wildfly-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
new file mode 100755
index 0000000..1111268
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
@@ -0,0 +1,36 @@
+package org.jboss.resteasy.example.oauth;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.keycloak.SkeletonKeySession;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ProductDatabaseClient
+{
+   public static List<String> getProducts(HttpServletRequest request)
+   {
+      SkeletonKeySession session = (SkeletonKeySession)request.getAttribute(SkeletonKeySession.class.getName());
+      ResteasyClient client = new ResteasyClientBuilder()
+                 .trustStore(session.getMetadata().getTruststore())
+                 .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY).build();
+      try
+      {
+         Response response = client.target("http://localhost:8080/database/products").request()
+                 .header(HttpHeaders.AUTHORIZATION, "Bearer " + session.getTokenString()).get();
+         return response.readEntity(new GenericType<List<String>>(){});
+      }
+      finally
+      {
+         client.close();
+      }
+   }
+}
diff --git a/examples/wildfly-demo/product-app/src/main/webapp/admin/admin.jsp b/examples/wildfly-demo/product-app/src/main/webapp/admin/admin.jsp
new file mode 100644
index 0000000..b6448d7
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/webapp/admin/admin.jsp
@@ -0,0 +1,11 @@
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
+ pageEncoding="ISO-8859-1"%>
+<html>
+<head>
+    <title>Product Admin Interface</title>
+</head>
+<body bgcolor="#F5F6CE">
+<h1>Product Admin Interface</h1>
+User <b><%=request.getUserPrincipal().getName()%></b> made this request.
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/product-app/src/main/webapp/index.html b/examples/wildfly-demo/product-app/src/main/webapp/index.html
new file mode 100644
index 0000000..e30ebc5
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/webapp/index.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <title></title>
+</head>
+<body bgcolor="#F5F6CE">
+<h1>Product Portal</h1>
+
+<p><a href="products/view.jsp">Product Listing</a></p>
+<p><a href="admin/admin.html">Admin Interface</a></p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/product-app/src/main/webapp/products/view.jsp b/examples/wildfly-demo/product-app/src/main/webapp/products/view.jsp
new file mode 100755
index 0000000..bf1ca5a
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/webapp/products/view.jsp
@@ -0,0 +1,28 @@
+<%@ page import="javax.ws.rs.core.UriBuilder" language="java" contentType="text/html; charset=ISO-8859-1"
+ pageEncoding="ISO-8859-1"%>
+<html>
+<head>
+    <title>Product View Page</title>
+</head>
+<body bgcolor="#F5F6CE">
+<%
+   String logoutUri = UriBuilder.fromUri("http://localhost:8080/auth-server/rest/realms/demo/tokens/logout")
+                                     .queryParam("redirect_uri", "http://localhost:8080/product-portal").build().toString();
+%>
+
+<p>Goto: <a href="http://localhost:8080/customer-portal">customers</a> | <a href="<%=logoutUri%>">logout</a></p>
+User <b><%=request.getUserPrincipal().getName()%></b> made this request.
+<h2>Product Listing</h2>
+<%
+java.util.List<String> list = org.jboss.resteasy.example.oauth.ProductDatabaseClient.getProducts(request);
+for (String cust : list)
+{
+   out.print("<p>");
+   out.print(cust);
+   out.println("</p>");
+
+}
+%>
+<br><br>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100755
index 0000000..1469973
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,11 @@
+<jboss-deployment-structure>
+    <deployment>
+        <!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
+        <dependencies>
+            <module name="org.bouncycastle"/>
+            <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
+            <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
+            <module name="org.jboss.resteasy.jose-jwt" />
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/jboss-web.xml b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/jboss-web.xml
new file mode 100755
index 0000000..3cec19c
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/jboss-web.xml
@@ -0,0 +1,5 @@
+<jboss-web>
+    <valve>
+        <class-name>org.keycloak.adapters.as7.OAuthManagedResourceValve</class-name>
+    </valve>
+</jboss-web>
\ No newline at end of file
diff --git a/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/keycloak.json b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/keycloak.json
new file mode 100755
index 0000000..26bc1fe
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,11 @@
+{
+  "realm" : "demo",
+  "resource" : "product-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/login",
+  "code-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/access/codes",
+   "ssl-not-required" : true,
+   "credentials" : {
+      "password" : "password"
+   }
+}
diff --git a/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/web.xml b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..bfcffb8
--- /dev/null
+++ b/examples/wildfly-demo/product-app/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>product-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>Products</web-resource-name>
+            <url-pattern>/products/*</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>commerce</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/examples/wildfly-demo/README.md b/examples/wildfly-demo/README.md
new file mode 100755
index 0000000..0193e93
--- /dev/null
+++ b/examples/wildfly-demo/README.md
@@ -0,0 +1,79 @@
+Login, Distributed SSO, Distributed Logout, and Oauth Token Grant AS7 Examples
+===================================
+The following examples requires JBoss AS7 or EAP 6.1, and Resteasy 3.0.2 and has been tested on version EAP 6.1.  Here's the highlights of the examples
+* Delegating authentication of a web app to the remote authentication server via OAuth 2 protocols
+* Distributed Single-Sign-On and Single-Logout
+* Transferring identity and role mappings via a special bearer token (Skeleton Key Token).
+* Bearer token authentication and authorization of JAX-RS services
+* Obtaining bearer tokens via the OAuth2 protocol
+
+There are 5 WAR projects.  These all will run on the same jboss instance, but pretend each one is running on a different
+machine on the network or Internet.
+* **auth-server**: This is the keycloak SSO auth server
+* **customer-app** A WAR applications that does remote login using OAUTH2 browser redirects with the auth server
+* **product-app** A WAR applications that does remote login using OAUTH2 browser redirects with the auth server
+* **database-service** JAX-RS services authenticated by bearer tokens only.  The customer and product app invoke on it
+  to get data
+* **third-party** Simple WAR that obtain a bearer token using OAuth2 using browser redirects to the auth-server.
+
+The UI of each of these applications is very crude and exists just to show our OAuth2 implementation in action.
+
+
+Step 1: Make sure you've upgraded Resteasy
+--------------------------------------
+The first thing you is upgrade Resteasy to 3.0.4 within JBoss as described [here](http://docs.jboss.org/resteasy/docs/3.0.4.Final/userguide/html/Installation_Configuration.html#upgrading-as7)
+
+
+Step 2: Boot JBoss
+---------------------------------------
+Boot JBoss in 'standalone' mode.
+
+Step 3: Build and deploy
+---------------------------------------
+next you must build and deploy
+
+1. cd as7-eap-demo
+2. mvn clean install
+3. mvn jboss-as:deploy
+
+Step 4: Login and Observe Apps
+---------------------------------------
+Try going to the customer app and viewing customer data:
+
+[http://localhost:8080/customer-portal/customers/view.jsp](http://localhost:8080/customer-portal/customers/view.jsp)
+
+This should take you to the auth-server login screen.  Enter username: bburke@redhat.com and password: password.
+
+If you click on the products link, you'll be take to the products app and show a product listing.  The redirects
+are still happening, but the auth-server knows you are already logged in so the login is bypassed.
+
+If you click on the logout link of either of the product or customer app, you'll be logged out of all the applications.
+
+Step 5: Traditional OAuth2 Example
+----------------------------------
+The customer and product apps are logins.  The third-party app is the traditional OAuth2 usecase of a client wanting
+to get permission to access a user's data.  To run this example
+
+[http://localhost:8080/oauth-client](http://localhost:8080/oauth-client)
+
+If you area already logged in, you will not be asked for a username and password, but you will be redirected to
+an oauth grant page.  This page asks you if you want to grant certain permissions to the third-part app.
+
+Admin Console
+==========================
+
+1. Register or login
+
+You'll have to first register and create an account
+
+Login:
+[http://localhost:8080/auth-server/rest/saas/login](http://localhost:8080/auth-server/rest/saas/login)
+
+Register:
+[http://localhost:8080/auth-server/rest/saas/registrations](http://localhost:8080/auth-server/rest/saas/registrations)
+
+2. Next you'll be brought to the admin console.  Click "New Realm" button and start doing stuff.
+
+
+
+
diff --git a/examples/wildfly-demo/server/pom.xml b/examples/wildfly-demo/server/pom.xml
new file mode 100755
index 0000000..fc030e0
--- /dev/null
+++ b/examples/wildfly-demo/server/pom.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.keycloak.example.wildfly.demo</groupId>
+    <artifactId>keycloak-server</artifactId>
+    <packaging>war</packaging>
+    <name>Keycloak Demo</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jose-jwt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-social-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-social-google</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-social-twitter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-social-facebook</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-forms</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-admin-ui</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-admin-ui-styles</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jaxrs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mongodb</groupId>
+            <artifactId>mongo-java-driver</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>de.flapdoodle.embed</groupId>
+            <artifactId>de.flapdoodle.embed.mongo</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>auth-server</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <version>7.5.Final</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/wildfly-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java b/examples/wildfly-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java
new file mode 100755
index 0000000..0b7b49d
--- /dev/null
+++ b/examples/wildfly-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java
@@ -0,0 +1,59 @@
+package org.keycloak.example.demo;
+
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.ApplianceBootstrap;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.KeycloakApplication;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Context;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class DemoApplication extends KeycloakApplication {
+
+    public DemoApplication(@Context ServletContext servletContext) {
+        super(servletContext);
+        KeycloakSession session = factory.createSession();
+        session.getTransaction().begin();
+        ApplianceBootstrap bootstrap = new ApplianceBootstrap();
+        bootstrap.bootstrap(session);
+        install(new RealmManager(session));
+        session.getTransaction().commit();
+    }
+
+    public void install(RealmManager manager) {
+        RealmRepresentation rep = loadJson("META-INF/testrealm.json");
+        RealmModel realm = manager.createRealm("demo", rep.getRealm());
+        manager.importRealm(rep, realm);
+
+    }
+
+    public static RealmRepresentation loadJson(String path)
+    {
+        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        int c;
+        try {
+            while ( (c = is.read()) != -1)
+            {
+                os.write(c);
+            }
+            byte[] bytes = os.toByteArray();
+            //System.out.println(new String(bytes));
+
+            return JsonSerialization.fromBytes(RealmRepresentation.class, bytes);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+}
diff --git a/examples/wildfly-demo/server/src/main/resources/META-INF/persistence.xml b/examples/wildfly-demo/server/src/main/resources/META-INF/persistence.xml
new file mode 100755
index 0000000..b949715
--- /dev/null
+++ b/examples/wildfly-demo/server/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,31 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+    version="1.0">
+    <persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
+
+        <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+        <class>org.keycloak.models.jpa.entities.ApplicationScopeMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+        <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+        <class>org.keycloak.models.jpa.entities.RealmScopeMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.RealmUserRoleMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+        <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+        <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserEntity</class>
+        <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+
+        <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+        <properties>
+            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+            <property name="hibernate.show_sql" value="false" />
+            <property name="hibernate.format_sql" value="false" />
+        </properties>
+    </persistence-unit>
+
+</persistence>
diff --git a/examples/wildfly-demo/server/src/main/resources/META-INF/testrealm.json b/examples/wildfly-demo/server/src/main/resources/META-INF/testrealm.json
new file mode 100755
index 0000000..284a4be
--- /dev/null
+++ b/examples/wildfly-demo/server/src/main/resources/META-INF/testrealm.json
@@ -0,0 +1,91 @@
+{
+    "realm": "demo",
+    "enabled": true,
+    "tokenLifespan": 300,
+    "accessCodeLifespan": 10,
+    "accessCodeLifespanUserAction": 600,
+    "sslNotRequired": true,
+    "cookieLoginAllowed": true,
+    "registrationAllowed": true,
+    "social": true,
+    "automaticRegistrationAfterSocialLogin": false,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "requiredApplicationCredentials": [ "password" ],
+    "requiredOAuthClientCredentials": [ "password" ],
+    "defaultRoles": [ "user" ],
+    "users" : [
+        {
+            "username" : "bburke@redhat.com",
+            "enabled": true,
+            "attributes" : {
+                "email" : "bburke@redhat.com"
+            },
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "third-party",
+            "enabled": true,
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        }
+    ],
+    "roles": [
+        {
+            "name": "user",
+            "description": "Have User privileges"
+        },
+        {
+            "name": "admin",
+            "description": "Have Administrator privileges"
+        }
+    ],
+    "roleMappings": [
+        {
+            "username": "bburke@redhat.com",
+            "roles": ["user"]
+        },
+        {
+            "username": "third-party",
+            "roles": ["KEYCLOAK_IDENTITY_REQUESTER"]
+        }
+    ],
+    "scopeMappings": [
+        {
+            "username": "third-party",
+            "roles": ["user"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "customer-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
+            "useRealmMappings": true,
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        },
+        {
+            "name": "product-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8080/product-portal/j_admin_request",
+            "useRealmMappings": true,
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/examples/wildfly-demo/server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/wildfly-demo/server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100755
index 0000000..8caa96f
--- /dev/null
+++ b/examples/wildfly-demo/server/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,10 @@
+<jboss-deployment-structure>
+    <deployment>
+        <!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
+        <dependencies>
+            <module name="org.jboss.resteasy.jose-jwt"/>
+            <module name="org.jboss.resteasy.resteasy-crypto"/>
+            <module name="org.bouncycastle"/>
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/wildfly-demo/server/src/main/webapp/WEB-INF/web.xml b/examples/wildfly-demo/server/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..fafd744
--- /dev/null
+++ b/examples/wildfly-demo/server/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>auth-server</module-name>
+
+    <servlet>
+        <servlet-name>Resteasy</servlet-name>
+        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.keycloak.example.demo.DemoApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>resteasy.servlet.mapping.prefix</param-name>
+            <param-value>/rest</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+        <async-supported>true</async-supported>
+    </servlet>
+
+    <listener>
+        <listener-class>org.keycloak.services.listeners.MongoRunnerListener</listener-class>
+    </listener>
+
+    <filter>
+        <filter-name>Keycloak Session Management</filter-name>
+        <filter-class>org.keycloak.services.filters.KeycloakSessionServletFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Keycloak Session Management</filter-name>
+        <url-pattern>/rest/*</url-pattern>
+    </filter-mapping>
+
+    <servlet-mapping>
+        <servlet-name>Resteasy</servlet-name>
+        <url-pattern>/rest/*</url-pattern>
+    </servlet-mapping>
+
+    <!--
+    <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> -->
+
+</web-app>
diff --git a/examples/wildfly-demo/third-party/pom.xml b/examples/wildfly-demo/third-party/pom.xml
new file mode 100755
index 0000000..4cdfbf1
--- /dev/null
+++ b/examples/wildfly-demo/third-party/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.keycloak.example.wildfly.demo</groupId>
+    <artifactId>oauth-client-example</artifactId>
+    <packaging>war</packaging>
+    <name>Simple OAuth Wildfly Client</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <version>1.0.1.Final</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>oauth-client</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <version>7.5.Final</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/wildfly-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/Bootstrap.java b/examples/wildfly-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/Bootstrap.java
new file mode 100755
index 0000000..717cd3e
--- /dev/null
+++ b/examples/wildfly-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/Bootstrap.java
@@ -0,0 +1,69 @@
+package org.jboss.resteasy.example.oauth;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.keycloak.servlet.ServletOAuthClient;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.KeyStore;
+
+/**
+ * Stupid init code to load up the truststore so we can make appropriate SSL connections
+ * You really should use a better way of initializing this stuff.
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class Bootstrap implements ServletContextListener {
+
+    private ServletOAuthClient client;
+
+    private static KeyStore loadKeyStore(String filename, String password) throws Exception {
+        KeyStore trustStore = KeyStore.getInstance(KeyStore
+                .getDefaultType());
+        File truststoreFile = new File(filename);
+        FileInputStream trustStream = new FileInputStream(truststoreFile);
+        trustStore.load(trustStream, password.toCharArray());
+        trustStream.close();
+        return trustStore;
+    }
+
+    @Override
+    public void contextInitialized(ServletContextEvent sce) {
+        client = new ServletOAuthClient();
+/*
+       // hardcoded, WARNING, you should really have a better way of doing this
+      // configuration.  Either use something like Spring or CDI, or even pull
+      // config vales from context-params
+      String truststorePath = "${jboss.server.config.dir}/client-truststore.ts";
+      String truststorePassword = "password";
+      truststorePath = EnvUtil.replace(truststorePath);
+      KeyStore truststore = null;
+      try
+      {
+         truststore = loadKeyStore(truststorePath, truststorePassword);
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException(e);
+      }
+      client.setTruststore(truststore);
+      */
+        client.setClientId("third-party");
+        client.setPassword("password");
+        client.setAuthUrl("http://localhost:8080/auth-server/rest/realms/demo/tokens/login");
+        client.setCodeUrl("http://localhost:8080/auth-server/rest/realms/demo/tokens/access/codes");
+        client.setClient(new ResteasyClientBuilder().build());
+        client.start();
+        sce.getServletContext().setAttribute(ServletOAuthClient.class.getName(), client);
+
+
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce) {
+        client.stop();
+    }
+}
diff --git a/examples/wildfly-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java b/examples/wildfly-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
new file mode 100755
index 0000000..d21c823
--- /dev/null
+++ b/examples/wildfly-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
@@ -0,0 +1,69 @@
+package org.jboss.resteasy.example.oauth;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.keycloak.servlet.ServletOAuthClient;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ProductDatabaseClient {
+    public static void redirect(HttpServletRequest request, HttpServletResponse response) {
+        // This is really the worst code ever. The ServletOAuthClient is obtained by getting a context attribute
+        // that is set in the Bootstrap context listenr in this project.
+        // You really should come up with a better way to initialize
+        // and obtain the ServletOAuthClient.  I actually suggest downloading the ServletOAuthClient code
+        // and take a look how it works.
+        ServletOAuthClient oAuthClient = (ServletOAuthClient) request.getServletContext().getAttribute(ServletOAuthClient.class.getName());
+        try {
+            oAuthClient.redirectRelative("pull_data.jsp", request, response);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static List<String> getProducts(HttpServletRequest request) {
+        // This is really the worst code ever. The ServletOAuthClient is obtained by getting a context attribute
+        // that is set in the Bootstrap context listenr in this project.
+        // You really should come up with a better way to initialize
+        // and obtain the ServletOAuthClient.  I actually suggest downloading the ServletOAuthClient code
+        // and take a look how it works.
+        ServletOAuthClient oAuthClient = (ServletOAuthClient) request.getServletContext().getAttribute(ServletOAuthClient.class.getName());
+        String token = oAuthClient.getBearerToken(request);
+        ResteasyClient client = new ResteasyClientBuilder()
+                .trustStore(oAuthClient.getTruststore())
+                .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY).build();
+        try {
+            // invoke without the Authorization header
+            Response response = client.target("http://localhost:8080/database/products").request().get();
+            response.close();
+            if (response.getStatus() != 401) {
+                response.close();
+                client.close();
+                throw new RuntimeException("Expecting an auth status code: " + response.getStatus());
+            }
+        } finally {
+        }
+        try {
+            Response response = client.target("http://localhost:8080/database/products").request()
+                    .header(HttpHeaders.AUTHORIZATION, "Bearer " + token).get();
+            if (response.getStatus() != 200) {
+               response.close();
+               throw new RuntimeException("Failed to access!: " + response.getStatus());
+            }
+                return response.readEntity(new GenericType<List<String>>() {
+                });
+        } finally {
+            client.close();
+        }
+    }
+}
diff --git a/examples/wildfly-demo/third-party/src/main/webapp/index.html b/examples/wildfly-demo/third-party/src/main/webapp/index.html
new file mode 100644
index 0000000..dbd7d7a
--- /dev/null
+++ b/examples/wildfly-demo/third-party/src/main/webapp/index.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+<h1>Third Party App That Pulls Data Using OAuth</h1>
+<a href="redirect.jsp">Pull Data</a>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/third-party/src/main/webapp/pull_data.jsp b/examples/wildfly-demo/third-party/src/main/webapp/pull_data.jsp
new file mode 100644
index 0000000..63ad9d9
--- /dev/null
+++ b/examples/wildfly-demo/third-party/src/main/webapp/pull_data.jsp
@@ -0,0 +1,21 @@
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
+ pageEncoding="ISO-8859-1"%>
+<html>
+<head>
+    <title>Pull Page</title>
+</head>
+<body>
+<h2>Pulled Product Listing</h2>
+<%
+java.util.List<String> list = org.jboss.resteasy.example.oauth.ProductDatabaseClient.getProducts(request);
+for (String prod : list)
+{
+   out.print("<p>");
+   out.print(prod);
+   out.println("</p>");
+
+}
+%>
+<br><br>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/wildfly-demo/third-party/src/main/webapp/redirect.jsp b/examples/wildfly-demo/third-party/src/main/webapp/redirect.jsp
new file mode 100644
index 0000000..35ff870
--- /dev/null
+++ b/examples/wildfly-demo/third-party/src/main/webapp/redirect.jsp
@@ -0,0 +1,3 @@
+<%
+   org.jboss.resteasy.example.oauth.ProductDatabaseClient.redirect(request, response);
+%>
\ No newline at end of file
diff --git a/examples/wildfly-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/wildfly-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100755
index 0000000..74f5dff
--- /dev/null
+++ b/examples/wildfly-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,9 @@
+<jboss-deployment-structure>
+    <deployment>
+        <!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
+        <dependencies>
+            <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
+            <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/wildfly-demo/third-party/src/main/webapp/WEB-INF/web.xml b/examples/wildfly-demo/third-party/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..501b203
--- /dev/null
+++ b/examples/wildfly-demo/third-party/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>oauth-client</module-name>
+
+    <listener>
+        <listener-class>org.jboss.resteasy.example.oauth.Bootstrap</listener-class>
+    </listener>
+    <!--
+    <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>
+    -->
+
+</web-app>
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java
index 5d2d7d4..531aa0c 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/AuthenticatedActionsValve.java
@@ -8,7 +8,7 @@ import org.apache.catalina.connector.Response;
 import org.apache.catalina.valves.ValveBase;
 import org.jboss.logging.Logger;
 import org.keycloak.SkeletonKeySession;
-import org.keycloak.adapters.as7.config.ManagedResourceConfig;
+import org.keycloak.adapters.config.ManagedResourceConfig;
 import org.keycloak.representations.SkeletonKeyToken;
 
 import javax.management.ObjectName;
@@ -66,6 +66,7 @@ public class AuthenticatedActionsValve extends ValveBase {
     protected void queryBearerToken(Request request, Response response, SkeletonKeySession session) throws IOException, ServletException {
         log.debugv("queryBearerToken {0}",request.getRequestURI());
         if (abortTokenResponse(request, response, session)) return;
+        response.setStatus(200);
         response.setContentType("text/plain");
         response.getOutputStream().write(session.getTokenString().getBytes());
         response.getOutputStream().flush();
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
index 88e05b6..3c314e1 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
@@ -12,8 +12,9 @@ import org.apache.catalina.deploy.LoginConfig;
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.ResourceMetadata;
-import org.keycloak.adapters.as7.config.ManagedResourceConfig;
-import org.keycloak.adapters.as7.config.ManagedResourceConfigLoader;
+import org.keycloak.adapters.as7.config.CatalinaManagedResourceConfigLoader;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+import org.keycloak.adapters.config.ManagedResourceConfigLoader;
 
 import javax.security.auth.login.LoginException;
 import javax.servlet.ServletException;
@@ -45,7 +46,7 @@ public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements 
     }
 
     protected void init() {
-        ManagedResourceConfigLoader managedResourceConfigLoader = new ManagedResourceConfigLoader(context);
+        ManagedResourceConfigLoader managedResourceConfigLoader = new CatalinaManagedResourceConfigLoader(context);
         remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
         managedResourceConfigLoader.init(false);
         resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaManagedResourceConfigLoader.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaManagedResourceConfigLoader.java
new file mode 100755
index 0000000..ebdec9b
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaManagedResourceConfigLoader.java
@@ -0,0 +1,27 @@
+package org.keycloak.adapters.as7.config;
+
+import org.apache.catalina.Context;
+import org.keycloak.adapters.config.ManagedResourceConfigLoader;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public class CatalinaManagedResourceConfigLoader extends ManagedResourceConfigLoader {
+
+    public CatalinaManagedResourceConfigLoader(Context context) {
+        InputStream is = null;
+        String path = context.getServletContext().getInitParameter("keycloak.config.file");
+        if (path == null) {
+            is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
+        } else {
+            try {
+                is = new FileInputStream(path);
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        loadConfig(is);
+    }
+
+}
\ No newline at end of file
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CorsPreflightChecker.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CorsPreflightChecker.java
index f1ecfd8..2e926c8 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CorsPreflightChecker.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CorsPreflightChecker.java
@@ -3,7 +3,7 @@ package org.keycloak.adapters.as7;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
 import org.jboss.logging.Logger;
-import org.keycloak.adapters.as7.config.ManagedResourceConfig;
+import org.keycloak.adapters.config.ManagedResourceConfig;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java
index a25a12d..3d67ed4 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java
@@ -22,8 +22,9 @@ import org.keycloak.RealmConfiguration;
 import org.keycloak.ResourceMetadata;
 import org.keycloak.SkeletonKeyPrincipal;
 import org.keycloak.SkeletonKeySession;
-import org.keycloak.adapters.as7.config.ManagedResourceConfig;
-import org.keycloak.adapters.as7.config.ManagedResourceConfigLoader;
+import org.keycloak.adapters.as7.config.CatalinaManagedResourceConfigLoader;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+import org.keycloak.adapters.config.ManagedResourceConfigLoader;
 import org.keycloak.representations.SkeletonKeyToken;
 import org.keycloak.representations.idm.admin.LogoutAction;
 
@@ -66,32 +67,12 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
     }
 
     protected void init() {
-        ManagedResourceConfigLoader managedResourceConfigLoader = new ManagedResourceConfigLoader(context);
+        ManagedResourceConfigLoader managedResourceConfigLoader = new CatalinaManagedResourceConfigLoader(context);
         managedResourceConfigLoader.init(true);
         resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
         remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
 
-        realmConfiguration = new RealmConfiguration();
-        String authUrl = remoteSkeletonKeyConfig.getAuthUrl();
-        if (authUrl == null) {
-            throw new RuntimeException("You must specify auth-url");
-        }
-        String tokenUrl = remoteSkeletonKeyConfig.getCodeUrl();
-        if (tokenUrl == null) {
-            throw new RuntimeException("You mut specify code-url");
-        }
-        realmConfiguration.setMetadata(resourceMetadata);
-        realmConfiguration.setSslRequired(!remoteSkeletonKeyConfig.isSslNotRequired());
-
-        for (Map.Entry<String, String> entry : managedResourceConfigLoader.getRemoteSkeletonKeyConfig().getCredentials().entrySet()) {
-            realmConfiguration.getResourceCredentials().param(entry.getKey(), entry.getValue());
-        }
-
-        ResteasyClient client = managedResourceConfigLoader.getClient();
-
-        realmConfiguration.setClient(client);
-        realmConfiguration.setAuthUrl(UriBuilder.fromUri(authUrl).queryParam("client_id", resourceMetadata.getResourceName()));
-        realmConfiguration.setCodeUrl(client.target(tokenUrl));
+        realmConfiguration = managedResourceConfigLoader.getRealmConfiguration();
         AuthenticatedActionsValve actions = new AuthenticatedActionsValve(remoteSkeletonKeyConfig, getNext(), getContainer(), getController());
         setNext(actions);
     }
diff --git a/integration/pom.xml b/integration/pom.xml
index 5028c1d..74f4c15 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -16,6 +16,7 @@
 
     <modules>
         <module>as7-eap6/adapter</module>
+        <module>undertow</module>
         <!-- <module>as7-eap6/jboss-modules</module> -->
     </modules>
 </project>
diff --git a/integration/undertow/pom.xml b/integration/undertow/pom.xml
new file mode 100755
index 0000000..4fed19b
--- /dev/null
+++ b/integration/undertow/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<project>
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-1</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-undertow-adapter</artifactId>
+    <name>Keycloak Undertow Integration</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <version>3.1.2.GA</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jose-jwt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-servlet</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AuthenticatedActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AuthenticatedActionsHandler.java
new file mode 100755
index 0000000..15b445e
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AuthenticatedActionsHandler.java
@@ -0,0 +1,116 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.Headers;
+import org.jboss.logging.Logger;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+import org.keycloak.representations.SkeletonKeyToken;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Pre-installed actions that must be authenticated
+ *
+ * Actions include:
+ *
+ * CORS Origin Check and Response headers
+ * K_QUERY_BEARER_TOKEN: Get bearer token from server for Javascripts CORS requests
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticatedActionsHandler implements HttpHandler {
+    private static final Logger log = Logger.getLogger(AuthenticatedActionsHandler.class);
+    protected ManagedResourceConfig config;
+    protected HttpHandler next;
+
+    protected AuthenticatedActionsHandler(ManagedResourceConfig config, HttpHandler next) {
+        this.config = config;
+        this.next = next;
+    }
+
+    @Override
+    public void handleRequest(HttpServerExchange exchange) throws Exception {
+        log.debugv("AuthenticatedActionsValve.invoke {0}", exchange.getRequestURI());
+        SkeletonKeySession session = getSkeletonKeySession(exchange);
+        if (corsRequest(exchange, session)) return;
+        String requestUri = exchange.getRequestURI();
+        if (requestUri.endsWith("K_QUERY_BEARER_TOKEN")) {
+            queryBearerToken(exchange, session);
+            return;
+        }
+        next.handleRequest(exchange);
+    }
+
+    public SkeletonKeySession getSkeletonKeySession(HttpServerExchange exchange) {
+        SkeletonKeySession skSession = exchange.getAttachment(KeycloakAuthenticationMechanism.SKELETON_KEY_SESSION_ATTACHMENT_KEY);
+        if (skSession != null) return skSession;
+        return null;
+    }
+
+    protected void queryBearerToken(HttpServerExchange exchange, SkeletonKeySession session) throws IOException, ServletException {
+        log.debugv("queryBearerToken {0}",exchange.getRequestURI());
+        if (abortTokenResponse(exchange, session)) return;
+        exchange.setResponseCode(200);
+        exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
+        exchange.getResponseSender().send(session.getTokenString());
+        exchange.endExchange();
+    }
+
+    protected boolean abortTokenResponse(HttpServerExchange exchange, SkeletonKeySession session) throws IOException {
+        if (session == null) {
+            log.debugv("session was null, sending back 401: {0}",exchange.getRequestURI());
+            exchange.setResponseCode(200);
+            exchange.endExchange();
+            return true;
+        }
+        if (!config.isExposeToken()) {
+            exchange.setResponseCode(200);
+            exchange.endExchange();
+            return true;
+        }
+        if (!config.isCors() && exchange.getRequestHeaders().getFirst(Headers.ORIGIN) != null) {
+            exchange.setResponseCode(200);
+            exchange.endExchange();
+            return true;
+        }
+        return false;
+    }
+
+    protected boolean corsRequest(HttpServerExchange exchange, SkeletonKeySession session) throws IOException {
+        if (!config.isCors()) return false;
+        log.debugv("CORS enabled + request.getRequestURI()");
+        String origin = exchange.getRequestHeaders().getFirst("Origin");
+        log.debugv("Origin: {0} uri: {1}", origin, exchange.getRequestURI());
+        if (session != null && origin != null) {
+            SkeletonKeyToken token = session.getToken();
+            Set<String> allowedOrigins = token.getAllowedOrigins();
+            if (log.isDebugEnabled()) {
+                for (String a : allowedOrigins) log.debug("   " + a);
+            }
+            if (allowedOrigins == null || (!allowedOrigins.contains("*") && !allowedOrigins.contains(origin))) {
+                if (allowedOrigins == null) {
+                    log.debugv("allowedOrigins was null in token");
+                }
+                if (!allowedOrigins.contains("*") && !allowedOrigins.contains(origin)) {
+                    log.debugv("allowedOrigins did not contain origin");
+
+                }
+                exchange.setResponseCode(403);
+                exchange.endExchange();
+                return true;
+            }
+            log.debugv("returning origin: {0}", origin);
+            exchange.setResponseCode(200);
+            exchange.getResponseHeaders().put(PreflightCorsHandler.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+            exchange.getResponseHeaders().put(PreflightCorsHandler.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+        } else {
+            log.debugv("session or origin was null: {0}", exchange.getRequestURI());
+        }
+        return false;
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/BearerTokenAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/BearerTokenAuthenticator.java
new file mode 100755
index 0000000..a76604b
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/BearerTokenAuthenticator.java
@@ -0,0 +1,149 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.SecurityContext;
+import io.undertow.server.HttpServerExchange;
+import org.jboss.logging.Logger;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.VerificationException;
+import org.keycloak.representations.SkeletonKeyToken;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static io.undertow.util.Headers.AUTHORIZATION;
+import static io.undertow.util.Headers.WWW_AUTHENTICATE;
+import static io.undertow.util.StatusCodes.UNAUTHORIZED;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class BearerTokenAuthenticator {
+    protected ResourceMetadata resourceMetadata;
+    protected Logger log = Logger.getLogger(BearerTokenAuthenticator.class);
+    protected String tokenString;
+    protected SkeletonKeyToken token;
+    protected boolean useResourceRoleMappings;
+    protected String surrogate;
+    protected KeycloakChallenge challenge;
+
+    public BearerTokenAuthenticator(ResourceMetadata resourceMetadata, boolean useResourceRoleMappings) {
+        this.resourceMetadata = resourceMetadata;
+        this.useResourceRoleMappings = useResourceRoleMappings;
+    }
+
+    public KeycloakChallenge getChallenge() {
+        return challenge;
+    }
+
+    public ResourceMetadata getResourceMetadata() {
+        return resourceMetadata;
+    }
+
+    public String getTokenString() {
+        return tokenString;
+    }
+
+    public SkeletonKeyToken getToken() {
+        return token;
+    }
+
+    public String getSurrogate() {
+        return surrogate;
+    }
+
+    public AuthenticationMechanism.AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange)  {
+        List<String> authHeaders = exchange.getRequestHeaders().get(AUTHORIZATION);
+        if (authHeaders == null || authHeaders.size() == 0) {
+            challenge = challengeResponse(exchange, null, null);
+            return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+        }
+
+        tokenString = null;
+        for (String authHeader : authHeaders) {
+            String[] split = authHeader.trim().split("\\s+");
+            if (split == null || split.length != 2) continue;
+            if (!split[0].equalsIgnoreCase("Bearer")) continue;
+            tokenString = split[1];
+        }
+
+        if (tokenString == null) {
+            challenge = challengeResponse(exchange, null, null);
+            return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+        }
+
+        try {
+            token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
+        } catch (VerificationException e) {
+            log.error("Failed to verify token", e);
+            challenge = challengeResponse(exchange, "invalid_token", e.getMessage());
+            return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+        }
+        boolean verifyCaller = false;
+        Set<String> roles = new HashSet<String>();
+        if (useResourceRoleMappings) {
+            verifyCaller = token.isVerifyCaller(resourceMetadata.getResourceName());
+        } else {
+            verifyCaller = token.isVerifyCaller();
+        }
+        surrogate = null;
+        if (verifyCaller) {
+            if (token.getTrustedCertificates() == null || token.getTrustedCertificates().size() == 0) {
+                log.warn("No trusted certificates in token");
+                challenge = clientCertChallenge();
+                return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+            }
+
+            // for now, we just make sure Undertow did two-way SSL
+            // assume JBoss Web verifies the client cert
+            X509Certificate[] chain = new X509Certificate[0];
+            try {
+                chain = exchange.getConnection().getSslSessionInfo().getPeerCertificateChain();
+            } catch (SSLPeerUnverifiedException ignore) {
+
+            }
+            if (chain == null || chain.length == 0) {
+                log.warn("No certificates provided by undertow to verify the caller");
+                challenge = clientCertChallenge();
+                return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+            }
+            surrogate = chain[0].getSubjectDN().getName();
+        }
+        return AuthenticationMechanism.AuthenticationMechanismOutcome.AUTHENTICATED;
+    }
+
+    protected KeycloakChallenge clientCertChallenge() {
+        return new KeycloakChallenge() {
+            @Override
+            public AuthenticationMechanism.ChallengeResult sendChallenge(HttpServerExchange httpServerExchange, SecurityContext securityContext) {
+                // do the same thing as client cert auth
+                return new AuthenticationMechanism.ChallengeResult(false);
+            }
+        };
+    }
+
+
+    protected KeycloakChallenge challengeResponse(HttpServerExchange exchange, String error, String description) {
+        StringBuilder header = new StringBuilder("Bearer realm=\"");
+        header.append(resourceMetadata.getRealm()).append("\"");
+        if (error != null) {
+            header.append(", error=\"").append(error).append("\"");
+        }
+        if (description != null) {
+            header.append(", error_description=\"").append(description).append("\"");
+        }
+        String challenge = header.toString();
+        exchange.getResponseHeaders().add(WWW_AUTHENTICATE, challenge);
+        return new KeycloakChallenge() {
+            @Override
+            public AuthenticationMechanism.ChallengeResult sendChallenge(HttpServerExchange httpServerExchange, SecurityContext securityContext) {
+                return new AuthenticationMechanism.ChallengeResult(true, UNAUTHORIZED);
+            }
+        };
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
new file mode 100755
index 0000000..75b3df5
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java
@@ -0,0 +1,142 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.SecurityContext;
+import io.undertow.security.idm.Account;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.AttachmentKey;
+import org.jboss.logging.Logger;
+import org.keycloak.RealmConfiguration;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.SkeletonKeyPrincipal;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+import org.keycloak.representations.SkeletonKeyToken;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Set;
+
+import static io.undertow.util.Headers.WWW_AUTHENTICATE;
+import static io.undertow.util.StatusCodes.UNAUTHORIZED;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakAuthenticationMechanism implements AuthenticationMechanism {
+    protected Logger log = Logger.getLogger(KeycloakAuthenticationMechanism.class);
+
+    public static final AttachmentKey<KeycloakChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(KeycloakChallenge.class);
+    public static final AttachmentKey<SkeletonKeySession> SKELETON_KEY_SESSION_ATTACHMENT_KEY = AttachmentKey.create(SkeletonKeySession.class);
+
+    protected ResourceMetadata resourceMetadata;
+    protected ManagedResourceConfig config;
+    protected RealmConfiguration realmConfig;
+    protected int sslRedirectPort;
+
+    public KeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, ManagedResourceConfig config, RealmConfiguration realmConfig, int sslRedirectPort) {
+        this.resourceMetadata = resourceMetadata;
+        this.config = config;
+        this.realmConfig = realmConfig;
+        this.sslRedirectPort = sslRedirectPort;
+    }
+
+    public KeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, ManagedResourceConfig config, RealmConfiguration realmConfig) {
+        this.resourceMetadata = resourceMetadata;
+        this.config = config;
+        this.realmConfig = realmConfig;
+    }
+
+    @Override
+    public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
+        BearerTokenAuthenticator bearer = createBearerTokenAuthenticator();
+        AuthenticationMechanismOutcome outcome = bearer.authenticate(exchange);
+        if (outcome == AuthenticationMechanismOutcome.NOT_AUTHENTICATED) {
+            exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, bearer.getChallenge());
+            return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+        }
+        else if (outcome == AuthenticationMechanismOutcome.AUTHENTICATED) {
+            final SkeletonKeyToken token = bearer.getToken();
+            String surrogate = bearer.getSurrogate();
+            SkeletonKeySession session = new SkeletonKeySession(bearer.getTokenString(), token, resourceMetadata);
+            propagateBearer(exchange, session);
+            completeAuthentication(exchange, securityContext, token, surrogate);
+            return AuthenticationMechanismOutcome.AUTHENTICATED;
+        }
+        else if (config.isBearerOnly()) {
+            exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, bearer.getChallenge());
+            return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+        }
+
+        OAuthAuthenticator oauth = createOAuthAuthenticator(exchange);
+        outcome = oauth.authenticate();
+        if (outcome == AuthenticationMechanismOutcome.NOT_AUTHENTICATED) {
+            exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, oauth.getChallenge());
+            return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+        }
+        else if (outcome == AuthenticationMechanismOutcome.NOT_ATTEMPTED) {
+            exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, oauth.getChallenge());
+            return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+
+        }
+        SkeletonKeySession session = new SkeletonKeySession(oauth.getTokenString(), oauth.getToken(), resourceMetadata);
+        propagateOauth(exchange, session);
+        completeAuthentication(exchange, securityContext, oauth.getToken(), null);
+        log.info("AUTHENTICATED");
+        return AuthenticationMechanismOutcome.AUTHENTICATED;
+    }
+
+    protected OAuthAuthenticator createOAuthAuthenticator(HttpServerExchange exchange) {
+        return new OAuthAuthenticator(exchange, realmConfig, sslRedirectPort);
+    }
+
+    protected BearerTokenAuthenticator createBearerTokenAuthenticator() {
+        return new BearerTokenAuthenticator(resourceMetadata, config.isUseResourceRoleMappings());
+    }
+
+    protected void completeAuthentication(HttpServerExchange exchange, SecurityContext securityContext, SkeletonKeyToken token, String surrogate) {
+        final SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getPrincipal(), surrogate);
+        Set<String> roles = null;
+        if (config.isUseResourceRoleMappings()) {
+            SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
+            if (access != null) roles = access.getRoles();
+        } else {
+            SkeletonKeyToken.Access access = token.getRealmAccess();
+            if (access != null) roles = access.getRoles();
+        }
+        if (roles == null) roles = Collections.emptySet();
+        final Set<String> accountRoles = roles;
+        Account account = new Account() {
+            @Override
+            public Principal getPrincipal() {
+                return skeletonKeyPrincipal;
+            }
+
+            @Override
+            public Set<String> getRoles() {
+                return accountRoles;
+            }
+        };
+        securityContext.authenticationComplete(account, "FORM");
+    }
+
+    protected void propagateBearer(HttpServerExchange exchange, SkeletonKeySession session) {
+        exchange.putAttachment(SKELETON_KEY_SESSION_ATTACHMENT_KEY, session);
+
+    }
+
+    protected void propagateOauth(HttpServerExchange exchange, SkeletonKeySession session) {
+        exchange.putAttachment(SKELETON_KEY_SESSION_ATTACHMENT_KEY, session);
+    }
+
+
+    @Override
+    public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
+        KeycloakChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
+        if (challenge != null) {
+            return challenge.sendChallenge(exchange, securityContext);
+        }
+        return new ChallengeResult(false);
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakChallenge.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakChallenge.java
new file mode 100755
index 0000000..6e77c89
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakChallenge.java
@@ -0,0 +1,13 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.SecurityContext;
+import io.undertow.server.HttpServerExchange;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface KeycloakChallenge {
+    public AuthenticationMechanism.ChallengeResult sendChallenge(HttpServerExchange httpServerExchange, SecurityContext securityContext);
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
new file mode 100755
index 0000000..5ff6e1e
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
@@ -0,0 +1,75 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.security.idm.Account;
+import io.undertow.security.idm.Credential;
+import io.undertow.security.idm.IdentityManager;
+import io.undertow.servlet.ServletExtension;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.ServletSessionConfig;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+import org.keycloak.adapters.config.ManagedResourceConfigLoader;
+
+import javax.servlet.ServletContext;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakServletExtension implements ServletExtension {
+    protected Logger log = Logger.getLogger(KeycloakServletExtension.class);
+
+    @Override
+    public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
+        if (deploymentInfo.getLoginConfig() == null || !deploymentInfo.getLoginConfig().getAuthMethod().equalsIgnoreCase("keycloak")) {
+            log.info("auth-method is not keycloak!");
+            return;
+        }
+        log.info("KeycloakServletException initialization");
+        deploymentInfo.setIgnoreStandardAuthenticationMechanism(true);
+        InputStream is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
+        if (is == null) throw new RuntimeException("Unable to find /WEB-INF/keycloak.json configuration file");
+        ManagedResourceConfigLoader loader = new ManagedResourceConfigLoader(is);
+        loader.init(true);
+        ManagedResourceConfig keycloakConfig = loader.getRemoteSkeletonKeyConfig();
+        PreflightCorsHandler.Wrapper preflight = new PreflightCorsHandler.Wrapper(keycloakConfig);
+        ServletKeycloakAuthenticationMechanism auth = new ServletKeycloakAuthenticationMechanism(loader.getResourceMetadata(),
+                keycloakConfig,
+                loader.getRealmConfiguration(),
+                deploymentInfo.getConfidentialPortManager());
+        ServletAuthenticatedActionsHandler.Wrapper actions = new ServletAuthenticatedActionsHandler.Wrapper(keycloakConfig);
+
+        // setup handlers
+
+        deploymentInfo.addInitialHandlerChainWrapper(preflight); // cors preflight
+        deploymentInfo.addAuthenticationMechanism(auth); // authentication
+        deploymentInfo.addInnerHandlerChainWrapper(ServletPropagateSessionHandler.WRAPPER); // propagates SkeletonKeySession
+        deploymentInfo.addInnerHandlerChainWrapper(actions); // handles authenticated actions and cors.
+
+        deploymentInfo.setIdentityManager(new IdentityManager() {
+            @Override
+            public Account verify(Account account) {
+                log.info("Verifying account in IdentityManager");
+                return account;
+            }
+
+            @Override
+            public Account verify(String id, Credential credential) {
+                log.warn("Shouldn't call verify!!!");
+                throw new IllegalStateException("Not allowed");
+            }
+
+            @Override
+            public Account verify(Credential credential) {
+                log.warn("Shouldn't call verify!!!");
+                throw new IllegalStateException("Not allowed");
+            }
+        });
+
+        log.info("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
+        ServletSessionConfig cookieConfig = new ServletSessionConfig();
+        cookieConfig.setPath(deploymentInfo.getContextPath());
+        deploymentInfo.setServletSessionConfig(cookieConfig);
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
new file mode 100755
index 0000000..23fd318
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/OAuthAuthenticator.java
@@ -0,0 +1,294 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.SecurityContext;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.Cookie;
+import io.undertow.server.handlers.CookieImpl;
+import io.undertow.util.Headers;
+import org.jboss.logging.Logger;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.RealmConfiguration;
+import org.keycloak.VerificationException;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.util.Deque;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OAuthAuthenticator {
+    private static final Logger log = Logger.getLogger(OAuthAuthenticator.class);
+    protected RealmConfiguration realmInfo;
+    protected int sslRedirectPort;
+    protected String tokenString;
+    protected SkeletonKeyToken token;
+    protected HttpServerExchange exchange;
+    protected String redirectUri;
+    protected KeycloakChallenge challenge;
+
+    public OAuthAuthenticator(HttpServerExchange exchange, RealmConfiguration realmInfo,  int sslRedirectPort) {
+        this.exchange = exchange;
+        this.realmInfo = realmInfo;
+        this.sslRedirectPort = sslRedirectPort;
+    }
+
+    public KeycloakChallenge getChallenge() {
+        return challenge;
+    }
+
+    public String getTokenString() {
+        return tokenString;
+    }
+
+    public SkeletonKeyToken getToken() {
+        return token;
+    }
+
+    public String getRedirectUri() {
+        return redirectUri;
+    }
+
+    protected String getRequestUrl() {
+        UriBuilder uriBuilder = UriBuilder.fromUri(exchange.getRequestURI())
+                .replaceQuery(exchange.getQueryString());
+        if (!exchange.isHostIncludedInRequestURI()) uriBuilder.scheme(exchange.getRequestScheme()).host(exchange.getHostAndPort());
+        return uriBuilder.build().toString();
+    }
+
+    protected boolean isRequestSecure() {
+        return exchange.getProtocol().toString().equalsIgnoreCase("https");
+    }
+
+    protected Cookie getCookie(String cookieName) {
+        Map<String, Cookie> requestCookies = exchange.getRequestCookies();
+        if (requestCookies == null) return null;
+        return requestCookies.get(cookieName);
+    }
+
+    protected String getCookieValue(String cookieName) {
+        Cookie cookie = getCookie(cookieName);
+        if (cookie == null) return null;
+        return cookie.getValue();
+    }
+
+    protected String getQueryParamValue(String paramName) {
+        Map<String,Deque<String>> queryParameters = exchange.getQueryParameters();
+        if (queryParameters == null) return null;
+        Deque<String> strings = queryParameters.get(paramName);
+        if (strings == null) return null;
+        return strings.getFirst();
+    }
+
+    protected String getError() {
+        return getQueryParamValue("error");
+    }
+
+    protected String getCode() {
+        return getQueryParamValue("code");
+    }
+
+    protected String getRedirectUri(String state) {
+        String url = getRequestUrl();
+        log.info("sending redirect uri: " + url);
+        if (!isRequestSecure() && realmInfo.isSslRequired()) {
+            int port = sslRedirectPort();
+            if (port < 0) {
+                // disabled?
+                return null;
+            }
+            UriBuilder secureUrl = UriBuilder.fromUri(url).scheme("https").port(-1);
+            if (port != 443) secureUrl.port(port);
+            url = secureUrl.build().toString();
+        }
+        return realmInfo.getAuthUrl().clone()
+                .queryParam("client_id", realmInfo.getMetadata().getResourceName())
+                .queryParam("redirect_uri", url)
+                .queryParam("state", state)
+                .queryParam("login", "true")
+                .build().toString();
+    }
+
+    protected int sslRedirectPort() {
+        return sslRedirectPort;
+    }
+
+    protected static final AtomicLong counter = new AtomicLong();
+
+    protected String getStateCode() {
+        return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
+    }
+
+    protected KeycloakChallenge loginRedirect() {
+        final String state = getStateCode();
+        final String redirect = getRedirectUri(state);
+        return new KeycloakChallenge() {
+            @Override
+            public AuthenticationMechanism.ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
+                if (redirect == null) {
+                    return new AuthenticationMechanism.ChallengeResult(true, 403);
+                }
+                CookieImpl cookie = new CookieImpl(realmInfo.getStateCookieName(), state);
+                //cookie.setPath(getDefaultCookiePath()); todo I don't think we need to set state cookie path as it will be the same redirect
+                cookie.setSecure(realmInfo.isSslRequired());
+                exchange.setResponseCookie(cookie);
+                exchange.getResponseHeaders().put(Headers.LOCATION, redirect);
+                return new AuthenticationMechanism.ChallengeResult(true, 302);
+            }
+        };
+    }
+
+    protected KeycloakChallenge checkStateCookie() {
+        Cookie stateCookie = getCookie(realmInfo.getStateCookieName());
+
+        if (stateCookie == null) {
+            log.warn("No state cookie");
+            return challenge(400);
+        }
+        // reset the cookie
+        log.info("** reseting application state cookie");
+        Cookie reset = new CookieImpl(realmInfo.getStateCookieName(), "");
+        reset.setPath(stateCookie.getPath());
+        reset.setMaxAge(0);
+        exchange.setResponseCookie(reset);
+
+        String stateCookieValue = getCookieValue(realmInfo.getStateCookieName());
+
+        String state = getQueryParamValue("state");
+        if (state == null) {
+            log.warn("state parameter was null");
+            return challenge(400);
+        }
+        if (!state.equals(stateCookieValue)) {
+            log.warn("state parameter invalid");
+            log.warn("cookie: " + stateCookieValue);
+            log.warn("queryParam: " + state);
+            return challenge(400);
+        }
+        return null;
+
+    }
+
+    public AuthenticationMechanism.AuthenticationMechanismOutcome authenticate() {
+        String code = getCode();
+        if (code == null) {
+            log.info("there was no code");
+            String error = getError();
+            if (error != null) {
+                // todo how do we send a response?
+                log.warn("There was an error: " + error);
+                challenge = challenge(400);
+                return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+            } else {
+                log.info("redirecting to auth server");
+                challenge = loginRedirect();
+                return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+            }
+        } else {
+            log.info("there was a code, resolving");
+            challenge = resolveCode(code);
+            if (challenge != null) {
+                return AuthenticationMechanism.AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+            }
+            return AuthenticationMechanism.AuthenticationMechanismOutcome.AUTHENTICATED;
+        }
+
+    }
+
+    protected KeycloakChallenge challenge(final int code) {
+        return new KeycloakChallenge() {
+            @Override
+            public AuthenticationMechanism.ChallengeResult sendChallenge(HttpServerExchange httpServerExchange, SecurityContext securityContext) {
+                return new AuthenticationMechanism.ChallengeResult(true, code);
+            }
+        };
+    }
+
+    /**
+     * Start or continue the oauth login process.
+     * <p/>
+     * if code query parameter is not present, then browser is redirected to authUrl.  The redirect URL will be
+     * the URL of the current request.
+     * <p/>
+     * If code query parameter is present, then an access token is obtained by invoking a secure request to the codeUrl.
+     * If the access token is obtained, the browser is again redirected to the current request URL, but any OAuth
+     * protocol specific query parameters are removed.
+     *
+     * @return null if an access token was obtained, otherwise a challenge is returned
+     */
+    protected KeycloakChallenge resolveCode(String code) {
+        // abort if not HTTPS
+        if (realmInfo.isSslRequired() && !isRequestSecure()) {
+            log.error("SSL is required");
+            return challenge(403);
+        }
+
+        log.info("checking state cookie for after code");
+        KeycloakChallenge challenge = checkStateCookie();
+        if (challenge != null) return challenge;
+
+        String client_id = realmInfo.getMetadata().getResourceName();
+        String password = realmInfo.getResourceCredentials().asMap().getFirst("password");
+        //String authHeader = BasicAuthHelper.createHeader(client_id, password);
+        redirectUri = stripOauthParametersFromRedirect();
+        Form form = new Form();
+        form.param("grant_type", "authorization_code")
+                .param("code", code)
+                .param("client_id", client_id)
+                .param(CredentialRepresentation.PASSWORD, password)
+                .param("redirect_uri", redirectUri);
+
+        Response res = realmInfo.getCodeUrl().request()
+                .post(Entity.form(form));
+        AccessTokenResponse tokenResponse;
+        try {
+            if (res.getStatus() != 200) {
+                log.error("failed to turn code into token");
+                log.error("status from server: " + res.getStatus());
+                if (res.getStatus() == 400 && res.getMediaType() != null) {
+                    log.error("   " + res.readEntity(String.class));
+                }
+                return challenge(403);
+            }
+            log.debug("media type: " + res.getMediaType());
+            log.debug("Content-Type header: " + res.getHeaderString("Content-Type"));
+            tokenResponse = res.readEntity(AccessTokenResponse.class);
+        } finally {
+            res.close();
+        }
+
+        tokenString = tokenResponse.getToken();
+        try {
+            token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata());
+            log.debug("Token Verification succeeded!");
+        } catch (VerificationException e) {
+            log.error("failed verification of token");
+            return challenge(403);
+        }
+        log.info("successful authenticated");
+        return null;
+    }
+
+    /**
+     * strip out unwanted query parameters and redirect so bookmarks don't retain oauth protocol bits
+     */
+    protected String stripOauthParametersFromRedirect() {
+        UriBuilder builder = UriBuilder.fromUri(exchange.getRequestURI())
+                .replaceQuery(exchange.getQueryString())
+                .replaceQueryParam("code", null)
+                .replaceQueryParam("state", null);
+        return builder.build().toString();
+    }
+
+
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/PreflightCorsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/PreflightCorsHandler.java
new file mode 100755
index 0000000..0096b2d
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/PreflightCorsHandler.java
@@ -0,0 +1,80 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HandlerWrapper;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HttpString;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PreflightCorsHandler implements HttpHandler {
+    private static final Logger log = Logger.getLogger(PreflightCorsHandler.class);
+    protected ManagedResourceConfig config;
+    protected HttpHandler next;
+
+    public static final HttpString ACCESS_CONTROL_ALLOW_ORIGIN = new HttpString("Access-Control-Allow-Origin");
+    public static final HttpString ACCESS_CONTROL_ALLOW_CREDENTIALS = new HttpString("Access-Control-Allow-Credentials");
+    public static final HttpString ACCESS_CONTROL_ALLOW_METHODS = new HttpString("Access-Control-Allow-Methods");
+    public static final HttpString ACCESS_CONTROL_ALLOW_HEADERS = new HttpString("Access-Control-Allow-Headers");
+    public static final HttpString ACCESS_CONTROL_MAX_AGE = new HttpString("Access-Control-Max-Age");
+
+    public static class Wrapper implements HandlerWrapper {
+        protected ManagedResourceConfig config;
+
+        public Wrapper(ManagedResourceConfig config) {
+            this.config = config;
+        }
+
+        @Override
+        public HttpHandler wrap(HttpHandler handler) {
+            return new PreflightCorsHandler(config, handler);
+        }
+    }
+
+    protected PreflightCorsHandler(ManagedResourceConfig config, HttpHandler next) {
+        this.config = config;
+        this.next = next;
+    }
+
+    @Override
+    public void handleRequest(HttpServerExchange exchange) throws Exception {
+        log.debugv("checkCorsPreflight {0}", exchange.getRequestURI());
+        if (!exchange.getRequestMethod().toString().equalsIgnoreCase("OPTIONS")) {
+            log.debug("checkCorsPreflight: not options ");
+            next.handleRequest(exchange);
+            return;
+        }
+        if (exchange.getRequestHeaders().getFirst("Origin") == null) {
+            log.debug("checkCorsPreflight: no origin header");
+            next.handleRequest(exchange);
+            return;
+        }
+        log.debug("Preflight request returning");
+        exchange.setResponseCode(200);
+        String origin = exchange.getRequestHeaders().getFirst("Origin");
+        exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+        exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+        String requestMethods = exchange.getRequestHeaders().getFirst("Access-Control-Request-Method");
+        if (requestMethods != null) {
+            if (config.getCorsAllowedMethods() != null) {
+                requestMethods = config.getCorsAllowedMethods();
+            }
+            exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_METHODS, requestMethods);
+        }
+        String allowHeaders = exchange.getRequestHeaders().getFirst("Access-Control-Request-Headers");
+        if (allowHeaders != null) {
+            if (config.getCorsAllowedHeaders() != null) {
+                allowHeaders = config.getCorsAllowedHeaders();
+            }
+            exchange.getResponseHeaders().put(ACCESS_CONTROL_ALLOW_HEADERS, allowHeaders);
+        }
+        if (config.getCorsMaxAge() > -1) {
+            exchange.getResponseHeaders().put(ACCESS_CONTROL_MAX_AGE, Integer.toString(config.getCorsMaxAge()));
+        }
+        exchange.endExchange();
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAuthenticatedActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAuthenticatedActionsHandler.java
new file mode 100755
index 0000000..fb771e6
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAuthenticatedActionsHandler.java
@@ -0,0 +1,48 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HandlerWrapper;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletAuthenticatedActionsHandler extends AuthenticatedActionsHandler {
+
+    protected ServletAuthenticatedActionsHandler(ManagedResourceConfig config, HttpHandler next) {
+        super(config, next);
+    }
+
+    public static class Wrapper implements HandlerWrapper {
+        protected ManagedResourceConfig config;
+
+        public Wrapper(ManagedResourceConfig config) {
+            this.config = config;
+        }
+
+        @Override
+        public HttpHandler wrap(HttpHandler handler) {
+            return new ServletAuthenticatedActionsHandler(config, handler);
+        }
+    }
+
+    @Override
+    public SkeletonKeySession getSkeletonKeySession(HttpServerExchange exchange) {
+        SkeletonKeySession skSession = super.getSkeletonKeySession(exchange);
+        if (skSession != null) return skSession;
+
+        final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+        HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
+        HttpSession session = req.getSession(false);
+        if (session == null) return null;
+        return (SkeletonKeySession)session.getAttribute(SkeletonKeySession.class.getName());
+
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java
new file mode 100755
index 0000000..9ac90aa
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthenticationMechanism.java
@@ -0,0 +1,48 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.api.ConfidentialPortManager;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import org.keycloak.RealmConfiguration;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.config.ManagedResourceConfig;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletKeycloakAuthenticationMechanism extends KeycloakAuthenticationMechanism {
+    protected ConfidentialPortManager portManager;
+
+    public ServletKeycloakAuthenticationMechanism(ResourceMetadata resourceMetadata, ManagedResourceConfig config, RealmConfiguration realmConfig, ConfidentialPortManager portManager) {
+        super(resourceMetadata, config, realmConfig);
+        this.portManager = portManager;
+    }
+
+    @Override
+    protected OAuthAuthenticator createOAuthAuthenticator(HttpServerExchange exchange) {
+        return new ServletOAuthAuthenticator(exchange, realmConfig, portManager);
+    }
+
+    @Override
+    protected void propagateBearer(HttpServerExchange exchange, SkeletonKeySession session) {
+        super.propagateBearer(exchange, session);
+        final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+        HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
+        req.setAttribute(SkeletonKeySession.class.getName(), session);
+    }
+
+    @Override
+    protected void propagateOauth(HttpServerExchange exchange, SkeletonKeySession skSession) {
+        super.propagateOauth(exchange, skSession);
+        final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+        HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
+        req.setAttribute(SkeletonKeySession.class.getName(), skSession);
+        HttpSession session = req.getSession(true);
+        session.setAttribute(SkeletonKeySession.class.getName(), skSession);
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java
new file mode 100755
index 0000000..754c7b4
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java
@@ -0,0 +1,23 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.api.ConfidentialPortManager;
+import org.keycloak.RealmConfiguration;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletOAuthAuthenticator extends OAuthAuthenticator {
+    protected ConfidentialPortManager portManager;
+
+    public ServletOAuthAuthenticator(HttpServerExchange exchange, RealmConfiguration realmInfo, ConfidentialPortManager portManager) {
+        super(exchange, realmInfo, -1);
+        this.portManager = portManager;
+    }
+
+    @Override
+    protected int sslRedirectPort() {
+        return portManager.getConfidentialPort(exchange);
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPropagateSessionHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPropagateSessionHandler.java
new file mode 100755
index 0000000..b8a25d3
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletPropagateSessionHandler.java
@@ -0,0 +1,63 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HandlerWrapper;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import org.jboss.logging.Logger;
+import org.keycloak.SkeletonKeySession;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletPropagateSessionHandler implements HttpHandler {
+
+    private static final Logger log = Logger.getLogger(ServletPropagateSessionHandler.class);
+
+    protected HttpHandler next;
+
+    protected ServletPropagateSessionHandler(HttpHandler next) {
+        this.next = next;
+    }
+
+    public static final HandlerWrapper WRAPPER = new HandlerWrapper() {
+        @Override
+        public HttpHandler wrap(HttpHandler handler) {
+            return new ServletPropagateSessionHandler(handler);
+        }
+    };
+
+    @Override
+    public void handleRequest(HttpServerExchange exchange) throws Exception {
+        log.info("handleRequest");
+        final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+        HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
+        SkeletonKeySession skSession = (SkeletonKeySession)req.getAttribute(SkeletonKeySession.class.getName());
+        if (skSession != null) {
+            log.info("skSession is in request");
+            next.handleRequest(exchange);
+            return;
+        }
+
+        HttpSession session = req.getSession(false);
+        if (session == null) {
+            log.info("http session was null, nothing to propagate");
+            next.handleRequest(exchange);
+            return;
+        }
+        skSession = (SkeletonKeySession)session.getAttribute(SkeletonKeySession.class.getName());
+        if (skSession == null) {
+            log.info("skSession not in http session, nothing to propagate");
+            next.handleRequest(exchange);
+            return;
+        }
+        log.info("propagating");
+        req.setAttribute(SkeletonKeySession.class.getName(), skSession);
+        exchange.putAttachment(KeycloakAuthenticationMechanism.SKELETON_KEY_SESSION_ATTACHMENT_KEY, skSession);
+        next.handleRequest(exchange);
+    }
+}
diff --git a/integration/undertow/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension b/integration/undertow/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension
new file mode 100755
index 0000000..fc0939d
--- /dev/null
+++ b/integration/undertow/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension
@@ -0,0 +1 @@
+org.keycloak.adapters.undertow.KeycloakServletExtension