keycloak-aplcache

mappers

3/6/2015 8:42:20 PM

Changes

examples/pom.xml 1(+1 -0)

Details

diff --git a/core/src/main/java/org/keycloak/util/KerberosSerializationUtils.java b/core/src/main/java/org/keycloak/util/KerberosSerializationUtils.java
new file mode 100644
index 0000000..2963623
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/KerberosSerializationUtils.java
@@ -0,0 +1,196 @@
+package org.keycloak.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import javax.security.auth.kerberos.KerberosTicket;
+
+import net.iharder.Base64;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.Oid;
+import org.keycloak.constants.KerberosConstants;
+import org.keycloak.util.reflections.Reflections;
+import sun.security.jgss.GSSCredentialImpl;
+import sun.security.jgss.GSSManagerImpl;
+import sun.security.jgss.krb5.Krb5InitCredential;
+import sun.security.jgss.krb5.Krb5NameElement;
+import sun.security.jgss.spi.GSSCredentialSpi;
+import sun.security.krb5.Credentials;
+
+/**
+ * Provides serialization/deserialization of kerberos {@link org.ietf.jgss.GSSCredential}, so it can be transmitted from auth-server to the application
+ * and used for further calls to kerberos-secured services
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KerberosSerializationUtils {
+
+    public static final Oid KRB5_OID;
+    public static final Oid KRB5_NAME_OID;
+    public static final String JAVA_INFO;
+
+    static {
+        try {
+            KRB5_OID = new Oid(KerberosConstants.KRB5_OID);
+            KRB5_NAME_OID = new Oid(KerberosConstants.KRB5_NAME_OID);
+        } catch (GSSException e) {
+            throw new RuntimeException(e);
+        }
+
+        String javaVersion = System.getProperty("java.version");
+        String javaRuntimeVersion = System.getProperty("java.runtime.version");
+        String javaVendor = System.getProperty("java.vendor");
+        String os = System.getProperty("os.version");
+        JAVA_INFO = "Java version: " + javaVersion + ", runtime version: " + javaRuntimeVersion + ", vendor: " + javaVendor + ", os: " + os;
+    }
+
+    private KerberosSerializationUtils() {
+    }
+
+    public static String serializeCredential(GSSCredential gssCredential) throws KerberosSerializationException {
+        try {
+            if (gssCredential == null) {
+                throw new KerberosSerializationException("Null credential given as input");
+            }
+
+            if (!(gssCredential instanceof GSSCredentialImpl)) {
+                throw new KerberosSerializationException("Unknown credential type: " + gssCredential.getClass());
+            }
+
+            GSSCredentialImpl gssCredImpl = (GSSCredentialImpl) gssCredential;
+            Oid[] mechs = gssCredImpl.getMechs();
+
+            for (Oid oid : mechs) {
+                if (oid.equals(KRB5_OID)) {
+                    int usage = gssCredImpl.getUsage(oid);
+                    boolean initiate = (usage == GSSCredential.INITIATE_ONLY || usage == GSSCredential.INITIATE_AND_ACCEPT);
+
+                    GSSCredentialSpi credentialSpi = gssCredImpl.getElement(oid, initiate);
+                    if (credentialSpi instanceof Krb5InitCredential) {
+                        Krb5InitCredential credential = (Krb5InitCredential) credentialSpi;
+                        KerberosTicket kerberosTicket = new KerberosTicket(credential.getEncoded(),
+                                credential.getClient(),
+                                credential.getServer(),
+                                credential.getSessionKey().getEncoded(),
+                                credential.getSessionKeyType(),
+                                credential.getFlags(),
+                                credential.getAuthTime(),
+                                credential.getStartTime(),
+                                credential.getEndTime(),
+                                credential.getRenewTill(),
+                                credential.getClientAddresses());
+                        return serialize(kerberosTicket);
+                    } else {
+                        throw new KerberosSerializationException("Unsupported type of credentialSpi: " + credentialSpi.getClass());
+                    }
+                }
+            }
+
+            throw new KerberosSerializationException("Kerberos credential not found. Available mechanisms: " + mechs);
+        } catch (IOException e) {
+            throw new KerberosSerializationException("Exception occured", e);
+        } catch (GSSException e) {
+            throw new KerberosSerializationException("Exception occured", e);
+        }
+    }
+
+
+    public static GSSCredential deserializeCredential(String serializedCred) throws KerberosSerializationException {
+        if (serializedCred == null) {
+            throw new KerberosSerializationException("Null credential given as input. Did you enable kerberos credential delegation for your web browser and mapping of gss credential to access token?");
+        }
+
+        try {
+            Object deserializedCred = deserialize(serializedCred);
+            if (!(deserializedCred instanceof KerberosTicket)) {
+                throw new KerberosSerializationException("Deserialized object is not KerberosTicket! Type is: " + deserializedCred);
+            }
+
+            KerberosTicket ticket = (KerberosTicket) deserializedCred;
+            String fullName = ticket.getClient().getName();
+
+            Method getInstance = Reflections.findDeclaredMethod(Krb5NameElement.class, "getInstance", String.class, Oid.class);
+            Krb5NameElement krb5Name = Reflections.invokeMethod(true, getInstance, Krb5NameElement.class, null, fullName, KRB5_NAME_OID);
+
+            Credentials krb5CredsInternal = new Credentials(
+                    ticket.getEncoded(),
+                    ticket.getClient().getName(),
+                    ticket.getServer().getName(),
+                    ticket.getSessionKey().getEncoded(),
+                    ticket.getSessionKeyType(),
+                    ticket.getFlags(),
+                    ticket.getAuthTime(),
+                    ticket.getStartTime(),
+                    ticket.getEndTime(),
+                    ticket.getRenewTill(),
+                    ticket.getClientAddresses()
+            );
+
+            Method getInstance2 = Reflections.findDeclaredMethod(Krb5InitCredential.class, "getInstance", Krb5NameElement.class, Credentials.class);
+            Krb5InitCredential initCredential = Reflections.invokeMethod(true, getInstance2, Krb5InitCredential.class, null, krb5Name, krb5CredsInternal);
+
+            GSSManagerImpl manager = (GSSManagerImpl) GSSManager.getInstance();
+            return new GSSCredentialImpl(manager, initCredential);
+        } catch (Exception ioe) {
+            throw new KerberosSerializationException("Exception occured", ioe);
+        }
+    }
+
+
+    private static String serialize(Serializable obj) throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ObjectOutput out = null;
+        try {
+            out = new ObjectOutputStream(bos);
+            out.writeObject(obj);
+            byte[] objBytes = bos.toByteArray();
+            return Base64.encodeBytes(objBytes);
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    private static Object deserialize(String serialized) throws ClassNotFoundException, IOException {
+        byte[] bytes = Base64.decode(serialized);
+        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+        ObjectInput in = null;
+        try {
+            in = new ObjectInputStream(bis);
+            return in.readObject();
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    public static class KerberosSerializationException extends RuntimeException {
+
+        public KerberosSerializationException(String message, Throwable cause) {
+            super(message + ", " + JAVA_INFO, cause);
+        }
+
+        public KerberosSerializationException(String message) {
+            super(message + ", " + JAVA_INFO);
+        }
+    }
+}
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index 339afad..43907f6 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -217,6 +217,12 @@
                 </exclusion>
             </exclusions>
         </dependency>
+
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-wildfly-extensions</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/distribution/examples-docs-zip/build.xml b/distribution/examples-docs-zip/build.xml
index 74d5032..c2790db 100755
--- a/distribution/examples-docs-zip/build.xml
+++ b/distribution/examples-docs-zip/build.xml
@@ -107,6 +107,12 @@
                 <exclude name="**/*.iml"/>
             </fileset>
         </copy>
+        <copy todir="target/examples/kerberos" overwrite="true">
+            <fileset dir="../../examples/kerberos">
+                <exclude name="**/target/**"/>
+                <exclude name="**/*.iml"/>
+            </fileset>
+        </copy>
         <copy file="../../examples/README.md" tofile="target/examples/README.md"/>
         <move file="target/examples/unconfigured-demo/README.md.unconfigured" tofile="target/examples/unconfigured-demo/README.md"/>
         <move file="target/examples/unconfigured-demo/customer-app/src/main/webapp/WEB-INF/web.xml.unconfigured" tofile="target/examples/unconfigured-demo/customer-app/src/main/webapp/WEB-INF/web.xml"/>
diff --git a/distribution/modules/build.xml b/distribution/modules/build.xml
index 0cd950a..efb3ba4 100755
--- a/distribution/modules/build.xml
+++ b/distribution/modules/build.xml
@@ -66,6 +66,10 @@
             <maven-resource group="org.keycloak" artifact="keycloak-services"/>
         </module-def>
 
