keycloak-memoizeit

Merge pull request #811 from ssilvert/deploy-auth-in-subsys KEYCLOAK-795

11/6/2014 8:57:19 PM

Changes

.gitignore 3(+2 -1)

pom.xml 111(+70 -41)

Details

.gitignore 3(+2 -1)

diff --git a/.gitignore b/.gitignore
index 319769b..44612eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,10 +9,11 @@
 .settings
 .classpath
 
-
 # NetBeans #
 ############
+nbactions.xml
 nb-configuration.xml
+catalog.xml
 
 # Compiled source #
 ###################
diff --git a/distribution/appliance-dist/assembly.xml b/distribution/appliance-dist/assembly.xml
index 0e788d8..53de6bc 100755
--- a/distribution/appliance-dist/assembly.xml
+++ b/distribution/appliance-dist/assembly.xml
@@ -22,6 +22,8 @@
                 <exclude>**/*.sh</exclude>
                 <exclude>welcome-content/*</exclude>
                 <exclude>**/modules/system/layers/base/org/picketlink/**</exclude>
+                <exclude>**/modules/system/layers/base/org/jboss/as/cli/**</exclude>
+                <exclude>**/modules/system/layers/base/org/jboss/aesh/**</exclude>
             </excludes>
         </fileSet>
         <fileSet>
@@ -40,13 +42,6 @@
             </includes>
         </fileSet>
         <fileSet>
-            <directory>${project.build.directory}/unpacked/deployments</directory>
-            <outputDirectory>keycloak/standalone/deployments</outputDirectory>
-            <excludes>
-                <exclude>keycloak-ds.xml</exclude>
-            </excludes>
-        </fileSet>
-        <fileSet>
             <directory>${project.build.directory}/unpacked/deployments/auth-server.war/WEB-INF/classes/META-INF</directory>
             <outputDirectory>keycloak/standalone/configuration</outputDirectory>
             <includes>
diff --git a/distribution/appliance-dist/src/main/xslt/standalone.xsl b/distribution/appliance-dist/src/main/xslt/standalone.xsl
index 7b8ba53..3cb32a9 100755
--- a/distribution/appliance-dist/src/main/xslt/standalone.xsl
+++ b/distribution/appliance-dist/src/main/xslt/standalone.xsl
@@ -34,7 +34,12 @@
     <xsl:template match="node()[name(.)='profile']">
         <xsl:copy>
             <xsl:apply-templates select="node()|@*"/>
-            <subsystem xmlns="urn:jboss:domain:keycloak:1.0"/>
+            <subsystem xmlns="urn:jboss:domain:keycloak:1.0">
+                <auth-server name="main-auth-server">
+                    <enabled>true</enabled>
+                    <web-context>auth</web-context>
+                </auth-server>
+            </subsystem>
         </xsl:copy>
     </xsl:template>
 
@@ -56,7 +61,7 @@
 
 
     <!-- for some reason, Wildfly 8 final decided to turn off management-native which means jboss-as-maven-plugin no
-         longer works -->
+    longer works -->
     <xsl:template match="node()[name(.)='management-interfaces']">
         <xsl:copy>
             <xsl:apply-templates select="node()|@*"/>
@@ -67,14 +72,14 @@
     </xsl:template>
 
     <!-- for some reason, Wildfly 8 final decided to turn off management-native which means jboss-as-maven-plugin no
-         longer works -->
+    longer works -->
     <xsl:template match="node()[name(.)='socket-binding-group']">
         <xsl:copy>
             <xsl:apply-templates select="node()|@*"/>
             <socket-binding name="management-native" interface="management" port="9999"/>
         </xsl:copy>
     </xsl:template>
-
+    
     <xsl:template match="@*|node()">
         <xsl:copy>
             <xsl:apply-templates select="@*|node()" />
diff --git a/distribution/modules/build.xml b/distribution/modules/build.xml
index 7495cdc..e101928 100755
--- a/distribution/modules/build.xml
+++ b/distribution/modules/build.xml
@@ -51,6 +51,14 @@
             <maven-resource group="org.bouncycastle" artifact="bcprov-jdk16"/>
         </module-def>
 
+        <module-def name="org.jboss.aesh">
+            <maven-resource group="org.jboss.aesh" artifact="aesh"/>
+        </module-def>
+
+        <module-def name="org.jboss.as.cli">
+            <maven-resource group="org.wildfly.core" artifact="wildfly-cli"/>
+        </module-def>        
+
         <module-def name="org.keycloak.keycloak-core">
             <maven-resource group="org.keycloak" artifact="keycloak-core"/>
         </module-def>
diff --git a/distribution/modules/lib.xml b/distribution/modules/lib.xml
index 6e2f849..3d9438a 100755
--- a/distribution/modules/lib.xml
+++ b/distribution/modules/lib.xml
@@ -52,7 +52,12 @@
 
             <!-- Process the resource -->
             <resources/>
-
+            
+            <!-- Add keycloak version property to module xml -->
+            <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}"
+                     token="$${project.version}"
+                     value="${project.version}"/>
+            
             <!-- Some final cleanup -->
             <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
                 <replacetoken>
diff --git a/distribution/modules/pom.xml b/distribution/modules/pom.xml
index afc41ed..d0a9838 100755
--- a/distribution/modules/pom.xml
+++ b/distribution/modules/pom.xml
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <!--
-  ~ JBoss, Home of Professional Open Source.
-  ~ Copyright 2010, Red Hat, Inc., and individual contributors
-  ~ as indicated by the @author tags. See the copyright.txt file in the
-  ~ distribution for a full listing of individual contributors.
-  ~
-  ~ This is free software; you can redistribute it and/or modify it
-  ~ under the terms of the GNU Lesser General Public License as
-  ~ published by the Free Software Foundation; either version 2.1 of
-  ~ the License, or (at your option) any later version.
-  ~
-  ~ This software is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  ~ Lesser General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU Lesser General Public
-  ~ License along with this software; if not, write to the Free
-  ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-  ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-  -->
+~ JBoss, Home of Professional Open Source.
+~ Copyright 2010, Red Hat, Inc., and individual contributors
+~ as indicated by the @author tags. See the copyright.txt file in the
+~ distribution for a full listing of individual contributors.
+~
+~ This is free software; you can redistribute it and/or modify it
+~ under the terms of the GNU Lesser General Public License as
+~ published by the Free Software Foundation; either version 2.1 of
+~ the License, or (at your option) any later version.
+~
+~ This software is distributed in the hope that it will be useful,
+~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+~ Lesser General Public License for more details.
+~
+~ You should have received a copy of the GNU Lesser General Public
+~ License along with this software; if not, write to the Free
+~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+-->
 
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -84,6 +84,11 @@
             <artifactId>bcprov-jdk16</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.wildfly.core</groupId>
+            <artifactId>wildfly-cli</artifactId>
+            <version>${wildfly.core.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.picketlink</groupId>
             <artifactId>picketlink-common</artifactId>
         </dependency>
@@ -119,7 +124,10 @@
             <groupId>org.picketlink</groupId>
             <artifactId>picketlink-impl</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.jboss.aesh</groupId>
+            <artifactId>aesh</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
@@ -211,6 +219,31 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.keycloak</groupId>
+                                    <artifactId>keycloak-server</artifactId>
+                                    <version>${project.version}</version>
+                                    <type>war</type>
+                                    <overWrite>true</overWrite>
+                                    <outputDirectory>${project.build.directory}/modules/org/keycloak/keycloak-wildfly-subsystem/main/auth-server</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/distribution/modules/src/main/resources/modules/org/jboss/aesh/main/module.xml b/distribution/modules/src/main/resources/modules/org/jboss/aesh/main/module.xml
new file mode 100644
index 0000000..8789ef3
--- /dev/null
+++ b/distribution/modules/src/main/resources/modules/org/jboss/aesh/main/module.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2010, Red Hat, Inc., and individual contributors
+  ~ as indicated by the @author tags. See the copyright.txt file in the
+  ~ distribution for a full listing of individual contributors.
+  ~
+  ~ This is free software; you can redistribute it and/or modify it
+  ~ under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ This software is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  ~ Lesser General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public
+  ~ License along with this software; if not, write to the Free
+  ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  -->
+
+<module xmlns="urn:jboss:module:1.3" name="org.jboss.aesh">
+    <properties>
+        <property name="jboss.api" value="private"/>
+    </properties>
+
+    <resources>
+        <artifact name="org.jboss.aesh:aesh:0.33.12"/>
+    </resources>
+
+    <dependencies>
+        <module name="org.fusesource.jansi" />
+    </dependencies>
+</module>
diff --git a/distribution/modules/src/main/resources/modules/org/jboss/as/cli/main/module.xml b/distribution/modules/src/main/resources/modules/org/jboss/as/cli/main/module.xml
new file mode 100644
index 0000000..c46ff3a
--- /dev/null
+++ b/distribution/modules/src/main/resources/modules/org/jboss/as/cli/main/module.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2011, Red Hat, Inc., and individual contributors
+  ~ as indicated by the @author tags. See the copyright.txt file in the
+  ~ distribution for a full listing of individual contributors.
+  ~
+  ~ This is free software; you can redistribute it and/or modify it
+  ~ under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ This software is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  ~ Lesser General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public
+  ~ License along with this software; if not, write to the Free
+  ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  -->
+
+<module xmlns="urn:jboss:module:1.3" name="org.jboss.as.cli">
+    <properties>
+        <property name="jboss.api" value="private"/>
+        <property name="jboss.require-java-version" value="1.7"/>
+    </properties>
+
+    <main-class name="org.jboss.as.cli.CommandLineMain"/>
+
+    <resources>
+        <resource-root path="wildfly-cli-1.0.0.Alpha11-SNAPSHOT.jar"/>
+    </resources>
+
+    <dependencies>
+        <module name="org.jboss.aesh"/>
+        <module name="org.jboss.modules"/>
+        <module name="org.jboss.as.controller-client"/>
+        <module name="org.jboss.as.protocol"/>
+        <module name="org.wildfly.security.manager"/>
+        <module name="org.jboss.as.patching.cli" optional="true" services="import"/>
+        <module name="org.jboss.dmr"/>
+        <module name="org.jboss.logging"/>
+        <module name="org.jboss.logmanager" services="import"/>
+        <module name="org.jboss.remoting"/>
+        <module name="org.jboss.sasl"/>
+        <module name="org.jboss.staxmapper"/>
+        <module name="org.jboss.threads"/>
+        <module name="org.jboss.vfs"/>
+        <module name="org.picketbox" optional="true"/>
+        <module name="javax.api"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/modules/src/main/resources/modules/org/jboss/as/cli/main/wildfly-cli-1.0.0.Alpha11-SNAPSHOT.jar b/distribution/modules/src/main/resources/modules/org/jboss/as/cli/main/wildfly-cli-1.0.0.Alpha11-SNAPSHOT.jar
new file mode 100644
index 0000000..7e9aac2
Binary files /dev/null and b/distribution/modules/src/main/resources/modules/org/jboss/as/cli/main/wildfly-cli-1.0.0.Alpha11-SNAPSHOT.jar differ
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-subsystem/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-subsystem/main/module.xml
index 0bc0ec9..636909d 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-subsystem/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-subsystem/main/module.xml
@@ -2,7 +2,7 @@
 
 <!--
   ~ JBoss, Home of Professional Open Source.
-  ~ Copyright 2010, Red Hat, Inc., and individual contributors
+  ~ Copyright 2014, Red Hat, Inc., and individual contributors
   ~ as indicated by the @author tags. See the copyright.txt file in the
   ~ distribution for a full listing of individual contributors.
   ~
@@ -23,7 +23,13 @@
   -->
 
 <module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-subsystem">
+    <properties>
+        <property name="keycloak-version" value="${project.version}"/>
+        <property name="auth-server-exploded" value="false"/>
+    </properties>
+
     <resources>
+        <resource-root path="."/>
         <!-- Insert resources here -->
     </resources>
 
@@ -31,6 +37,7 @@
         <module name="javax.api"/>
         <module name="org.jboss.staxmapper"/>
         <module name="org.jboss.as.controller"/>
+        <module name="org.jboss.as.ee"/>
         <module name="org.jboss.as.server"/>
         <module name="org.jboss.modules"/>
         <module name="org.jboss.msc"/>
diff --git a/docbook/reference/en/en-US/modules/server-installation.xml b/docbook/reference/en/en-US/modules/server-installation.xml
index 1fae98c..74e5411 100755
--- a/docbook/reference/en/en-US/modules/server-installation.xml
+++ b/docbook/reference/en/en-US/modules/server-installation.xml
@@ -124,6 +124,14 @@ keycloak-war-dist-all-&project.version;/
             If you have Keycloak on JBoss AS 7.1.1 <link linkend="as7-specifics">these steps</link>.
         </para>
     </section>
+    <section id="subsystem_installation">
+        <title>Subsystem Installation</title>
+        <para>
+            For WildFly installations, the Keycloak server is not deployed from the /deployments directory.  Instead, the Keycloak 
+            subsystem module contains the Keycloak server and it is controlled by the subsystem.  If you are using the 
+            appliance install, this subsystem is already present and a Keycloak server is pre-defined in the subsytem declaration.
+        </para>
+    </section>
     <section>
         <title id="configure-server">Configuring the Server</title>
         <para>
@@ -749,4 +757,142 @@ keycloak-war-dist-all-&project.version;/
         </section>
 
     </section>
+    <section>
+        <title>Configuring Servers from the Subsystem</title>
+        <para>
+            If you are using WildFly, the Keycloak server is deployed and configured from the Keycloak subsystem.  This makes provisioning simpler in a domain environment.
+            It also allows you to create more than one Keycloak server instance inside a single WildFly instance.  And, you can upload providers, themes, and
+            server configurations without disturbing Keycloak's auth-server.war.
+        </para>
+        <section>
+            <title>Manually Creating A Server</title>
+            <para>
+                A Keycloak server can be declared by editing standalone.xml or domain.xml.
+            </para>
+<para>
+<programlisting><![CDATA[
+<server xmlns="urn:jboss:domain:1.4">
+
+  <profile>
+    <subsystem xmlns="urn:jboss:domain:keycloak:1.0">
+        <auth-server name="keycloak-1">
+            <enabled>true</enabled>
+            <web-context>auth</web-context>
+        </auth-server>
+        <auth-server name="keyclaok-2">
+            <enabled>false</enabled>
+            <web-context>auth2</web-context>
+        </auth-server>
+    </subsystem>
+  </profile>
+]]>
+</programlisting>
+</para>
+            <warning>
+                <para>
+                    If you create more than one Keycloak server, you will need to use CLI to fully configure each instance.  At the least, 
+                    you will need to run the <link linkend="uploading-extra-config">update-server-config</link> operation.
+                </para>
+            </warning>
+        </section>
+        <section>
+            <title>Using CLI and CLI GUI with the Keycloak Subsystem</title>
+            <para>
+                Servers can also be added/removed or enabled/disabled at runtime using the <ulink url="https://developer.jboss.org/wiki/CommandLineInterface">CLI</ulink> or 
+                <ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> tool.  These are tools that ship with WildFly and also with
+                the Keycloak Appliance installation.  See <ulink url="https://developer.jboss.org/wiki/CommandLineInterface">CLI</ulink> or 
+                <ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> documentation to learn more about how to start the tools,
+                issue commands, and create CLI scripts.
+            </para>
+<para>
+To start CLI with the Keycloak Appliance install:
+<programlisting><![CDATA[
+cd <APPLIANCE_INSTALL_DIR>/keycloak/bin
+./jboss-cli.sh --gui
+or
+./jboss.cli.bat --gui]]>
+</programlisting>
+<note>Your Keycloak server must be running to start in --gui mode.</note>
+</para>  
+            <section>
+                <title>Basic CLI Commands</title>
+<para>
+Command to add a server in CLI:
+<programlisting><![CDATA[
+/subsystem=keycloak/auth-server=my-auth-server/:add(web-context=my-auth, enabled=true)]]>
+</programlisting>
+Because "enabled=true", a new Keycloak server will be immediately deployed.  By default "enabled" is set to false.
+</para>  
+<para>
+Command to remove a server in CLI:
+<programlisting><![CDATA[
+/subsystem=keycloak/auth-server=my-auth-server/:remove]]>
+</programlisting>
+The Keycloak server will be immediately deleted and undeployed.
+</para>  
+<para>
+Command to enable or disable a server in CLI:
+<programlisting><![CDATA[
+/subsystem=keycloak/auth-server=foo/:write-attribute(name=enabled,value=true)]]>
+</programlisting>
+The Keycloak server will be immediately deployed or undeployed, but not deleted.
+</para>
+            </section>
+            <section id="uploading-extra-config">
+                <title>Uploading extra configuration using CLI</title>
+                <para>
+                    The WildFly Keycloak subsystem allows you to upload keycloak-server.json, provider jars, and theme jars to a Keycloak server instance.  The
+                    CLI operations for this are "update-server-config" and "add-provider".  You may use plain or CLI scripts for these operations.  The following
+                    examples are shown using <ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> for clarity.
+                </para>
+                <para>
+                    To use a new keycloak-server.json file for your server, find your server under the Keycloak subsystem.  Then right-click the server,
+                    select "update-server-config", and upload your file.
+                </para>
+            
+                <para>
+                    <imagedata fileref="images/update-server-config-select.png"/>
+                </para>
+                <para>
+                    <imagedata fileref="images/update-server-config-dialog.png"/>
+                </para>
+            
+                <warning>
+                    <para>
+                    If you use the update-server-config operation, you should delete or rename &lt;WILDFLY_HOME&gt;/standalone/configuration/keycloak-server.json.
+                    Otherwise, all Keycloak server instances will use this file instead of your uploaded file.
+                    </para>
+                </warning>
+                <para>
+                    To upload a new provider jar or theme jar to your server, find your server under the Keycloak subsystem.  Then right-click the server,
+                    select "add-provider", and upload your file.
+                </para>
+                <para>
+                    <imagedata fileref="images/add-provider-select.png"/>
+                </para>
+                <para>
+                    <imagedata fileref="images/add-provider-dialog.png"/>
+                </para>
+            </section>
+        </section>
+        <section>
+            <title>Adding a Keycloak server in Domain Mode</title>
+            <para>
+                In domain mode, you start the server with the "domain" command instead of the "standalone" command.  In this case, the Keycloak subsystem is
+                defined in domain/configuration/domain.xml instead of standalone/configuration.standalone.xml.  Inside domain.xml, you will see more than one
+                profile.  A Keycloak subsystem can be defined in zero or more of those profiles.
+            </para>
+            <para>
+                In the example below, a Keycloak server named "foo" is defined in the "full" profile.  The "full" profile is assigned to the "main-server-group".
+                Every WildFly instance that belongs to "main-server-group" will get an identically configured deployment of the "foo" Keycloak server.
+            </para>
+            <para>
+                All operations discussed earlier are valid for a Keycloak server in a domain.  You can enable/disable, upload new keyclaok-server.json, and add provider jars.
+                In the following example, any changes that are made to the "foo" server will be automatically propogated to every instance in "main-server-group".
+            </para>
+            <para>
+                <imagedata fileref="images/domain-mode.png"/>
+            </para>
+        </section>
+    </section>
 </chapter>
diff --git a/docbook/reference/en/images/add-provider-dialog.png b/docbook/reference/en/images/add-provider-dialog.png
new file mode 100644
index 0000000..ee1c44a
Binary files /dev/null and b/docbook/reference/en/images/add-provider-dialog.png differ
diff --git a/docbook/reference/en/images/add-provider-select.png b/docbook/reference/en/images/add-provider-select.png
new file mode 100644
index 0000000..c0f6766
Binary files /dev/null and b/docbook/reference/en/images/add-provider-select.png differ
diff --git a/docbook/reference/en/images/domain-mode.png b/docbook/reference/en/images/domain-mode.png
new file mode 100644
index 0000000..9d96bd0
Binary files /dev/null and b/docbook/reference/en/images/domain-mode.png differ
diff --git a/docbook/reference/en/images/update-server-config-dialog.png b/docbook/reference/en/images/update-server-config-dialog.png
new file mode 100644
index 0000000..9c52546
Binary files /dev/null and b/docbook/reference/en/images/update-server-config-dialog.png differ
diff --git a/docbook/reference/en/images/update-server-config-select.png b/docbook/reference/en/images/update-server-config-select.png
new file mode 100644
index 0000000..2af89df
Binary files /dev/null and b/docbook/reference/en/images/update-server-config-select.png differ
diff --git a/integration/wildfly-subsystem/pom.xml b/integration/wildfly-subsystem/pom.xml
index d776e60..ed0a337 100755
--- a/integration/wildfly-subsystem/pom.xml
+++ b/integration/wildfly-subsystem/pom.xml
@@ -42,11 +42,9 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.8.1</version>
                 <configuration>
                     <redirectTestOutputToFile>false</redirectTestOutputToFile>
                     <enableAssertions>true</enableAssertions>
-                    <argLine>-Xmx512m</argLine>
                     <systemProperties>
                         <property>
                             <name>jboss.home</name>
@@ -56,9 +54,8 @@
                     <includes>
                         <include>**/*TestCase.java</include>
                     </includes>