+        <module-def name="org.keycloak.keycloak-wildfly-extensions">
+            <maven-resource group="org.keycloak" artifact="keycloak-wildfly-extensions"/>
+        </module-def>
+
         <module-def name="com.google.zxing.core">
             <maven-resource group="com.google.zxing" artifact="core"/>
         </module-def>
@@ -232,6 +236,8 @@
             <maven-resource group="org.keycloak" artifact="keycloak-kerberos-federation"/>
         </module-def>
 
+        <module-def name="sun.jdk.jgss"></module-def>
+
         <module-def name="org.keycloak.keycloak-ldap-federation">
             <maven-resource group="org.keycloak" artifact="keycloak-ldap-federation"/>
         </module-def>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
index c10c776..5a35b13 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
@@ -14,6 +14,8 @@
         <module name="net.iharder.base64"/>
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
+        <module name="sun.jdk" optional="true" />
+        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
index 4cc0d13..74e67a7 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
@@ -47,6 +47,7 @@
         <module name="org.keycloak.keycloak-model-sessions-mem" services="import"/>
         <module name="org.keycloak.keycloak-model-sessions-mongo" services="import"/>
         <module name="org.keycloak.keycloak-picketlink-api" services="import"/>
+        <module name="org.keycloak.keycloak-wildfly-extensions" services="import"/>
 
         <module name="org.keycloak.keycloak-picketlink-ldap" services="import"/>
         <module name="org.keycloak.keycloak-saml-protocol" services="import"/>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-extensions/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-extensions/main/module.xml
new file mode 100755
index 0000000..9ccc11e
--- /dev/null
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-extensions/main/module.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-extensions">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-model-api"/>
+        <module name="org.keycloak.keycloak-services"/>
+        <module name="org.jboss.modules"/>
+    </dependencies>
+</module>
diff --git a/distribution/modules/src/main/resources/modules/sun/jdk/jgss/main/module.xml b/distribution/modules/src/main/resources/modules/sun/jdk/jgss/main/module.xml
new file mode 100644
index 0000000..6df03ff
--- /dev/null
+++ b/distribution/modules/src/main/resources/modules/sun/jdk/jgss/main/module.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="sun.jdk.jgss">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <system export="true">
+            <paths>
+                <path name="sun/security/jgss" />
+                <path name="sun/security/jgss/spi" />
+                <path name="sun/security/jgss/krb5" />
+            </paths>
+        </system>
+    </dependencies>
+
+</module>
\ No newline at end of file
diff --git a/docbook/reference/en/en-US/modules/kerberos.xml b/docbook/reference/en/en-US/modules/kerberos.xml
index 24b5d9b..31c8d10 100644
--- a/docbook/reference/en/en-US/modules/kerberos.xml
+++ b/docbook/reference/en/en-US/modules/kerberos.xml
@@ -204,6 +204,40 @@ ktadd -k /tmp/http.keytab HTTP/www.mydomain.org@MYDOMAIN.ORG
             </para>
         </section>
     </section>
+
+    <section>
+        <title>Credential delegation</title>
+        <para>
+            One scenario supported by Kerberos 5 is credential delegation. In this case when user receives forwardable TGT and authenticates to the web server,
+            then web server might be able to reuse the ticket and forward it to another service secured by Kerberos (for example LDAP server or IMAP server).
+        </para>
+        <para>
+            The scenario is supported by Keycloak, but there is tricky thing that SPNEGO authentication is done by Keycloak server but
+            GSS credential will need to be used by your application. So you need to enable built-in <literal>gss delegation credential</literal> protocol mapper
+            in admin console for your application. This will cause that Keycloak will deserialize GSS credential and transmit it to the application
+            in access token. Application will need to deserialize it and use it for further GSS calls against other services.
+        </para>
+        <para>
+            GSSContext will need to
+            be created with this credential passed to the method <literal>GSSManager.createContext</literal> for example like this:
+            <programlisting><![CDATA[
+GSSContext context = gssManager.createContext(serviceName, krb5Oid,
+    deserializedGssCredFromKeycloakAccessToken, GSSContext.DEFAULT_LIFETIME);
+]]></programlisting>
+        </para>
+        <para>
+            Note that you also need to configure <literal>forwardable</literal> kerberos tickets in <literal>krb5.conf</literal> file
+            and add support for delegated credentials to your browser. See the kerberos example from Keycloak example set for details.
+        </para>
+        <warning>
+            <para>
+                Credential delegation has some security implications. So enable the protocol claim and support in browser just if you really need it.
+                It's highly recommended to use it together with HTTPS. See for example
+                <ulink url="http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html#idp18752">this article</ulink>
+                for details.
+            </para>
+        </warning>
+    </section>
     <section>
         <title>Troubleshooting</title>
         <para>
diff --git a/docbook/reference/en/en-US/modules/providers.xml b/docbook/reference/en/en-US/modules/providers.xml
index 4e2239d..e88bc34 100755
--- a/docbook/reference/en/en-US/modules/providers.xml
+++ b/docbook/reference/en/en-US/modules/providers.xml
@@ -87,33 +87,69 @@ public class MyEventListenerProvider implements EventListenerProvider {
     <section>
         <title>Registering provider implementations</title>
         <para>
-            Keycloak loads provider implementations from the file-system. By default all JARs inside
-            <literal>standalone/configuration/providers</literal> are loaded. This is simple, but requires all providers
-            to share the same library. All provides also inherit all classes from the Keycloak class-loader. In the future
-            we'll add support to load providers from modules, which allows better control of class isolation.
+            Keycloak can load provider implementations from JBoss Modules or directly from the file-system. Using Modules
+            is recommended as you can control exactly what classes are available to your provider. Any providers loaded
+            from the file-system uses a classloader with the Keycloak classloader as its parent.
         </para>
-        <para>
-            To register your provider simply copy the JAR including the ProviderFactory and Provider classes and the
-            provider configuration file to <literal>standalone/configuration/providers</literal>.
-        </para>
-        <para>
-            You can also define multiple provider class-path if you want to create isolated class-loaders. To do this
-            edit keycloak-server.json and add more classpath entries to the providers array. For example:
+
+        <section>
+            <title>Register a provider using Modules</title>
+            <para>
+                To register a provider using Modules first create a module. To do this you have to create a folder inside
+                KEYCLOAK_HOME/modules and add your jar and a <literal>module.xml</literal>. For example to add the event listener
+                sysout example provider create the folder <literal>KEYCLOAK_HOME/modules/org/keycloak/examples/event-sysout/main</literal>.
+                Copy <literal>event-listener-sysout-example.jar</literal> to this folder and create <literal>module.xml</literal>
+                with the following content:
+<programlisting><![CDATA[{
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.examples.event-sysout">
+    <resources>
+        <resource-root path="event-listener-sysout-example.jar"/>
+    </resources>
+    <dependencies>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="org.keycloak.keycloak-model-api"/>
+        <module name="org.keycloak.keycloak-events-api"/>
+    </dependencies>
+</module>
+}]]></programlisting>
+                Next you need to register this module with Keycloak. This is done by editing keycloak-server.json and adding
+                it to the providers:
+<programlisting><![CDATA[{
+"providers": [
+    ...
+    "module:org.keycloak.examples.event-sysout"
+]
+}]]></programlisting>
+            </para>
+        </section>
+
+
+        <section>
+            <title>Register a provider using file-system</title>
+            <para>
+                To register your provider simply copy the JAR including the ProviderFactory and Provider classes and the
+                provider configuration file to <literal>standalone/configuration/providers</literal>.
+            </para>
+            <para>
+                You can also define multiple provider class-path if you want to create isolated class-loaders. To do this
+                edit keycloak-server.json and add more classpath entries to the providers array. For example:
 <programlisting><![CDATA[{
     "providers": [
         "classpath:provider1.jar;lib-v1.jar",
         "classpath:provider2.jar;lib-v2.jar"
     ]
 }]]></programlisting>
-            The above example will create two separate class-loaders for providers. The classpath entries follow the
-            same syntax as Java classpath, with ';' separating multiple-entries. Wildcard is also supported allowing
-            loading all jars (files with .jar or .JAR extension) in a folder, for example:
+                The above example will create two separate class-loaders for providers. The classpath entries follow the
+                same syntax as Java classpath, with ';' separating multiple-entries. Wildcard is also supported allowing
+                loading all jars (files with .jar or .JAR extension) in a folder, for example:
 <programlisting><![CDATA[{
     "providers": [
         "classpath:/home/user/providers/*"
     ]
 }]]></programlisting>
-        </para>
+            </para>
+        </section>
     </section>
 
     <section>
diff --git a/examples/kerberos/http.keytab b/examples/kerberos/http.keytab
new file mode 100644
index 0000000..0e7fd96
Binary files /dev/null and b/examples/kerberos/http.keytab differ
diff --git a/examples/kerberos/kerberosrealm.json b/examples/kerberos/kerberosrealm.json
new file mode 100644
index 0000000..0f044f6
--- /dev/null
+++ b/examples/kerberos/kerberosrealm.json
@@ -0,0 +1,94 @@
+{
+    "id": "kerberos-demo",
+    "realm": "kerberos-demo",
+    "enabled": true,
+    "sslRequired": "external",
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password", "kerberos" ],
+    "defaultRoles": [ "user" ],
+    "scopeMappings": [
+        {
+            "client": "kerberos-app",
+            "roles": [ "user" ]
+        }
+    ],
+    "applications": [
+        {
+            "name": "kerberos-app",
+            "enabled": true,
+            "baseUrl": "/kerberos-portal",
+            "redirectUris": [
+                "/kerberos-portal/*"
+            ],
+            "adminUrl": "/kerberos-portal",
+            "secret": "password",
+            "protocolMappers": [
+        		{
+		        	"protocolMapper" : "oidc-usermodel-property-mapper",
+			        "protocol" : "openid-connect",
+			        "name" : "username",
+			        "consentText" : "username",
+			        "consentRequired" : true,
+			        "config" : {
+				        "Claim JSON Type" : "String",
+				        "user.attribute" : "username",
+				        "Token Claim Name" : "preferred_username",
+				        "id.token.claim" : "true",
+				        "access.token.claim" : "true"
+			        }
+		        },
+                {
+			        "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+			        "protocol" : "openid-connect",
+			        "name" : "gss delegation credential",
+			        "consentText" : "gss delegation credential",
+			        "consentRequired" : true,
+			        "config" : {
+				        "user.session.note" : "gss_delegation_credential",
+				        "Token Claim Name" : "gss_delegation_credential",
+                        "id.token.claim" : "false",
+                        "access.token.claim" : "true"
+			        }
+		        }
+            ]
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "user",
+                "description": "Have User privileges"
+            }
+        ]
+    },
+    "userFederationProviders": [
+        {
+            "displayName": "kerberos-ldap-provider",
+            "providerName": "ldap",
+            "priority": 1,
+            "fullSyncPeriod": -1,
+            "changedSyncPeriod": -1,
+            "config": {
+                "syncRegistrations" : "false",
+                "userAccountControlsAfterPasswordUpdate" : "true",
+                "connectionPooling" : "true",
+                "pagination" : "true",
+                "allowKerberosAuthentication" : "true",
+                "debug" : "true",
+                "editMode" : "WRITABLE",
+                "vendor" : "other",
+                "usernameLDAPAttribute" : "uid",
+                "userObjectClasses" : "inetOrgPerson, organizationalPerson",
+                "connectionUrl" : "ldap://localhost:10389",
+                "baseDn" : "dc=keycloak,dc=org",
+                "userDnSuffix" : "ou=People,dc=keycloak,dc=org",
+                "bindDn" : "uid=admin,ou=system",
+                "bindCredential" : "secret",
+                "kerberosRealm" : "KEYCLOAK.ORG",
+                "serverPrincipal" : "HTTP/localhost@KEYCLOAK.ORG",
+                "keyTab" : "/tmp/http.keytab"
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/examples/kerberos/pom.xml b/examples/kerberos/pom.xml
new file mode 100644
index 0000000..431876e
--- /dev/null
+++ b/examples/kerberos/pom.xml
@@ -0,0 +1,83 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.2.0.Beta1-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <name>Keycloak Examples - Kerberos Credential Delegation</name>
+    <artifactId>examples-kerberos</artifactId>
+    <packaging>war</packaging>
+
+    <description>
+        Kerberos Credential Delegation Example
+    </description>
+
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <name>jboss repo</name>
+            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-adapter-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>kerberos-portal</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/examples/kerberos/README.md b/examples/kerberos/README.md
new file mode 100644
index 0000000..5acdb62
--- /dev/null
+++ b/examples/kerberos/README.md
@@ -0,0 +1,66 @@
+Keycloak Example - Kerberos Credential Delegation
+=================================================
+
+This example requires that Keycloak is configured with Kerberos/SPNEGO authentication. It's showing how the forwardable TGT is sent from
+the Keycloak auth-server to the application, which deserializes it and authenticates with it to further Kerberized service, which in the example is LDAP server.
+
+Example is using built-in ApacheDS Kerberos server from the keycloak testsuite and the realm with preconfigured federation provider and `gss delegation credential` protocol mapper.
+It also needs to enable forwardable ticket support in Kerberos configuration and your browser.
+
+Detailed steps:
+
+**1)** Build and deploy this sample's WAR file. For this example, deploy on the same server that is running the Keycloak Server, although this is not required for real world scenarios.
+
+
+**2)** Copy `http.keytab` file from the root directory of example to `/tmp` directory (On Linux):
+
+```
+cp http.keytab /tmp/http.keytab
+```
+
+Alternative is to configure different location for `keyTab` property in `kerberosrealm.json` configuration file (On Windows this will be needed).
+Note that in production, keytab file should be in secured location accessible just to the user under which is Keycloak server running.
+
+
+**3)** Run Keycloak server and import `kerberosrealm.json` into it through admin console. This will import realm with sample application
+and configured LDAP federation provider with Kerberos/SPNEGO authentication support enabled and with `gss delegation credential` protocol mapper 
+added to the application.
+
+**WARNING:** It's recommended to use JDK8 to run Keycloak server. For JDK7 you may be faced with the bug described [here](http://darranl.blogspot.cz/2014/09/kerberos-encrypteddata-null-key-keytype.html) . 
+Alternatively you can use OpenJDK7 but in this case you will need to use aes256-cts-hmac-sha1-96 for both KDC and Kerberos client configuration. For server, 
+you can add system property to the maven command when running ApacheDS Kerberos server `-Dkerberos.encTypes=aes256-cts-hmac-sha1-96` (see below) and for 
+client add encryption types to configuration file like `/etc/krb5.conf` (but they should be already available. See below).
+
+
+**4)**  Run ApacheDS based Kerberos server embedded in Keycloak. Easiest is to checkout keycloak sources, build and then run KerberosEmbeddedServer 
+as shown here: 
+
+```
+git clone https://github.com/keycloak/keycloak.git
+mvn clean install -DskipTests=true
+cd testsuite/integration
+mvn exec:java -Pkerberos
+```
+
+More details about embedded Kerberos server in [testsuite README](https://github.com/keycloak/keycloak/blob/master/testsuite/integration/README.md#kerberos-server).
+
+
+**5)** Configure Kerberos client (On linux it's in file `/etc/krb5.conf` ). You need to configure `KEYCLOAK.ORG` realm and enable `forwardable` flag, which is needed 
+for credential delegation example, as application needs to forward Kerberos ticket and authenticate with it against LDAP server. 
+See [this file](https://github.com/keycloak/keycloak/blob/master/testsuite/integration/src/main/resources/kerberos/test-krb5.conf) for inspiration.
+
+  
+**6)** Configure browser (Firefox, Chrome or other) and enable SPNEGO authentication and credential delegation for `localhost` . 
+In Firefox it can be done by adding `localhost` to both `network.negotiate-auth.trusted-uris` and `network.negotiate-auth.delegation-uris` . 
+More info in [testsuite README](https://github.com/keycloak/keycloak/blob/master/testsuite/integration/README.md#kerberos-server).  
+ 
+ 
+**7)** Test the example. Obtain kerberos ticket by running command from CMD (on linux):
+```
+kinit hnelson@KEYCLOAK.ORG
+```
+with password `secret` .
+
+Then in your web browser open `http://localhost:8080/kerberos-portal` . You should be logged-in automatically through SPNEGO without displaying Keycloak login screen.
+Keycloak will also transmit the delegated GSS credential to the application inside access token and application will be able to login with this credential
+to the LDAP server and retrieve some data from it (Actually it just retrieve few simple data about authenticated user himself).
\ No newline at end of file
diff --git a/examples/kerberos/src/main/java/org/keycloak/example/kerberos/GSSCredentialsClient.java b/examples/kerberos/src/main/java/org/keycloak/example/kerberos/GSSCredentialsClient.java
new file mode 100644
index 0000000..3017b1e
--- /dev/null
+++ b/examples/kerberos/src/main/java/org/keycloak/example/kerberos/GSSCredentialsClient.java
@@ -0,0 +1,101 @@
+package org.keycloak.example.kerberos;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.security.sasl.Sasl;
+import javax.servlet.http.HttpServletRequest;
+
+import org.ietf.jgss.GSSCredential;
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.constants.KerberosConstants;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.util.KerberosSerializationUtils;
+
+/**
+ * Sample client able to authenticate against ApacheDS LDAP server with Krb5 GSS Credential.
+ *
+ * Credential was previously retrieved from SPNEGO authentication against Keycloak auth-server and transmitted from
+ * Keycloak to the application in OIDC access token
+ *
+ * We can use GSSCredential to further GSS API calls . Note that if you will use GSS API directly, you can
+ * attach GSSCredential when creating GSSContext like this:
+ * GSSContext context = gssManager.createContext(serviceName, KerberosSerializationUtils.KRB5_OID, deserializedGssCredential, GSSContext.DEFAULT_LIFETIME);
+ *
+ * In this example we authenticate against LDAP server, which calls GSS API under the hood when credential is attached to env under Sasl.CREDENTIALS key
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class GSSCredentialsClient {
+
+    public static LDAPUser getUserFromLDAP(HttpServletRequest req) throws Exception {
+        KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) req.getUserPrincipal();
+        AccessToken accessToken = keycloakPrincipal.getKeycloakSecurityContext().getToken();
+        String username = accessToken.getPreferredUsername();
+
+        // Retrieve kerberos credential from accessToken and deserialize it
+        String serializedGssCredential = (String) accessToken.getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
+        GSSCredential deserializedGssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential);
+
+        // First try to invoke without gssCredential. It should fail. This is here just for illustration purposes
+        try {
+            invokeLdap(null, username);
+            throw new RuntimeException("Not expected to authenticate to LDAP without credential");
+        } catch (NamingException nse) {
+            System.out.println("GSSCredentialsClient: Expected exception: " + nse.getMessage());
+        }
+
+        return invokeLdap(deserializedGssCredential, username);
+    }
+
+    private static LDAPUser invokeLdap(GSSCredential gssCredential, String username) throws NamingException {
+        Hashtable env = new Hashtable(11);
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
+
+        if (gssCredential != null) {
+            env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
+            env.put(Sasl.CREDENTIALS, gssCredential);
+        }
+
+        DirContext ctx = new InitialDirContext(env);
+        try {
+            Attributes attrs = ctx.getAttributes("uid=" + username + ",ou=People,dc=keycloak,dc=org");
+            String uid = username;
+            String cn = (String) attrs.get("cn").get();
+            String sn = (String) attrs.get("sn").get();
+            return new LDAPUser(uid, cn, sn);
+        } finally {
+            ctx.close();
+        }
+    }
+
+    public static class LDAPUser {
+
+        private final String uid;
+        private final String cn;
+        private final String sn;
+
+        public LDAPUser(String uid, String cn, String sn) {
+            this.uid = uid;
+            this.cn = cn;
+            this.sn = sn;
+        }
+
+        public String getUid() {
+            return uid;
+        }
+
+        public String getCn() {
+            return cn;
+        }
+
+        public String getSn() {
+            return sn;
+        }
+    }
+}
diff --git a/examples/kerberos/src/main/webapp/index.jsp b/examples/kerberos/src/main/webapp/index.jsp
new file mode 100644
index 0000000..c1df8f0
--- /dev/null
+++ b/examples/kerberos/src/main/webapp/index.jsp
@@ -0,0 +1,37 @@
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
+         pageEncoding="ISO-8859-1" %>
+<%@ page import="org.keycloak.constants.ServiceUrlConstants" %>
+<%@ page import="org.keycloak.util.KeycloakUriBuilder" %>
+<%@ page import="org.keycloak.example.kerberos.GSSCredentialsClient" %>
+<%@ page import="org.keycloak.example.kerberos.GSSCredentialsClient.LDAPUser" %>
+<%@ page session="false" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+    <head>
+        <title>Kerberos Credentials Delegation Example</title>
+    </head>
+    <body bgcolor="#ffffff">
+        <h1>Kerberos Credentials Delegation Example</h1>
+        <hr />
+
+<%
+    String logoutUri = KeycloakUriBuilder.fromUri("/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+            .queryParam("redirect_uri", "/kerberos-portal").build("kerberos-demo").toString();
+%>
+        <b>Details about user from LDAP</b> | <a href="<%=logoutUri%>">Logout</a><br />
+        <hr />
+<%
+    try {
+        GSSCredentialsClient.LDAPUser ldapUser = GSSCredentialsClient.getUserFromLDAP(request);
+        out.println("<p>uid: <b>" + ldapUser.getUid() + "</b></p>");
+        out.println("<p>cn: <b>" + ldapUser.getCn() + "</b></p>");
+        out.println("<p>sn: <b>" + ldapUser.getSn() + "</b></p>");
+    } catch (Exception e) {
+        e.printStackTrace();
+        out.println("<b>There was a failure in retrieve GSS credential or invoking LDAP. Check server.log for more details</b>");
+    }
+%>
+    </body>
+</html>
\ No newline at end of file
diff --git a/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json b/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json
new file mode 100644
index 0000000..dfb8577
--- /dev/null
+++ b/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,11 @@
+{
+  "realm" : "kerberos-demo",
+  "resource" : "kerberos-app",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url": "/auth",
+  "ssl-required" : "external",
+  "enable-basic-auth" : "true",
+  "credentials": {
+      "secret": "password"
+  }
+}
\ No newline at end of file
diff --git a/examples/kerberos/src/main/webapp/WEB-INF/web.xml b/examples/kerberos/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..aa21166
--- /dev/null
+++ b/examples/kerberos/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>kerberos-portal</module-name>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>KerberosApp</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <!--
+    <security-constraint>
+        <web-resource-collection>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>
+    </security-constraint> -->
+
+    <login-config>
+        <auth-method>KEYCLOAK</auth-method>
+        <realm-name>does-not-matter</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
\ No newline at end of file

examples/pom.xml 1(+1 -0)

diff --git a/examples/pom.xml b/examples/pom.xml
index 9a8a585..eb68e33 100755
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -33,5 +33,6 @@
         <module>multi-tenant</module>
         <module>basic-auth</module>
         <module>fuse</module>
+        <module>kerberos</module>
     </modules>
 </project>
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java
index 04b498e..f98364a 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java
@@ -3,7 +3,7 @@ package org.keycloak.federation.kerberos;
 import java.util.Map;
 
 import org.keycloak.models.UserFederationProviderModel;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 
 /**
  * Common configuration useful for all providers
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
index ddebe66..fa13622 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
@@ -105,7 +105,7 @@ public class KerberosUsernamePasswordAuthenticator {
             try {
                 loginContext.logout();
             } catch (LoginException le) {
-                logger.error("Failed to logout kerberos server subject: " + config.getServerPrincipal(), le);
+                logger.error("Failed to logout kerberos subject", le);
             }
         }
     }
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java
index 1132351..6dd2576 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java
@@ -1,7 +1,6 @@
 package org.keycloak.federation.kerberos.impl;
 
 import java.io.IOException;
-import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 
 import javax.security.auth.Subject;
@@ -13,6 +12,7 @@ import org.ietf.jgss.GSSException;
 import org.ietf.jgss.GSSManager;
 import org.jboss.logging.Logger;
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
+import org.keycloak.util.KerberosSerializationUtils;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -21,8 +21,6 @@ public class SPNEGOAuthenticator {
 
     private static final Logger log = Logger.getLogger(SPNEGOAuthenticator.class);
 
-    private static final GSSManager GSS_MANAGER = GSSManager.getInstance();
-
     private final KerberosServerSubjectAuthenticator kerberosSubjectAuthenticator;
     private final String spnegoToken;
     private final CommonKerberosConfig kerberosConfig;
@@ -61,8 +59,24 @@ public class SPNEGOAuthenticator {
         return responseToken;
     }
 
-    public GSSCredential getDelegationCredential() {
-        return delegationCredential;
+    public String getSerializedDelegationCredential() {
+        if (delegationCredential == null) {
+            if (log.isTraceEnabled()) {
+                log.trace("No delegation credential available.");
+            }
+
+            return null;
+        }
+
+        try {
+            if (log.isTraceEnabled()) {
+                log.trace("Serializing credential " + delegationCredential);
+            }
+            return KerberosSerializationUtils.serializeCredential(delegationCredential);
+        } catch (KerberosSerializationUtils.KerberosSerializationException kse) {
+            log.warn("Couldn't serialize credential: " + delegationCredential, kse);
+            return null;
+        }
     }
 
     /**
@@ -114,7 +128,8 @@ public class SPNEGOAuthenticator {
 
 
     protected GSSContext establishContext() throws GSSException, IOException {
-        GSSContext gssContext = GSS_MANAGER.createContext((GSSCredential) null);
+        GSSManager manager = GSSManager.getInstance();
+        GSSContext gssContext = manager.createContext((GSSCredential) null);
 
         byte[] inputToken = Base64.decode(spnegoToken);
         byte[] respToken = gssContext.acceptSecContext(inputToken, 0, inputToken.length);
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosConfig.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosConfig.java
index c1f59b7..32349b7 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosConfig.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosConfig.java
@@ -1,6 +1,6 @@
 package org.keycloak.federation.kerberos;
 
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.models.LDAPConstants;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
index 603f3bd..40094ef 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
@@ -20,7 +20,7 @@ import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -176,19 +176,21 @@ public class KerberosFederationProvider implements UserFederationProvider {
 
             spnegoAuthenticator.authenticate();
 
+            Map<String, String> state = new HashMap<String, String>();
             if (spnegoAuthenticator.isAuthenticated()) {
-                Map<String, Object> state = new HashMap<String, Object>();
-                state.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, spnegoAuthenticator.getDelegationCredential());
-
                 String username = spnegoAuthenticator.getAuthenticatedUsername();
                 UserModel user = findOrCreateAuthenticatedUser(realm, username);
                 if (user == null) {
                     return CredentialValidationOutput.failed();
                 } else {
+                    String delegationCredential = spnegoAuthenticator.getSerializedDelegationCredential();
+                    if (delegationCredential != null) {
+                        state.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, delegationCredential);
+                    }
+
                     return new CredentialValidationOutput(user, CredentialValidationOutput.Status.AUTHENTICATED, state);
                 }
             }  else {
-                Map<String, Object> state = new HashMap<String, Object>();
                 state.put(KerberosConstants.RESPONSE_TOKEN, spnegoAuthenticator.getResponseToken());
                 return new CredentialValidationOutput(null, CredentialValidationOutput.Status.CONTINUE, state);
             }
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java
index 4363ce9..d4d5c97 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java
@@ -1,7 +1,7 @@
 package org.keycloak.federation.ldap.kerberos;
 
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.models.UserFederationProviderModel;
 
 /**
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index 7aabe26..f48880c 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -15,7 +15,7 @@ import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 import org.picketlink.idm.IdentityManagementException;
 import org.picketlink.idm.IdentityManager;
 import org.picketlink.idm.PartitionManager;
@@ -344,9 +344,8 @@ public class LDAPFederationProvider implements UserFederationProvider {
 
                 spnegoAuthenticator.authenticate();
 
+                Map<String, String> state = new HashMap<String, String>();
                 if (spnegoAuthenticator.isAuthenticated()) {
-                    Map<String, Object> state = new HashMap<String, Object>();
-                    state.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, spnegoAuthenticator.getDelegationCredential());
 
                     // TODO: This assumes that LDAP "uid" is equal to kerberos principal name. Like uid "hnelson" and kerberos principal "hnelson@KEYCLOAK.ORG".
                     // Check if it's correct or if LDAP attribute for mapping kerberos principal should be available (For ApacheDS it seems to be attribute "krb5PrincipalName" but on MSAD it's likely different)
@@ -356,11 +355,15 @@ public class LDAPFederationProvider implements UserFederationProvider {
                     if (user == null) {
                         logger.warn("Kerberos/SPNEGO authentication succeeded with username [" + username + "], but couldn't find or create user with federation provider [" + model.getDisplayName() + "]");
                         return CredentialValidationOutput.failed();
-                    }
+                    } else {
+                        String delegationCredential = spnegoAuthenticator.getSerializedDelegationCredential();
+                        if (delegationCredential != null) {
+                            state.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, delegationCredential);
+                        }
 
-                    return new CredentialValidationOutput(user, CredentialValidationOutput.Status.AUTHENTICATED, state);
+                        return new CredentialValidationOutput(user, CredentialValidationOutput.Status.AUTHENTICATED, state);
+                    }
                 }  else {
-                    Map<String, Object> state = new HashMap<String, Object>();
                     state.put(KerberosConstants.RESPONSE_TOKEN, spnegoAuthenticator.getResponseToken());
                     return new CredentialValidationOutput(null, CredentialValidationOutput.Status.CONTINUE, state);
                 }
diff --git a/integration/pom.xml b/integration/pom.xml
index 2ccda8e..7549c55 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -23,6 +23,7 @@
         <module>jetty</module>
         <module>undertow</module>
         <module>wildfly-adapter</module>
+        <module>wildfly-extensions</module>
         <module>keycloak-subsystem</module>
         <module>keycloak-as7-subsystem</module>
         <module>js</module>
diff --git a/integration/wildfly-extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java b/integration/wildfly-extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java
index cb4a6f1..217c203 100644
--- a/integration/wildfly-extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java
+++ b/integration/wildfly-extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java
@@ -21,7 +21,6 @@ public class ModuleProviderLoaderFactory implements ProviderLoaderFactory {
     @Override
     public ProviderLoader create(ClassLoader baseClassLoader, String resource) {
         try {
-            System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx");
             Module module = Module.getContextModuleLoader().loadModule(ModuleIdentifier.fromString(resource));
             ModuleClassLoader classLoader = module.getClassLoader();
             return new DefaultProviderLoader(classLoader);
diff --git a/model/api/src/main/java/org/keycloak/models/CredentialValidationOutput.java b/model/api/src/main/java/org/keycloak/models/CredentialValidationOutput.java
index 70e4093..a056ef9 100644
--- a/model/api/src/main/java/org/keycloak/models/CredentialValidationOutput.java
+++ b/model/api/src/main/java/org/keycloak/models/CredentialValidationOutput.java
@@ -12,16 +12,16 @@ public class CredentialValidationOutput {
 
     private final UserModel authenticatedUser; // authenticated user.
     private final Status authStatus;           // status whether user is authenticated or more steps needed
-    private final Map<String, Object> state;   // Additional state related to authentication. It can contain data to be sent back to client or data about used credentials.
+    private final Map<String, String> state;   // Additional state related to authentication. It can contain data to be sent back to client or data about used credentials.
 
-    public CredentialValidationOutput(UserModel authenticatedUser, Status authStatus, Map<String, Object> state) {
+    public CredentialValidationOutput(UserModel authenticatedUser, Status authStatus, Map<String, String> state) {
         this.authenticatedUser = authenticatedUser;
         this.authStatus = authStatus;
         this.state = state;
     }
 
     public static CredentialValidationOutput failed() {
-        return new CredentialValidationOutput(null, CredentialValidationOutput.Status.FAILED, new HashMap<String, Object>());
+        return new CredentialValidationOutput(null, CredentialValidationOutput.Status.FAILED, new HashMap<String, String>());
     }
 
     public UserModel getAuthenticatedUser() {
@@ -32,7 +32,7 @@ public class CredentialValidationOutput {
         return authStatus;
     }
 
-    public Map<String, Object> getState() {
+    public Map<String, String> getState() {
         return state;
     }
 
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index 92465e1..f52e9ef 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -302,6 +302,7 @@ public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends A
             mapping.setId(entity.getId());
             mapping.setName(entity.getName());
             mapping.setProtocol(entity.getProtocol());
+            mapping.setProtocolMapper(entity.getProtocolMapper());
             mapping.setConsentRequired(entity.isConsentRequired());
             mapping.setConsentText(entity.getConsentText());
             Map<String, String> config = new HashMap<String, String>();
@@ -309,6 +310,7 @@ public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends A
                 config.putAll(entity.getConfig());
             }
             mapping.setConfig(config);
+            result.add(mapping);
         }
         return result;
     }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java
new file mode 100644
index 0000000..effa69a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java
@@ -0,0 +1,134 @@
+package org.keycloak.protocol.oidc.mappers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
+
+/**
+ * Mappings UserSessionModel.note to an ID Token claim.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OIDCUserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
+
+    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+
+    static {
+        ConfigProperty property;
+        property = new ConfigProperty();
+        property.setName(ProtocolMapperUtils.USER_SESSION_NOTE);
+        property.setLabel(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_LABEL);
+        property.setHelpText(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_HELP_TEXT);
+        property.setType(ConfigProperty.STRING_TYPE);
+        configProperties.add(property);
+        property = new ConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
+        property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
+        property.setType(ConfigProperty.STRING_TYPE);
+        property.setHelpText("Name of the claim to insert into the token.  This can be a fully qualified name like 'address.street'.  In this case, a nested json object will be created.");
+        configProperties.add(property);
+        property = new ConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
+        property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
+        property.setType(ConfigProperty.STRING_TYPE);
+        property.setDefaultValue(ConfigProperty.STRING_TYPE);
+        property.setHelpText("JSON type that should be used to populate the json claim in the token.  long, int, boolean, and String are valid values.");
+        configProperties.add(property);
+        property = new ConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
+        property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
+        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setDefaultValue("true");
+        property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
+        configProperties.add(property);
+        property = new ConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
+        property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
+        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setDefaultValue("true");
+        property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
+        configProperties.add(property);
+
+    }
+
+    public static final String PROVIDER_ID = "oidc-usersessionmodel-note-mapper";
+
+
+    public List<ConfigProperty> getConfigProperties() {
+        return configProperties;
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "User Session Note";
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return TOKEN_MAPPER_CATEGORY;
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Map a custom user session note to a token claim.";
+    }
+
+    @Override
+    public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
+                                            UserSessionModel userSession, ClientSessionModel clientSession) {
+        if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token;
+
+        setClaim(token, mappingModel, userSession);
+        return token;
+    }
+
+    protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
+        String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
+        String noteValue = userSession.getNote(noteName);
+        if (noteValue == null) return;
+        OIDCAttributeMapperHelper.mapClaim(token, mappingModel, noteValue);
+    }
+
+    @Override
+    public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
+        if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token;
+        setClaim(token, mappingModel, userSession);
+        return token;
+    }
+
+    public static ProtocolMapperModel createClaimMapper(String name,
+                                                        String userSessionNote,
+                                                        String tokenClaimName, String jsonType,
+                                                        boolean consentRequired, String consentText,
+                                                        boolean accessToken, boolean idToken) {
+        ProtocolMapperModel mapper = new ProtocolMapperModel();
+        mapper.setName(name);
+        mapper.setProtocolMapper(PROVIDER_ID);
+        mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+        mapper.setConsentRequired(consentRequired);
+        mapper.setConsentText(consentText);
+        Map<String, String> config = new HashMap<String, String>();
+        config.put(ProtocolMapperUtils.USER_SESSION_NOTE, userSessionNote);
+        config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
+        config.put(OIDCAttributeMapperHelper.JSON_TYPE, jsonType);
+        if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+        if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+        mapper.setConfig(config);
+        return mapper;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
index 1fc1277..1c27033 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -1,5 +1,6 @@
 package org.keycloak.protocol.oidc;
 
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
@@ -11,6 +12,7 @@ import org.keycloak.protocol.oidc.mappers.OIDCAddressMapper;
 import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
 import org.keycloak.protocol.oidc.mappers.OIDCFullNameMapper;
 import org.keycloak.protocol.oidc.mappers.OIDCUserModelMapper;
+import org.keycloak.protocol.oidc.mappers.OIDCUserSessionNoteMapper;
 import org.keycloak.services.managers.AuthenticationManager;
 
 import java.util.ArrayList;
@@ -89,6 +91,13 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
 
         ProtocolMapperModel address = OIDCAddressMapper.createAddressMapper();
         builtins.add(address);
+
+        model = OIDCUserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
+                KerberosConstants.GSS_DELEGATION_CREDENTIAL,
+                KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
+                true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
+                true, false);
+        builtins.add(model);
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
index abb915e..a009323 100755
--- a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
@@ -10,10 +10,13 @@ import java.lang.reflect.Method;
  */
 public class ProtocolMapperUtils {
     public static final String USER_ATTRIBUTE = "user.attribute";
+    public static final String USER_SESSION_NOTE = "user.session.note";
     public static final String USER_MODEL_PROPERTY_LABEL = "User Property";
     public static final String USER_MODEL_PROPERTY_HELP_TEXT = "Name of the property method in the UserModel interface.  For example, a value of 'email' would reference the UserModel.getEmail() method.";
     public static final String USER_MODEL_ATTRIBUTE_LABEL = "User Attribute";
     public static final String USER_MODEL_ATTRIBUTE_HELP_TEXT = "Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.";
+    public static final String USER_SESSION_MODEL_NOTE_LABEL = "User Session Note";
+    public static final String USER_SESSION_MODEL_NOTE_HELP_TEXT = "Name of stored user session note within the UserSessionModel.note map.";
 
     public static String getUserModelValue(UserModel user, String propertyName) {
 
diff --git a/services/src/main/java/org/keycloak/provider/ProviderManager.java b/services/src/main/java/org/keycloak/provider/ProviderManager.java
index 6a301fd..6246d83 100644
--- a/services/src/main/java/org/keycloak/provider/ProviderManager.java
+++ b/services/src/main/java/org/keycloak/provider/ProviderManager.java
@@ -20,7 +20,7 @@ public class ProviderManager {
 
     public ProviderManager(ClassLoader baseClassLoader, String... resources) {
         List<ProviderLoaderFactory> factories = new LinkedList<ProviderLoaderFactory>();
-        for (ProviderLoaderFactory f : ServiceLoader.load(ProviderLoaderFactory.class)) {
+        for (ProviderLoaderFactory f : ServiceLoader.load(ProviderLoaderFactory.class, getClass().getClassLoader())) {
             factories.add(f);
         }
 
diff --git a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
index 9791de3..7dca9c1 100644
--- a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
@@ -1,5 +1,7 @@
 package org.keycloak.services.managers;
 
+import java.util.Map;
+
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -19,7 +21,7 @@ import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.messages.Messages;
@@ -94,7 +96,7 @@ public class HttpAuthenticationManager {
             CredentialValidationOutput output = session.users().validCredentials(realm, spnegoCredential);
 
             if (output.getAuthStatus() == CredentialValidationOutput.Status.AUTHENTICATED) {
-                return sendResponse(output.getAuthenticatedUser(), "spnego");
+                return sendResponse(output.getAuthenticatedUser(), output.getState(), "spnego");
             }  else {
                 String spnegoResponseToken = (String) output.getState().get(KerberosConstants.RESPONSE_TOKEN);
                 return challengeNegotiation(spnegoResponseToken);
@@ -104,7 +106,7 @@ public class HttpAuthenticationManager {
 
 
     // Send response after successful authentication
-    private HttpAuthOutput sendResponse(UserModel user, String authMethod) {
+    private HttpAuthOutput sendResponse(UserModel user, Map<String, String> authState, String authMethod) {
         if (logger.isTraceEnabled()) {
             logger.trace("User " + user.getUsername() + " authenticated with " + authMethod);
         }
@@ -115,6 +117,12 @@ public class HttpAuthenticationManager {
             response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, Messages.ACCOUNT_DISABLED);
         } else {
             UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false);
+
+            // Propagate state (like kerberos delegation credentials etc) as attributes of userSession
+            for (Map.Entry<String, String> entry : authState.entrySet()) {
+                userSession.setNote(entry.getKey(), entry.getValue());
+            }
+
             TokenManager.attachClientSession(userSession, clientSession);
             event.user(user)
                     .session(userSession)
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
index e4fff5f..717a116 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
@@ -4,23 +4,11 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.models.ClientModel;
-import org.keycloak.models.KerberosConstants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.RequiredCredentialModel;
-import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.UserFederationProvider;
-import org.keycloak.models.UserFederationProviderFactory;
-import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
-import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
-import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
-import org.keycloak.representations.idm.UserFederationProviderRepresentation;
-import org.keycloak.services.managers.UsersSyncManager;
-import org.keycloak.timer.TimerProvider;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -30,14 +18,12 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import java.util.HashMap;
+
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Base resource for managing users
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
index 04e3c7f..47f1b65 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
@@ -10,7 +10,7 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderFactory;
 import org.keycloak.models.UserFederationProviderModel;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
index d89c1bf..f8cc934 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
+++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
@@ -5,5 +5,6 @@ org.keycloak.protocol.oidc.mappers.OIDCAddressMapper
 org.keycloak.protocol.oidc.mappers.OIDCAddClaimMapper
 org.keycloak.protocol.oidc.mappers.OIDCAddRoleMapper
 org.keycloak.protocol.oidc.mappers.OIDCRoleMapper
+org.keycloak.protocol.oidc.mappers.OIDCUserSessionNoteMapper
 
 
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPConfiguration.java b/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPConfiguration.java
index 63445b2..94b7672 100644
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPConfiguration.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPConfiguration.java
@@ -8,7 +8,7 @@ import java.util.Map;
 import java.util.Properties;
 
 import org.jboss.logging.Logger;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.models.LDAPConstants;
 import org.keycloak.models.UserFederationProvider;
 
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPEmbeddedServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPEmbeddedServer.java
index 1bf4f80..4fde15b 100644
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPEmbeddedServer.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/LDAPEmbeddedServer.java
@@ -121,6 +121,7 @@ public class LDAPEmbeddedServer {
         LdapServer ldapServer = new LdapServer();
 
         ldapServer.setServiceName("DefaultLdapServer");
+        ldapServer.setSearchBaseDn(this.baseDN);
 
         // Read the transports
         Transport ldap = new TcpTransport(this.bindHost, this.bindPort, 3, 50);
diff --git a/testsuite/integration/src/main/resources/kerberos/http.keytab b/testsuite/integration/src/main/resources/kerberos/http.keytab
index c156500..0e7fd96 100644
Binary files a/testsuite/integration/src/main/resources/kerberos/http.keytab and b/testsuite/integration/src/main/resources/kerberos/http.keytab differ
diff --git a/testsuite/integration/src/main/resources/kerberos/test-krb5.conf b/testsuite/integration/src/main/resources/kerberos/test-krb5.conf
index 350c086..a775b47 100644
--- a/testsuite/integration/src/main/resources/kerberos/test-krb5.conf
+++ b/testsuite/integration/src/main/resources/kerberos/test-krb5.conf
@@ -1,13 +1,14 @@
 [libdefaults]
     default_realm = KEYCLOAK.ORG
-    default_tgs_enctypes = des3-cbc-sha1-kd rc4-hmac
-    default_tkt_enctypes = des3-cbc-sha1-kd rc4-hmac
-    permitted_enctypes = des3-cbc-sha1-kd rc4-hmac
+    default_tgs_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac
+    default_tkt_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac
+    permitted_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac
     kdc_timeout = 30000
     dns_lookup_realm = false
     dns_lookup_kdc = false
     dns_canonicalize_hostname = false
     ignore_acceptor_hostname = true
+    forwardable = true
 
 [realms]
     KEYCLOAK.ORG = {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
index 0bb3cd2..2cbe7ae 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
@@ -16,31 +16,33 @@ import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.FixMethodOrder;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runners.MethodSorters;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.adapters.HttpClientBuilder;
 import org.keycloak.events.Details;
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
-import org.keycloak.federation.kerberos.KerberosConfig;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
+import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
+import org.keycloak.protocol.oidc.mappers.OIDCUserSessionNoteMapper;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.adapter.AdapterTest;
+import org.keycloak.testsuite.adapter.AdapterTestStrategy;
 import org.keycloak.testsuite.pages.AccountPasswordPage;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
-import org.keycloak.testsuite.rule.WebRule;
 import org.openqa.selenium.WebDriver;
 
 /**
@@ -48,6 +50,8 @@ import org.openqa.selenium.WebDriver;
  */
 public abstract class AbstractKerberosTest {
 
+    protected String KERBEROS_APP_URL = "http://localhost:8081/kerberos-portal";
+
     protected KeycloakSPNegoSchemeFactory spnegoSchemeFactory;
     protected ResteasyClient client;
 
@@ -63,9 +67,6 @@ public abstract class AbstractKerberosTest {
     @WebResource
     protected AccountPasswordPage changePasswordPage;
 
-    @WebResource
-    protected AppPage appPage;
-
     protected abstract CommonKerberosConfig getKerberosConfig();
     protected abstract KeycloakRule getKeycloakRule();
     protected abstract AssertEvents getAssertEvents();
@@ -88,7 +89,11 @@ public abstract class AbstractKerberosTest {
     @Test
     public void spnegoNotAvailableTest() throws Exception {
         initHttpClient(false);
-        Response response = client.target(oauth.getLoginFormUrl()).request().get();
+
+        driver.navigate().to(KERBEROS_APP_URL);
+        String kcLoginPageLocation = driver.getCurrentUrl();
+
+        Response response = client.target(kcLoginPageLocation).request().get();
         Assert.assertEquals(401, response.getStatus());
         Assert.assertEquals(KerberosConstants.NEGOTIATE, response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE));
         String responseText = response.readEntity(String.class);
@@ -105,17 +110,21 @@ public abstract class AbstractKerberosTest {
         Assert.assertEquals(302, spnegoResponse.getStatus());
 
         events.expectLogin()
+                .client("kerberos-app")
                 .user(keycloakRule.getUser("test", "hnelson").getId())
+                .detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
                 .detail(Details.AUTH_METHOD, "spnego")
                 .detail(Details.USERNAME, "hnelson")
                 .assertEvent();
 
         String location = spnegoResponse.getLocation().toString();
         driver.navigate().to(location);
-        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
-        Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+        String pageSource = driver.getPageSource();
+        Assert.assertTrue(pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content"));
 
         spnegoResponse.close();
+        events.clear();
     }
 
 
@@ -157,18 +166,72 @@ public abstract class AbstractKerberosTest {
         Response spnegoResponse = spnegoLogin("jduke", "theduke");
         Assert.assertEquals(302, spnegoResponse.getStatus());
         events.expectLogin()
+                .client("kerberos-app")
                 .user(keycloakRule.getUser("test", "jduke").getId())
+                .detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
                 .detail(Details.AUTH_METHOD, "spnego")
                 .detail(Details.USERNAME, "jduke")
                 .assertEvent();
         spnegoResponse.close();
     }
 
+    @Test
+    public void credentialDelegationTest() throws Exception {
+        // Add kerberos delegation credential mapper
+        getKeycloakRule().update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                ProtocolMapperModel protocolMapper = OIDCUserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
+                        KerberosConstants.GSS_DELEGATION_CREDENTIAL,
+                        KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
+                        true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
+                        true, false);
+
+                ApplicationModel kerberosApp = appRealm.getApplicationByName("kerberos-app");
+                kerberosApp.addProtocolMapper(protocolMapper);
+            }
+
+        });
+
+        // SPNEGO login
+        spnegoLoginTestImpl();
+
+        // Assert servlet authenticated to LDAP with delegated credential
+        driver.navigate().to(KERBEROS_APP_URL + KerberosCredDelegServlet.CRED_DELEG_TEST_PATH);
+        String pageSource = driver.getPageSource();
+        Assert.assertTrue(pageSource.contains("LDAP Data: Horatio Nelson"));
+
+        // Remove kerberos delegation credential mapper
+        getKeycloakRule().update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                ApplicationModel kerberosApp = appRealm.getApplicationByName("kerberos-app");
+                ProtocolMapperModel toRemove = kerberosApp.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
+                kerberosApp.removeProtocolMapper(toRemove);
+            }
+
+        });
+
+        // Clear driver and login again. I can't invoke LDAP now as GSS Credential is not in accessToken
+        driver.manage().deleteAllCookies();
+        spnegoLoginTestImpl();
+        driver.navigate().to(KERBEROS_APP_URL + KerberosCredDelegServlet.CRED_DELEG_TEST_PATH);
+        pageSource = driver.getPageSource();
+        Assert.assertFalse(pageSource.contains("LDAP Data: Horatio Nelson"));
+        Assert.assertTrue(pageSource.contains("LDAP Data: ERROR"));
+    }
+
 
 
     protected Response spnegoLogin(String username, String password) {
+        driver.navigate().to(KERBEROS_APP_URL);
+        String kcLoginPageLocation = driver.getCurrentUrl();
+
+        // Request for SPNEGO login sent with Resteasy client
         spnegoSchemeFactory.setCredentials(username, password);
-        return client.target(oauth.getLoginFormUrl()).request().get();
+        return client.target(kcLoginPageLocation).request().get();
     }
 
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosCredDelegServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosCredDelegServlet.java
new file mode 100644
index 0000000..22baba4
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosCredDelegServlet.java
@@ -0,0 +1,96 @@
+package org.keycloak.testsuite.federation;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.security.sasl.Sasl;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.ietf.jgss.GSSCredential;
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.constants.KerberosConstants;
+import org.keycloak.util.KerberosSerializationUtils;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KerberosCredDelegServlet extends HttpServlet {
+
+    public static final String CRED_DELEG_TEST_PATH = "/cred-deleg-test";
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        String ldapData = null;
+
+        if (req.getRequestURI().endsWith(CRED_DELEG_TEST_PATH)) {
+
+            try {
+                // Retrieve kerberos credential from accessToken and deserialize it
+                KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) req.getUserPrincipal();
+                String serializedGssCredential = (String) keycloakPrincipal.getKeycloakSecurityContext().getToken().getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
+                GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential);
+
+                // First try to invoke without gssCredential. It should fail
+                try {
+                    invokeLdap(null);
+                    throw new RuntimeException("Not expected to authenticate to LDAP without credential");
+                } catch (NamingException nse) {
+                    System.out.println("Expected exception: " + nse.getMessage());
+                }
+
+                ldapData = invokeLdap(gssCredential);
+            } catch (KerberosSerializationUtils.KerberosSerializationException kse) {
+                System.err.println("KerberosSerializationUtils.KerberosSerializationException: " + kse.getMessage());
+                ldapData = "ERROR";
+            } catch (Exception e) {
+                e.printStackTrace();
+                resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+        }
+
+        resp.setContentType("text/html");
+        PrintWriter pw = resp.getWriter();
+        pw.printf("<html><head><title>%s</title></head><body>", "Kerberos Test");
+        pw.printf("Kerberos servlet secured content<br>");
+
+        if (ldapData != null) {
+            pw.printf("LDAP Data: " + ldapData + "<br>");
+        }
+
+        pw.print("</body></html>");
+        pw.flush();
+
+
+    }
+
+    private String invokeLdap(GSSCredential gssCredential) throws NamingException {
+        Hashtable env = new Hashtable(11);
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
+
+        if (gssCredential != null) {
+            env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
+            env.put(Sasl.CREDENTIALS, gssCredential);
+        }
+
+        DirContext ctx = new InitialDirContext(env);
+        try {
+            Attributes attrs = ctx.getAttributes("uid=hnelson,ou=People,dc=keycloak,dc=org");
+            String cn = (String) attrs.get("cn").get();
+            String sn = (String) attrs.get("sn").get();
+            return cn + " " + sn;
+        } finally {
+            ctx.close();
+        }
+    }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
index 4123508..07ca87c 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
@@ -1,43 +1,29 @@
 package org.keycloak.testsuite.federation;
 
+import java.net.URL;
 import java.util.Map;
 
-import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 
 import org.junit.Assert;
 import org.junit.ClassRule;
-import org.junit.FixMethodOrder;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
 import org.junit.rules.TestRule;
-import org.junit.runners.MethodSorters;
-import org.keycloak.OAuth2Constants;
 import org.keycloak.events.Details;
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
-import org.keycloak.federation.kerberos.KerberosConfig;
-import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
 import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
 import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig;
-import org.keycloak.models.KerberosConstants;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
-import org.keycloak.testsuite.OAuthClient;
-import org.keycloak.testsuite.pages.AccountPasswordPage;
-import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
-import org.keycloak.testsuite.pages.AppPage;
-import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.pages.RegisterPage;
 import org.keycloak.testsuite.rule.KerberosRule;
 import org.keycloak.testsuite.rule.KeycloakRule;
-import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
-import org.openqa.selenium.WebDriver;
 
 /**
  * Test of LDAPFederationProvider (Kerberos backed by LDAP)
@@ -46,21 +32,30 @@ import org.openqa.selenium.WebDriver;
  */
 public class KerberosLdapTest extends AbstractKerberosTest {
 
-    public static final String CONFIG_LOCATION = "kerberos/kerberos-ldap-connection.properties";
+    private static final String PROVIDER_CONFIG_LOCATION = "kerberos/kerberos-ldap-connection.properties";
 
     private static UserFederationProviderModel ldapModel = null;
 
-    private static KerberosRule kerberosRule = new KerberosRule(CONFIG_LOCATION);
+    private static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
 
     private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
 
         @Override
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+            URL url = getClass().getResource("/kerberos-test/kerberos-app-keycloak.json");
+            keycloakRule.deployApplication("kerberos-portal", "/kerberos-portal", KerberosCredDelegServlet.class, url.getPath(), "user");
+
             Map<String,String> ldapConfig = kerberosRule.getConfig();
             ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "kerberos-ldap", -1, -1, 0);
-            appRealm.addRequiredCredential(UserCredentialModel.KERBEROS);
         }
-    });
+    }) {
+
+        @Override
+        protected void importRealm() {
+            server.importRealm(getClass().getResourceAsStream("/kerberos-test/kerberosrealm.json"));
+        }
+
+    };
 
     @ClassRule
     public static TestRule chain = RuleChain
@@ -129,12 +124,15 @@ public class KerberosLdapTest extends AbstractKerberosTest {
         Response spnegoResponse = spnegoLogin("jduke", "newPass");
         Assert.assertEquals(302, spnegoResponse.getStatus());
         events.expectLogin()
+                .client("kerberos-app")
                 .user(keycloakRule.getUser("test", "jduke").getId())
+                .detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
                 .detail(Details.AUTH_METHOD, "spnego")
                 .detail(Details.USERNAME, "jduke")
                 .assertEvent();
 
         // Change password back
+        changePasswordPage.open();
         loginPage.login("jduke", "newPass");
         changePasswordPage.assertCurrent();
         changePasswordPage.changePassword("newPass", "theduke", "theduke");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosStandaloneTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosStandaloneTest.java
index 65a753e..c665f44 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosStandaloneTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosStandaloneTest.java
@@ -1,5 +1,6 @@
 package org.keycloak.testsuite.federation;
 
+import java.net.URL;
 import java.util.Map;
 
 import javax.ws.rs.core.Response;
@@ -13,16 +14,20 @@ import org.junit.rules.TestRule;
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
 import org.keycloak.federation.kerberos.KerberosConfig;
 import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
-import org.keycloak.models.KerberosConstants;
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.adapter.CustomerServlet;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.KerberosRule;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.testutils.KeycloakServer;
 
 /**
  * Test of KerberosFederationProvider (Kerberos not backed by LDAP)
@@ -31,22 +36,31 @@ import org.keycloak.testsuite.rule.WebRule;
  */
 public class KerberosStandaloneTest extends AbstractKerberosTest {
 
-    public static final String CONFIG_LOCATION = "kerberos/kerberos-standalone-connection.properties";
+    private static final String PROVIDER_CONFIG_LOCATION = "kerberos/kerberos-standalone-connection.properties";
 
     private static UserFederationProviderModel kerberosModel;
 
-    private static KerberosRule kerberosRule = new KerberosRule(CONFIG_LOCATION);
+    private static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
 
     private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
 
         @Override
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
-            Map<String,String> kerberosConfig = kerberosRule.getConfig();
+            URL url = getClass().getResource("/kerberos-test/kerberos-app-keycloak.json");
+            keycloakRule.deployApplication("kerberos-portal", "/kerberos-portal", KerberosCredDelegServlet.class, url.getPath(), "user");
 
+            Map<String,String> kerberosConfig = kerberosRule.getConfig();
             kerberosModel = appRealm.addUserFederationProvider(KerberosFederationProviderFactory.PROVIDER_NAME, kerberosConfig, 0, "kerberos-standalone", -1, -1, 0);
-            appRealm.addRequiredCredential(UserCredentialModel.KERBEROS);
         }
-    });
+
+    }) {
+
+        @Override
+        protected void importRealm() {
+            server.importRealm(getClass().getResourceAsStream("/kerberos-test/kerberosrealm.json"));
+        }
+
+    };
 
 
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
index 957b66b..915ed5a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
@@ -137,7 +137,7 @@ public class RefreshTokenTest {
 
         Assert.assertEquals("bearer", tokenResponse.getTokenType());
 
-        Assert.assertThat(token.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
+        Assert.assertThat(token.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(200), lessThanOrEqualTo(350)));
         int actual = refreshToken.getExpiration() - Time.currentTime();
         Assert.assertThat(actual, allOf(greaterThanOrEqualTo(1799), lessThanOrEqualTo(1800)));
 
@@ -313,7 +313,7 @@ public class RefreshTokenTest {
         session.close();
 
         // lastSEssionRefresh should be updated because access code lifespan is higher than sso idle timeout
-        Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 6)));
+        Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 50)));
 
         session = keycloakRule.startSession();
         realm = session.realms().getRealmByName("test");