-                    <forkMode>once</forkMode>
                 </configuration>
-            </plugin>
+            </plugin>            
         </plugins>
     </build>
 
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AbstractAddOverlayHandler.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AbstractAddOverlayHandler.java
new file mode 100644
index 0000000..e9780a8
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AbstractAddOverlayHandler.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.extension.authserver;
+
+import java.util.Set;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.OperationStepHandler;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ProcessType;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT_OVERLAY;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.as.controller.registry.Resource;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+import static org.keycloak.subsystem.extension.authserver.AuthServerUtil.getHandler;
+
+/**
+ * Base class for operations that create overlays for an auth server.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public abstract class AbstractAddOverlayHandler implements OperationStepHandler {
+
+    protected static final String UPLOADED_FILE_OP_NAME = "uploaded-file-name";
+
+    protected static final SimpleAttributeDefinition BYTES_TO_UPLOAD
+            = new SimpleAttributeDefinitionBuilder("bytes-to-upload", ModelType.BYTES, false)
+            .setAllowExpression(false)
+            .setAllowNull(false)
+            .build();
+
+    @Override
+    public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
+        //System.out.println("*** execute operation ***");
+        //System.out.println(scrub(operation));
+
+        String uploadFileName = operation.get(UPLOADED_FILE_OP_NAME).asString();
+        String overlayPath = getOverlayPath(uploadFileName);
+        String overlayName = AuthServerUtil.getAuthServerName(operation) + "-keycloak-overlay";
+        PathAddress overlayAddress = PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT_OVERLAY, overlayName));
+
+        boolean isOverlayExists = isOverlayExists(context, overlayName, PathAddress.EMPTY_ADDRESS);
+        if (!isOverlayExists) {
+            addOverlay(context, overlayAddress);
+            if (!isHostController(context)) {
+                addDeploymentToOverlay(context, overlayAddress, AuthServerUtil.getDeploymentName(operation));
+            }
+        }
+
+        if (isHostController(context)) {
+            addOverlayToServerGroups(context, overlayAddress, operation, overlayName);
+        }
+
+        // There is no way to do an overwrite of content from here because it involves
+        // removing the overlay service in the runtime phase.  You have to remove
+        // the content in a seperate operation.
+        if (isOverlayExists && isContentExists(context, overlayAddress, overlayPath)) {
+            throw new OperationFailedException(pathExistsMessage(overlayAddress, overlayPath));
+        }
+
+        addContent(context, overlayAddress, operation.get(BYTES_TO_UPLOAD.getName()).asBytes(), overlayPath);
+
+        context.restartRequired();
+        context.completeStep(OperationContext.ResultHandler.NOOP_RESULT_HANDLER);
+    }
+
+    private boolean isHostController(OperationContext context) {
+        return context.getProcessType() == ProcessType.HOST_CONTROLLER;
+    }
+
+    private String pathExistsMessage(PathAddress overlayAddress, String overlayPath) {
+        PathAddress contentAddress = overlayAddress.append("content", overlayPath);
+        String msg = "Can not update overlay. ";
+        msg += "First remove the overlay with CLI using the following command with the content path in double quotes:  ";
+        msg += contentAddress.toCLIStyleString() + ":remove";
+        return msg;
+    }
+
+    private boolean isOverlayExists(OperationContext context, String overlayName, PathAddress address) {
+        Resource resource = context.readResourceFromRoot(address);
+        return resource.getChildrenNames("deployment-overlay").contains(overlayName);
+    }
+
+    private boolean isContentExists(OperationContext context, PathAddress overlayAddress, String overlayPath) {
+        Resource resource = context.readResourceFromRoot(overlayAddress);
+        return resource.getChildrenNames("content").contains(overlayPath);
+    }
+
+    private void addOverlay(OperationContext context, PathAddress overlayAddress) {
+        ModelNode op = Util.createAddOperation(overlayAddress);
+        doAddStep(context, overlayAddress, op);
+    }
+
+    private void addDeploymentToOverlay(OperationContext context, PathAddress overlayAddress, String deploymentName) {
+        PathAddress deploymentAddress = overlayAddress.append("deployment", deploymentName);
+        ModelNode op = Util.createAddOperation(deploymentAddress);
+        doAddStep(context, deploymentAddress, op);
+    }
+
+    // only call this if context.getProcessType() == ProcessType.HOST_CONTROLLER
+    private void addOverlayToServerGroups(OperationContext context, PathAddress overlayAddress, ModelNode operation, String overlayName) {
+        String myProfile = findMyProfile(operation);
+        for (String serverGroup : getServerGroupNames(context)) {
+            PathAddress address = PathAddress.pathAddress("server-group", serverGroup);
+            ModelNode serverGroupModel = context.readResourceFromRoot(address).getModel();
+            if (serverGroupModel.get("profile").asString().equals(myProfile)) {
+                PathAddress serverGroupOverlayAddress = address.append(overlayAddress);
+                boolean isOverlayExists = isOverlayExists(context, overlayName, address);
+                if (!isOverlayExists) {
+                    addOverlay(context, serverGroupOverlayAddress);
+                    addDeploymentToOverlay(context, serverGroupOverlayAddress, AuthServerUtil.getDeploymentName(operation));
+                }
+            }
+        }
+    }
+
+    // only call this if context.getProcessType() == ProcessType.HOST_CONTROLLER
+    private String findMyProfile(ModelNode operation) {
+        PathAddress address = PathAddress.pathAddress(operation.get("address"));
+        return address.getElement(0).getValue();
+    }
+
+    private Set<String> getServerGroupNames(OperationContext context) {
+        return context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS).getChildrenNames("server-group");
+    }
+
+    private void addContent(OperationContext context, PathAddress overlayAddress, byte[] bytes, String overlayPath) throws OperationFailedException {
+        PathAddress contentAddress = overlayAddress.append("content", overlayPath);
+        ModelNode op = Util.createAddOperation(contentAddress);
+
+        ModelNode content = new ModelNode();
+        content.get("bytes").set(bytes);
+        op.get("content").set(content);
+
+        doAddStep(context, contentAddress, op);
+    }
+
+    private void doAddStep(OperationContext context, PathAddress address, ModelNode operation) {
+        //System.out.println("**** Adding Add Step ****");
+        //System.out.println(scrub(operation).toString());
+        context.addStep(operation, getHandler(context, address, ADD), OperationContext.Stage.MODEL);
+    }
+
+    // used for debugging
+    private ModelNode scrub(ModelNode op) {
+        ModelNode scrubbed = op.clone();
+        if (scrubbed.has("content")) {
+            scrubbed.get("content").set("BYTES REMOVED FOR DISPLAY");
+        }
+        if (scrubbed.has("bytes-to-upload")) {
+            scrubbed.get("bytes-to-upload").set("BYTES REMOVED FOR DISPLAY");
+        }
+        return scrubbed;
+    }
+
+    /**
+     * Get the WAR path where the overlay will live.
+     *
+     * @param file The name of the file being uploaded.
+     * @return The overlay path as a String.
+     */
+    abstract String getOverlayPath(String fileName);
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AddProviderHandler.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AddProviderHandler.java
new file mode 100644
index 0000000..f661891
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AddProviderHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.extension.authserver;
+
+import java.io.File;
+import org.jboss.as.controller.OperationDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+/**
+ * Operation to add a provider jar to WEB-INF/lib.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class AddProviderHandler extends AbstractAddOverlayHandler {
+
+    public static final String OP = "add-provider";
+
+    public static final AddProviderHandler INSTANCE = new AddProviderHandler();
+
+    protected static final SimpleAttributeDefinition UPLOADED_FILE_NAME =
+            new SimpleAttributeDefinitionBuilder(UPLOADED_FILE_OP_NAME, ModelType.STRING, false)
+            .setAllowExpression(false)
+            .setAllowNull(false)
+            .setDefaultValue(new ModelNode().set("myprovider.jar"))
+            .build();
+
+    public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver)
+            .addParameter(BYTES_TO_UPLOAD)
+            .addParameter(UPLOADED_FILE_NAME)
+            .build();
+
+    @Override
+    String getOverlayPath(String fileName) {
+        if (!fileName.toLowerCase().endsWith(".jar")) {
+            throw new IllegalArgumentException("Uploaded file name must end with .jar");
+        }
+        return "/WEB-INF/lib/" + fileName;
+    }
+
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerAddHandler.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerAddHandler.java
new file mode 100755
index 0000000..fd3fcdd
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerAddHandler.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.extension.authserver;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.as.controller.registry.Resource;
+import org.keycloak.subsystem.extension.KeycloakAdapterConfigService;
+
+/**
+ * Add an auth server.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public final class AuthServerAddHandler extends AbstractAddStepHandler {
+
+    public static AuthServerAddHandler INSTANCE = new AuthServerAddHandler();
+
+    private AuthServerAddHandler() {
+    }
+
+    @Override
+    protected void populateModel(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
+        // TODO: localize exception. get id number
+        if (!operation.get(OP).asString().equals(ADD)) {
+            throw new OperationFailedException("Unexpected operation for add Auth Server. operation=" + operation.toString());
+        }
+
+        ModelNode model = resource.getModel();
+        for (AttributeDefinition attr : AuthServerDefinition.ALL_ATTRIBUTES) {
+            attr.validateAndSet(operation, model);
+        }
+
+        // returns early if on domain controller
+        if (!requiresRuntime(context)) return;
+
+        // don't want to try to start server on host controller
+        if (!context.isNormalServer()) return;
+
+
+        ModelNode webContextNode = model.get(AuthServerDefinition.WEB_CONTEXT.getName());
+        if (!webContextNode.isDefined()) webContextNode = AuthServerDefinition.WEB_CONTEXT.getDefaultValue();
+        String webContext = webContextNode.asString();
+
+        ModelNode isEnabled = model.get("enabled");
+        boolean enabled = isEnabled.isDefined() && isEnabled.asBoolean();
+
+        AuthServerUtil authServerUtil = new AuthServerUtil(operation);
+        authServerUtil.addStepToUploadAuthServer(context, enabled);
+        KeycloakAdapterConfigService.INSTANCE.addServerDeployment(authServerUtil.getDeploymentName(), webContext);
+    }
+
+    @Override
+    protected boolean requiresRuntimeVerification() {
+        return false;
+    }
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerDefinition.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerDefinition.java
new file mode 100755
index 0000000..ff43a6f
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerDefinition.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.extension.authserver;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
+import org.jboss.as.controller.operations.validation.ParameterValidator;
+import org.jboss.as.controller.registry.OperationEntry;
+import org.keycloak.subsystem.extension.KeycloakAdapterConfigService;
+import org.keycloak.subsystem.extension.KeycloakExtension;
+
+/**
+ * Defines attributes and operations for an Auth Server
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class AuthServerDefinition extends SimpleResourceDefinition {
+
+    public static final String TAG_NAME = "auth-server";
+
+    protected static final SimpleAttributeDefinition ENABLED =
+            new SimpleAttributeDefinitionBuilder("enabled", ModelType.BOOLEAN, true)
+            .setXmlName("enabled")
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode(false))
+            .setRestartAllServices()
+            .build();
+
+    protected static final SimpleAttributeDefinition WEB_CONTEXT =
+            new SimpleAttributeDefinitionBuilder("web-context", ModelType.STRING, true)
+            .setXmlName("web-context")
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode("auth"))
+            .setValidator(new WebContextValidator())
+            .setRestartAllServices()
+            .build();
+
+    protected static final ResourceDescriptionResolver rscDescriptionResolver = KeycloakExtension.getResourceDescriptionResolver(TAG_NAME);
+
+    public static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
+    static {
+        ALL_ATTRIBUTES.add(ENABLED);
+        ALL_ATTRIBUTES.add(WEB_CONTEXT);
+    }
+
+    private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
+    static {
+        for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
+            DEFINITION_LOOKUP.put(def.getXmlName(), def);
+        }
+    }
+
+    private static AuthServerWriteAttributeHandler attrHandler = new AuthServerWriteAttributeHandler(ALL_ATTRIBUTES);
+
+    public AuthServerDefinition() {
+        super(PathElement.pathElement(TAG_NAME),
+                rscDescriptionResolver,
+                AuthServerAddHandler.INSTANCE,
+                AuthServerRemoveHandler.INSTANCE,
+                null,
+                OperationEntry.Flag.RESTART_ALL_SERVICES);
+    }
+
+    @Override
+    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+        super.registerOperations(resourceRegistration);
+        resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+        resourceRegistration.registerOperationHandler(AddProviderHandler.DEFINITION, AddProviderHandler.INSTANCE);
+        resourceRegistration.registerOperationHandler(OverlayKeycloakServerJsonHandler.DEFINITION, OverlayKeycloakServerJsonHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+        for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
+            resourceRegistration.registerReadWriteAttribute(attrDef, null, attrHandler);
+        }
+    }
+
+    public static SimpleAttributeDefinition lookup(String name) {
+        return DEFINITION_LOOKUP.get(name);
+    }
+
+    private static class WebContextValidator implements ParameterValidator {
+
+        @Override
+        public void validateParameter(String paramName, ModelNode value) throws OperationFailedException {
+            String strValue = value.asString();
+            if (KeycloakAdapterConfigService.INSTANCE.isWebContextUsed(strValue)) {
+                throw new OperationFailedException("Can not set web-context to '" + strValue + "'. web-context must be unique among all deployments.");
+            }
+        }
+
+        @Override
+        public void validateResolvedParameter(String paramName, ModelNode value) throws OperationFailedException {
+            String strValue = value.asString();
+            if (KeycloakAdapterConfigService.INSTANCE.isWebContextUsed(strValue)) {
+                throw new OperationFailedException("Can not set web-context to '" + strValue + "'. web-context must be unique among all deployments.");
+            }
+        }
+
+    }
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerRemoveHandler.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerRemoveHandler.java
new file mode 100644
index 0000000..f84e45f
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerRemoveHandler.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.extension.authserver;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.OperationStepHandler;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.dmr.ModelNode;
+import org.keycloak.subsystem.extension.KeycloakAdapterConfigService;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
+import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
+
+/**
+ * Remove an auth-server from a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public final class AuthServerRemoveHandler extends AbstractRemoveStepHandler {
+
+    public static AuthServerRemoveHandler INSTANCE = new AuthServerRemoveHandler();
+
+    private AuthServerRemoveHandler() {}
+
+    @Override
+    protected void performRemove(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+        String deploymentName = AuthServerUtil.getDeploymentName(operation);
+        KeycloakAdapterConfigService.INSTANCE.removeServerDeployment(deploymentName);
+
+        if (requiresRuntime(context)) { // don't do this on a domain controller
+            addStepToRemoveAuthServer(context, deploymentName);
+        }
+
+        super.performRemove(context, operation, model);
+    }
+
+    private void addStepToRemoveAuthServer(OperationContext context, String deploymentName) {
+        PathAddress deploymentAddress = PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT, deploymentName));
+        ModelNode op = Util.createOperation(REMOVE, deploymentAddress);
+        context.addStep(op, getRemoveHandler(context, deploymentAddress), OperationContext.Stage.MODEL);
+    }
+
+    private OperationStepHandler getRemoveHandler(OperationContext context, PathAddress address) {
+        ImmutableManagementResourceRegistration rootResourceRegistration = context.getRootResourceRegistration();
+        return rootResourceRegistration.getOperationHandler(address, REMOVE);
+    }
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerUtil.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerUtil.java
new file mode 100644
index 0000000..d4085c9
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerUtil.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.extension.authserver;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.OperationStepHandler;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ARCHIVE;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOY;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ENABLED;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PERSISTENT;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REDEPLOY;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UNDEPLOY;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.URL;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoadException;
+
+/**
+ * Utility methods that help assemble and start an auth server.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class AuthServerUtil {
+
+    private static final ModuleIdentifier KEYCLOAK_SUBSYSTEM = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-subsystem");
+
+    private final String authServerName;
+    private final PathAddress pathAddress;
+    private final String deploymentName;
+
+    //private String overlayName;
+    private final Module subsysModule;
+    private final String keycloakVersion;
+    private final boolean isAuthServerExploded;
+
+    //private File overlaysDir;
+    private final URI authServerUri;
+    //private URL serverConfig = null;
+    //private Set<URL> spiUrls = new HashSet<URL>();
+
+    AuthServerUtil(ModelNode operation) {
+        this.authServerName = getAuthServerName(operation);
+        this.pathAddress = getPathAddress(operation);
+        this.deploymentName = getDeploymentName(operation);
+        this.subsysModule = findSubsysModule();
+        this.keycloakVersion = subsysModule.getProperty("keycloak-version");
+        this.isAuthServerExploded = Boolean.parseBoolean(subsysModule.getProperty("auth-server-exploded"));
+        this.authServerUri = findAuthServerUri();
+    }
+
+    String getDeploymentName() {
+        return this.deploymentName;
+    }
+
+    private Module findSubsysModule() {
+        try {
+            return Module.getModuleFromCallerModuleLoader(KEYCLOAK_SUBSYSTEM);
+        } catch (ModuleLoadException e) {
+            throw new IllegalStateException("Can't find Keycloak subsystem.", e);
+        }
+    }
+
+    private URI findAuthServerUri() throws IllegalStateException {
+        try {
+            URL subsysJar = this.subsysModule.getExportedResource("keycloak-wildfly-subsystem-" + this.keycloakVersion + ".jar");
+            File subsysDir = new File(subsysJar.toURI()).getParentFile();
+            File authServerDir = new File(subsysDir, "auth-server");
+            if (this.isAuthServerExploded) {
+                return authServerDir.toURI();
+            } else {
+                return new File(authServerDir, "keycloak-server-" + keycloakVersion + ".war").toURI();
+            }
+        } catch (URISyntaxException e) {
+            throw new IllegalStateException(e);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    void addStepToUploadAuthServer(OperationContext context, boolean isEnabled) throws OperationFailedException {
+        PathAddress deploymentAddress = deploymentAddress();
+        ModelNode op = Util.createOperation(ADD, deploymentAddress);
+        op.get(ENABLED).set(isEnabled);
+        op.get(PERSISTENT).set(false); // prevents writing this deployment out to standalone.xml
+
+        if (authServerUri == null) {
+            throw new OperationFailedException("Keycloak Auth Server WAR not found in keycloak-wildfly-subsystem module");
+        }
+
+        op.get(CONTENT).add(makeContentItem());
+
+        context.addStep(op, getHandler(context, deploymentAddress, ADD), OperationContext.Stage.MODEL);
+    }
+
+    private ModelNode makeContentItem() throws OperationFailedException {
+        ModelNode contentItem = new ModelNode();
+
+        if (this.isAuthServerExploded) {
+            String urlString = new File(authServerUri).getAbsolutePath();
+            contentItem.get(PATH).set(urlString);
+            contentItem.get(ARCHIVE).set(false);
+        } else {
+            String urlString = authServerUri.toString();
+            contentItem.get(URL).set(urlString);
+        }
+
+        return contentItem;
+    }
+
+    void addStepToRedeployAuthServer(OperationContext context) {
+        addDeploymentAction(context, REDEPLOY);
+    }
+
+    void addStepToUndeployAuthServer(OperationContext context) {
+        addDeploymentAction(context, UNDEPLOY);
+    }
+
+    void addStepToDeployAuthServer(OperationContext context) {
+        addDeploymentAction(context, DEPLOY);
+    }
+
+    private void addDeploymentAction(OperationContext context, String operation) {
+        PathAddress deploymentAddress = deploymentAddress();
+        ModelNode op = Util.createOperation(operation, deploymentAddress);
+        op.get(RUNTIME_NAME).set(deploymentName);
+        context.addStep(op, getHandler(context, deploymentAddress, operation), OperationContext.Stage.MODEL);
+    }
+
+    private PathAddress deploymentAddress() {
+        return PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT, deploymentName));
+    }
+
+    static OperationStepHandler getHandler(OperationContext context, PathAddress address, String opName) {
+        ImmutableManagementResourceRegistration rootResourceRegistration = context.getRootResourceRegistration();
+        return rootResourceRegistration.getOperationHandler(address, opName);
+    }
+
+    static String getDeploymentName(ModelNode operation) {
+        String deploymentName = Util.getNameFromAddress(operation.get(ADDRESS));
+        if (!deploymentName.toLowerCase().endsWith(".war")) {
+            deploymentName += ".war";
+        }
+
+        return deploymentName;
+    }
+
+    static String getAuthServerName(ModelNode operation) {
+        PathAddress pathAddr = getPathAddress(operation);
+        return pathAddr.getElement(pathAddr.size() - 1).getValue();
+    }
+
+    static PathAddress getPathAddress(ModelNode operation) {
+        return PathAddress.pathAddress(operation.get(ADDRESS));
+    }
+
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerWriteAttributeHandler.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerWriteAttributeHandler.java
new file mode 100755
index 0000000..24decfc
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/AuthServerWriteAttributeHandler.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.extension.authserver;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+
+import java.util.List;
+import org.jboss.as.controller.ModelOnlyWriteAttributeHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.registry.Resource;
+import org.jboss.dmr.ModelNode;
+import org.keycloak.subsystem.extension.KeycloakAdapterConfigService;
+
+/**
+ * Update an attribute on an Auth Server.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class AuthServerWriteAttributeHandler extends ModelOnlyWriteAttributeHandler { //extends ReloadRequiredWriteAttributeHandler {
+
+    public AuthServerWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
+        this(definitions.toArray(new AttributeDefinition[definitions.size()]));
+    }
+
+    public AuthServerWriteAttributeHandler(AttributeDefinition... definitions) {
+        super(definitions);
+    }
+
+    @Override
+    protected void finishModelStage(OperationContext context, ModelNode operation, String attributeName, ModelNode newValue, ModelNode oldValue, Resource model) throws OperationFailedException {
+        if (!context.isNormalServer() || attribNotChanging(attributeName, newValue, oldValue)) {
+            super.finishModelStage(context, operation, attributeName, newValue, oldValue, model);
+            return;
+        }
+
+        AuthServerUtil authServerUtil = new AuthServerUtil(operation);
+        boolean isEnabled = isEnabled(model); // is server currently enabled?
+
+        if (attributeName.equals(AuthServerDefinition.WEB_CONTEXT.getName())) {
+            String deploymentName = AuthServerUtil.getDeploymentName(operation);
+            KeycloakAdapterConfigService.INSTANCE.removeServerDeployment(deploymentName);
+            KeycloakAdapterConfigService.INSTANCE.addServerDeployment(deploymentName, newValue.asString());
+            if (isEnabled) {
+                authServerUtil.addStepToRedeployAuthServer(context);
+            }
+        }
+
+        if (attributeName.equals(AuthServerDefinition.ENABLED.getName())) {
+            if (!isEnabled) { // we are disabling
+                authServerUtil.addStepToUndeployAuthServer(context);
+            } else { // we are enabling
+                authServerUtil.addStepToDeployAuthServer(context);
+            }
+        }
+
+        super.finishModelStage(context, operation, attributeName, newValue, oldValue, model);
+    }
+
+    // Is auth server currently enabled?
+    private boolean isEnabled(Resource model) {
+        ModelNode authServer = model.getModel();
+        ModelNode isEnabled = authServer.get(AuthServerDefinition.ENABLED.getName());
+        if (!isEnabled.isDefined()) isEnabled = AuthServerDefinition.ENABLED.getDefaultValue();
+        return isEnabled.asBoolean();
+    }
+
+    private boolean attribNotChanging(String attributeName, ModelNode newValue, ModelNode oldValue) {
+        SimpleAttributeDefinition attribDef = AuthServerDefinition.lookup(attributeName);
+        if (!oldValue.isDefined()) oldValue = attribDef.getDefaultValue();
+        if (!newValue.isDefined()) newValue = attribDef.getDefaultValue();
+        return newValue.equals(oldValue);
+    }
+
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/OverlayKeycloakServerJsonHandler.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/OverlayKeycloakServerJsonHandler.java
new file mode 100644
index 0000000..485c37d
--- /dev/null
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/authserver/OverlayKeycloakServerJsonHandler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.extension.authserver;
+
+import org.jboss.as.controller.OperationDefinition;
+import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
+
+/**
+ * Operation to overlay keycloak-server.json.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class OverlayKeycloakServerJsonHandler extends AbstractAddOverlayHandler {
+
+    public static final String OP = "update-server-config";
+
+    public static final OverlayKeycloakServerJsonHandler INSTANCE = new OverlayKeycloakServerJsonHandler();
+
+    public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver)
+            .addParameter(BYTES_TO_UPLOAD)
+            .build();
+
+    @Override
+    String getOverlayPath(String fileName) {
+        return "/WEB-INF/classes/META-INF/keycloak-server.json";
+    }
+
+}
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
index 55e47eb..ea26666 100755
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
@@ -31,6 +31,10 @@ import org.keycloak.subsystem.logging.KeycloakLogger;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import org.jboss.as.ee.component.EEModuleDescription;
+import org.jboss.as.server.deployment.Attachments;
+import org.jboss.as.server.deployment.MountedDeploymentOverlay;
 
 /**
  * Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension.
@@ -45,14 +49,14 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
     // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
     public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
 
-    public static final Phase PHASE = Phase.INSTALL;
-    // Seems wise to have this run after INSTALL_WAR_DEPLOYMENT
-    public static final int PRIORITY = Phase.INSTALL_WAR_DEPLOYMENT - 1;
+    public static final Phase PHASE = Phase.POST_MODULE;
+    // This needs to run just before bean validator factory
+    public static final int PRIORITY = Phase.POST_MODULE_VALIDATOR_FACTORY - 1;
 
     // not sure if we need this yet, keeping here just in case
     protected void addSecurityDomain(DeploymentUnit deploymentUnit, KeycloakAdapterConfigService service) {
         String deploymentName = deploymentUnit.getName();
-        if (!service.isKeycloakDeployment(deploymentName)) {
+        if (!service.isSecureDeployment(deploymentName)) {
             return;
         }
         WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
@@ -75,11 +79,16 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
         String deploymentName = deploymentUnit.getName();
         KeycloakAdapterConfigService service = KeycloakAdapterConfigService.find(phaseContext.getServiceRegistry());
         //log.info("********* CHECK KEYCLOAK DEPLOYMENT: " + deploymentName);
-        if (service.isKeycloakDeployment(deploymentName)) {
-
+        if (service.isSecureDeployment(deploymentName)) {
             addKeycloakAuthData(phaseContext, deploymentName, service);
         }
 
+        if (service.isKeycloakServerDeployment(deploymentName)) {
+            final EEModuleDescription description = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
+            String webContext = service.getWebContext(deploymentName);
+            if (webContext == null) throw new DeploymentUnitProcessingException("Can't determine web context/module for Keycloak Auth Server");
+            description.setModuleName(webContext);
+        }
         // FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
 
         // todo notsure if we need this
@@ -99,6 +108,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
             webMetaData = new JBossWebMetaData();
             warMetaData.setMergedJBossWebMetaData(webMetaData);
         }
+
         LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
         if (loginConfig == null) {
             loginConfig = new LoginConfigMetaData();
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigService.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigService.java
index eb820fc..868f585 100755
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigService.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigService.java
@@ -51,7 +51,12 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
     public static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
 
     private Map<String, ModelNode> realms = new HashMap<String, ModelNode>();
-    private Map<String, ModelNode> deployments = new HashMap<String, ModelNode>();
+
+    // keycloak-secured deployments
+    private Map<String, ModelNode> secureDeployments = new HashMap<String, ModelNode>();
+
+    // key=auth-server deployment name; value=web-context
+    private Map<String, String> webContexts = new HashMap<String, String>();
 
     private KeycloakAdapterConfigService() {
 
@@ -72,6 +77,22 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
         return this;
     }
 
+    public void addServerDeployment(String deploymentName, String webContext) {
+        this.webContexts.put(deploymentName, webContext);
+    }
+
+    public String getWebContext(String deploymentName) {
+        return webContexts.get(deploymentName);
+    }
+
+    public void removeServerDeployment(String deploymentName) {
+        this.webContexts.remove(deploymentName);
+    }
+
+    public boolean isWebContextUsed(String webContext) {
+        return webContexts.containsValue(webContext);
+    }
+
     public void addRealm(ModelNode operation, ModelNode model) {
         this.realms.put(realmNameFromOp(operation), model.clone());
     }
@@ -87,16 +108,16 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
 
     public void addSecureDeployment(ModelNode operation, ModelNode model) {
         ModelNode deployment = model.clone();
-        this.deployments.put(deploymentNameFromOp(operation), deployment);
+        this.secureDeployments.put(deploymentNameFromOp(operation), deployment);
     }
 
     public void updateSecureDeployment(ModelNode operation, String attrName, ModelNode resolvedValue) {
-        ModelNode deployment = this.deployments.get(deploymentNameFromOp(operation));
+        ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
         deployment.get(attrName).set(resolvedValue);
     }
 
     public void removeSecureDeployment(ModelNode operation) {
-        this.deployments.remove(deploymentNameFromOp(operation));
+        this.secureDeployments.remove(deploymentNameFromOp(operation));
     }
 
     public void addCredential(ModelNode operation, ModelNode model) {
@@ -108,7 +129,7 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
         String credentialName = credentialNameFromOp(operation);
         credentials.get(credentialName).set(model.get("value").asString());
 
-        ModelNode deployment = this.deployments.get(deploymentNameFromOp(operation));
+        ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
         deployment.get(CREDENTIALS_JSON_NAME).set(credentials);
     }
 
@@ -133,7 +154,7 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
     }
 
     private ModelNode credentialsFromOp(ModelNode operation) {
-        ModelNode deployment = this.deployments.get(deploymentNameFromOp(operation));
+        ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
         return deployment.get(CREDENTIALS_JSON_NAME);
     }
 
@@ -164,13 +185,13 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
     }
 
     public String getRealmName(String deploymentName) {
-        ModelNode deployment = this.deployments.get(deploymentName);
+        ModelNode deployment = this.secureDeployments.get(deploymentName);
         return deployment.get(RealmDefinition.TAG_NAME).asString();
 
     }
 
     public String getJSON(String deploymentName) {
-        ModelNode deployment = this.deployments.get(deploymentName);
+        ModelNode deployment = this.secureDeployments.get(deploymentName);
         String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
         ModelNode realm = this.realms.get(realmName);
 
@@ -193,10 +214,14 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
         }
     }
 
-    public boolean isKeycloakDeployment(String deploymentName) {
+    public boolean isSecureDeployment(String deploymentName) {
         //log.info("********* CHECK KEYCLOAK DEPLOYMENT: deployments.size()" + deployments.size());
 
-        return this.deployments.containsKey(deploymentName);
+        return this.secureDeployments.containsKey(deploymentName);
+    }
+
+    public boolean isKeycloakServerDeployment(String deploymentName) {
+        return this.webContexts.containsKey(deploymentName);
     }
 
     static KeycloakAdapterConfigService find(ServiceRegistry registry) {
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakDependencyProcessor.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakDependencyProcessor.java
index 1283a2f..50f0bc0 100755
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakDependencyProcessor.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakDependencyProcessor.java
@@ -44,6 +44,10 @@ public class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
     @Override
     public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
         final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+
+        String deploymentName = deploymentUnit.getName();
+        KeycloakAdapterConfigService service = KeycloakAdapterConfigService.find(phaseContext.getServiceRegistry());
+
         addModules(deploymentUnit);
     }
 
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakExtension.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakExtension.java
index 32837aa..1816ad2 100755
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakExtension.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakExtension.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.subsystem.extension;
 
+import org.keycloak.subsystem.extension.authserver.AuthServerDefinition;
 import org.jboss.as.controller.Extension;
 import org.jboss.as.controller.ExtensionContext;
 import org.jboss.as.controller.PathElement;
@@ -46,11 +47,12 @@ public class KeycloakExtension implements Extension {
     private static final int MANAGEMENT_API_MICRO_VERSION = 0;
     protected static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
     private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
+    static final AuthServerDefinition AUTH_SERVER_DEFINITION = new AuthServerDefinition();
     static final RealmDefinition REALM_DEFINITION = new RealmDefinition();
     static final SecureDeploymentDefinition SECURE_DEPLOYMENT_DEFINITION = new SecureDeploymentDefinition();
     static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
 
-    static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
+    public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
         StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
         for (String kp : keyPrefix) {
             prefix.append('.').append(kp);
@@ -76,7 +78,8 @@ public class KeycloakExtension implements Extension {
                 MANAGEMENT_API_MINOR_VERSION, MANAGEMENT_API_MICRO_VERSION);
 
         ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
-        ManagementResourceRegistration realmRegistration = registration.registerSubModel(REALM_DEFINITION);
+        registration.registerSubModel(AUTH_SERVER_DEFINITION);
+        registration.registerSubModel(REALM_DEFINITION);
         ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
         secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
 
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemAdd.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemAdd.java
index c8f2e1d..00fc6c5 100755
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemAdd.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemAdd.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.subsystem.extension;
 
+
 import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
 import org.jboss.as.controller.OperationContext;
 import org.jboss.as.controller.OperationFailedException;
@@ -27,6 +28,7 @@ import org.jboss.dmr.ModelNode;
 import org.jboss.msc.service.ServiceController;
 
 import java.util.List;
+import org.jboss.as.controller.registry.Resource;
 
 /**
  * The Keycloak subsystem add update handler.
@@ -38,8 +40,10 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
     static final KeycloakSubsystemAdd INSTANCE = new KeycloakSubsystemAdd();
 
     @Override
-    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
-        model.setEmptyObject();
+    protected void populateModel(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
+        resource.getModel().setEmptyObject();
+
+
     }
 
     @Override
@@ -49,9 +53,9 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
             protected void execute(DeploymentProcessorTarget processorTarget) {
                 processorTarget.addDeploymentProcessor(KeycloakExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, 0, new KeycloakDependencyProcessor());
                 processorTarget.addDeploymentProcessor(KeycloakExtension.SUBSYSTEM_NAME,
-                                                       KeycloakAdapterConfigDeploymentProcessor.PHASE,
-                                                       KeycloakAdapterConfigDeploymentProcessor.PRIORITY,
-                                                       new KeycloakAdapterConfigDeploymentProcessor());
+                        KeycloakAdapterConfigDeploymentProcessor.PHASE,
+                        KeycloakAdapterConfigDeploymentProcessor.PRIORITY,
+                        new KeycloakAdapterConfigDeploymentProcessor());
             }
         }, OperationContext.Stage.RUNTIME);
     }
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemParser.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemParser.java
index 71b3e45..3cb5fad 100755
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemParser.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemParser.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.subsystem.extension;
 
+import org.keycloak.subsystem.extension.authserver.AuthServerDefinition;
 import org.jboss.as.controller.AttributeDefinition;
 import org.jboss.as.controller.PathAddress;
 import org.jboss.as.controller.PathElement;
@@ -58,6 +59,8 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
             }
             else if (reader.getLocalName().equals(SecureDeploymentDefinition.TAG_NAME)) {
                 readDeployment(reader, list);
+            } else if (reader.getLocalName().equals(AuthServerDefinition.TAG_NAME)) {
+                readAuthServer(reader, list);
             }
         }
     }
@@ -67,6 +70,24 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
         return reader.nextTag();
     }
 
+    private void readAuthServer(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
+        String authServerName = readNameAttribute(reader);
+        ModelNode addAuthServer = new ModelNode();
+        addAuthServer.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+        PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+                                                   PathElement.pathElement(AuthServerDefinition.TAG_NAME, authServerName));
+        addAuthServer.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+
+        while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+            String tagName = reader.getLocalName();
+            SimpleAttributeDefinition def = AuthServerDefinition.lookup(tagName);
+            if (def == null) throw new XMLStreamException("Unknown auth-server tag " + tagName);
+            def.parseAndSetParameter(reader.getElementText(), addAuthServer, reader);
+        }
+
+        list.add(addAuthServer);
+    }
+
     private void readRealm(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
         String realmName = readNameAttribute(reader);
         ModelNode addRealm = new ModelNode();
@@ -157,11 +178,28 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
     @Override
     public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
         context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
+        writeAuthServers(writer, context);
         writeRealms(writer, context);
         writeSecureDeployments(writer, context);
         writer.writeEndElement();
     }
 
+    private void writeAuthServers(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        if (!context.getModelNode().get(AuthServerDefinition.TAG_NAME).isDefined()) {
+            return;
+        }
+        for (Property authServer : context.getModelNode().get(AuthServerDefinition.TAG_NAME).asPropertyList()) {
+            writer.writeStartElement(AuthServerDefinition.TAG_NAME);
+            writer.writeAttribute("name", authServer.getName());
+            ModelNode authServerElements = authServer.getValue();
+            for (AttributeDefinition element : AuthServerDefinition.ALL_ATTRIBUTES) {
+                element.marshallAsElement(authServerElements, writer);
+            }
+
+            writer.writeEndElement();
+        }
+    }
+
     private void writeRealms(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
         if (!context.getModelNode().get(RealmDefinition.TAG_NAME).isDefined()) {
             return;
diff --git a/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties b/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
index 524ae4d..3239f14 100755
--- a/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
+++ b/integration/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
@@ -1,9 +1,23 @@
 keycloak.subsystem=Keycloak subsystem
 keycloak.subsystem.add=Operation Adds Keycloak subsystem
 keycloak.subsystem.remove=Operation removes Keycloak subsystem
+keycloak.subsystem.auth-server=Keycloak Auth Server
 keycloak.subsystem.realm=A Keycloak realm.
 keycloak.subsystem.secure-deployment=A deployment secured by Keycloak.
 
+
+keycloak.auth-server=A Keycloak Auth Server
+keycloak.auth-server.add=Add an Auth Server to the subsystem.
+keycloak.auth-server.remove=Remove an Auth Server from the subsystem.
+keycloak.auth-server.add-provider=Add a provider service jar to the Keycloak auth server.
+keycloak.auth-server.add-provider.uploaded-file-name=The file name of the provider service jar to be added or updated.
+keycloak.auth-server.add-provider.bytes-to-upload=The bytes of the provider service jar to be added or updated.
+keycloak.auth-server.update-server-config=Upload a new keycloak-server.json configuration file for the Keycloak auth server.
+keycloak.auth-server.update-server-config.uploaded-file-name=Should be the name keycloak-server.json.
+keycloak.auth-server.update-server-config.bytes-to-upload=The bytes of the keycloak-server.json file to be added or updated.
+keycloak.auth-server.enabled=Enable or disable the Auth Server.
+keycloak.auth-server.web-context=Web context the auth-server will use.  Also, the module name of the auth-server deployment.
+
 keycloak.realm=A Keycloak realm.
 keycloak.realm.add=Add a realm definition to the subsystem.
 keycloak.realm.remove=Remove a realm from the subsystem.

pom.xml 111(+70 -41)

diff --git a/pom.xml b/pom.xml
index 40305f3..14ee353 100755
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,7 @@
     <packaging>pom</packaging>
 
     <properties>
+        <aesh.version>0.33.12</aesh.version>
         <bouncycastle.version>1.46</bouncycastle.version>
         <jackson.version>1.9.9</jackson.version>
         <keycloak.apache.httpcomponents.version>4.2.1</keycloak.apache.httpcomponents.version>
@@ -35,7 +36,7 @@
         <slf4j.version>1.5.10</slf4j.version>
         <jboss.version>7.1.1.Final</jboss.version>
         <wildfly.version>8.1.0.Final</wildfly.version>
-        <wildfly.core.version>1.0.0.Alpha5</wildfly.core.version>
+        <wildfly.core.version>1.0.0.Alpha9</wildfly.core.version>
         <servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
         <google.zxing.version>2.2</google.zxing.version>
         <google.client.version>1.14.1-beta</google.client.version>
@@ -144,6 +145,17 @@
                 <version>${javax.mail.version}</version>
             </dependency>
             <dependency>
+                <groupId>org.jboss.aesh</groupId>
+                <artifactId>aesh</artifactId>
+                <version>${aesh.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>junit</groupId>
+                        <artifactId>junit</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
                 <groupId>org.jboss.resteasy</groupId>
                 <artifactId>jaxrs-api</artifactId>
                 <version>${resteasy.version}</version>
@@ -347,24 +359,24 @@
                 <version>${twitter4j.version}</version>
             </dependency>
 
-			<!-- QR Code Generator -->
+            <!-- QR Code Generator -->
             <dependency>
-      			<groupId>com.google.zxing</groupId>
-      			<artifactId>core</artifactId>
-      			<version>${google.zxing.version}</version>
-			</dependency>
+                <groupId>com.google.zxing</groupId>
+                <artifactId>core</artifactId>
+                <version>${google.zxing.version}</version>
+            </dependency>
             <dependency>
-      			<groupId>com.google.zxing</groupId>
-      			<artifactId>javase</artifactId>
-      			<version>${google.zxing.version}</version>
-			</dependency>
+                <groupId>com.google.zxing</groupId>
+                <artifactId>javase</artifactId>
+                <version>${google.zxing.version}</version>
+            </dependency>
 
-			<!-- Email Test Servers -->
-			<dependency>
-    			<groupId>com.icegreen</groupId>
-    			<artifactId>greenmail</artifactId>
-	    		<version>1.3.1b</version>
-			</dependency>
+            <!-- Email Test Servers -->
+            <dependency>
+                <groupId>com.icegreen</groupId>
+                <artifactId>greenmail</artifactId>
+                <version>1.3.1b</version>
+            </dependency>
 
             <!-- Encrypted ZIP -->
             <dependency>
@@ -373,18 +385,18 @@
                 <version>${winzipaes.version}</version>
             </dependency>
 
-			<!-- Selenium -->
-			<dependency>
-				<groupId>org.seleniumhq.selenium</groupId>
-				<artifactId>selenium-java</artifactId>
-				<version>${selenium.version}</version>
-  			</dependency>
-			<dependency>
-				<groupId>org.seleniumhq.selenium</groupId>
-				<artifactId>selenium-chrome-driver</artifactId>
-				<version>${selenium.version}</version>
+            <!-- Selenium -->
+            <dependency>
+                <groupId>org.seleniumhq.selenium</groupId>
+                <artifactId>selenium-java</artifactId>
+                <version>${selenium.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.seleniumhq.selenium</groupId>
+                <artifactId>selenium-chrome-driver</artifactId>
+                <version>${selenium.version}</version>
                 <scope>test</scope>
-			</dependency>
+            </dependency>
             <dependency>
                 <groupId>org.mongodb</groupId>
                 <artifactId>mongo-java-driver</artifactId>
@@ -424,12 +436,12 @@
                 <version>${mysql.version}</version>
             </dependency>
             <!-- the dependency seems to override Resteasy 3.0.5's depending on 4.2.1
-            <dependency>
-                <groupId>org.apache.httpcomponents</groupId>
-                <artifactId>httpclient</artifactId>
-                <version>${keycloak.apache.httpcomponents.version}</version>
-            </dependency>
-             -->
+           <dependency>
+               <groupId>org.apache.httpcomponents</groupId>
+               <artifactId>httpclient</artifactId>
+               <version>${keycloak.apache.httpcomponents.version}</version>
+           </dependency>
+            -->
             <dependency>
                 <groupId>org.wildfly.core</groupId>
                 <artifactId>wildfly-controller</artifactId>
@@ -453,6 +465,18 @@
                 <scope>test</scope>
             </dependency>
             <dependency>
+                <groupId>org.wildfly.core</groupId>
+                <artifactId>wildfly-core-feature-pack</artifactId>
+                <type>pom</type>
+                <version>${wildfly.core.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.wildfly.core</groupId>
+                <artifactId>wildfly-core-feature-pack</artifactId>
+                <type>zip</type>
+                <version>${wildfly.core.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.wildfly</groupId>
                 <artifactId>wildfly-undertow</artifactId>
                 <version>${wildfly.version}</version>
@@ -488,6 +512,11 @@
             <plugins>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-dependency-plugin</artifactId>
+                    <version>2.8</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-surefire-plugin</artifactId>
                     <version>2.16</version>
                     <configuration>
@@ -593,14 +622,14 @@
                     <artifactId>maven-deploy-plugin</artifactId>
                     <version>2.5</version>
                 </plugin>
-				<plugin>
-					<groupId>org.apache.maven.plugins</groupId>
-					<artifactId>maven-war-plugin</artifactId>
-					<version>2.3</version>
-					<configuration>
-						<failOnMissingWebXml>false</failOnMissingWebXml>
-					</configuration>
-				</plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-war-plugin</artifactId>
+                    <version>2.3</version>
+                    <configuration>
+                        <failOnMissingWebXml>false</failOnMissingWebXml>
+                    </configuration>
+                </plugin>
                 <plugin>
                     <groupId>com.lazerycode.jmeter</groupId>
                     <artifactId>jmeter-maven-plugin</artifactId>