diff --git a/testsuite/integration/src/test/resources/kerberos-test/kerberos-app-keycloak.json b/testsuite/integration/src/test/resources/kerberos-test/kerberos-app-keycloak.json
new file mode 100644
index 0000000..609bcdb
--- /dev/null
+++ b/testsuite/integration/src/test/resources/kerberos-test/kerberos-app-keycloak.json
@@ -0,0 +1,10 @@
+{
+    "realm": "test",
+    "resource": "kerberos-app",
+    "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "auth-server-url": "http://localhost:8081/auth",
+    "ssl-required" : "external",
+    "credentials": {
+        "secret": "password"
+    }
+}
diff --git a/testsuite/integration/src/test/resources/kerberos-test/kerberosrealm.json b/testsuite/integration/src/test/resources/kerberos-test/kerberosrealm.json
new file mode 100644
index 0000000..b0fb903
--- /dev/null
+++ b/testsuite/integration/src/test/resources/kerberos-test/kerberosrealm.json
@@ -0,0 +1,54 @@
+{
+    "id": "test",
+    "realm": "test",
+    "enabled": true,
+    "sslRequired": "external",
+    "registrationAllowed": true,
+    "resetPasswordAllowed": true,
+    "passwordCredentialGrantAllowed": true,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password", "kerberos" ],
+    "defaultRoles": [ "user" ],
+    "users" : [
+        {
+            "username" : "test-user@localhost",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                  "value" : "password" }
+            ],
+            "realmRoles": ["user"],
+            "applicationRoles": {
+                "account": [ "view-profile", "manage-account" ]
+            }
+        }
+    ],
+    "scopeMappings": [
+        {
+            "client": "kerberos-app",
+            "roles": ["user"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "kerberos-app",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/kerberos-portal",
+            "redirectUris": [
+                "http://localhost:8081/kerberos-portal/*"
+            ],
+            "adminUrl": "http://localhost:8081/kerberos-portal/logout",
+            "secret": "password"
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "user",
+                "description": "Have User privileges"
+            }
+        ]
+    }
+}
\ No newline at end of file