keycloak-aplcache

saved requests

12/18/2014 1:29:18 AM

Changes

integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java 32(+0 -32)

integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java 32(+0 -32)

integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettyRequestAuthenticator.java 32(+0 -32)

Details

diff --git a/core/src/main/java/org/keycloak/util/UriUtils.java b/core/src/main/java/org/keycloak/util/UriUtils.java
old mode 100644
new mode 100755
index 3f0f9af..70c6824
--- a/core/src/main/java/org/keycloak/util/UriUtils.java
+++ b/core/src/main/java/org/keycloak/util/UriUtils.java
@@ -1,6 +1,8 @@
 package org.keycloak.util;
 
+import java.io.UnsupportedEncodingException;
 import java.net.URI;
+import java.net.URLDecoder;
 import java.util.regex.Pattern;
 
 /**
@@ -23,4 +25,42 @@ public class UriUtils {
         return originPattern.matcher(url).matches();
     }
 
+    public static MultivaluedHashMap<String, String> decodeQueryString(String queryString) {
+        MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
+        if (queryString == null || queryString.equals("")) return map;
+
+        String[] params = queryString.split("&");
+
+        for (String param : params)
+        {
+            if (param.indexOf('=') >= 0)
+            {
+                String[] nv = param.split("=", 2);
+                try
+                {
+                    String name = URLDecoder.decode(nv[0], "UTF-8");
+                    String val = nv.length > 1 ? nv[1] : "";
+                    map.add(name, URLDecoder.decode(val, "UTF-8"));
+                }
+                catch (UnsupportedEncodingException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+            else
+            {
+                try
+                {
+                    String name = URLDecoder.decode(param, "UTF-8");
+                    map.add(name, "");
+                }
+                catch (UnsupportedEncodingException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+        return map;
+    }
+
 }
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
old mode 100644
new mode 100755
index 0d3be96..d07bffa
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterTokenStore.java
@@ -38,4 +38,7 @@ public interface AdapterTokenStore {
      * @param securityContext context where refresh was performed
      */
     void refreshCallback(RefreshableKeycloakSecurityContext securityContext);
+
+    void saveRequest();
+    boolean restoreRequest();
 }
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
index 312576e..48b5ed9 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
@@ -19,11 +19,12 @@ import java.util.concurrent.atomic.AtomicLong;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public abstract class OAuthRequestAuthenticator {
+public class OAuthRequestAuthenticator {
     private static final Logger log = Logger.getLogger(OAuthRequestAuthenticator.class);
     protected KeycloakDeployment deployment;
     protected RequestAuthenticator reqAuthenticator;
     protected int sslRedirectPort;
+    protected AdapterTokenStore tokenStore;
     protected String tokenString;
     protected String idTokenString;
     protected IDToken idToken;
@@ -33,11 +34,12 @@ public abstract class OAuthRequestAuthenticator {
     protected String refreshToken;
     protected String strippedOauthParametersRequestUri;
 
-    public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort) {
+    public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort, AdapterTokenStore tokenStore) {
         this.reqAuthenticator = requestAuthenticator;
         this.facade = facade;
         this.deployment = deployment;
         this.sslRedirectPort = sslRedirectPort;
+        this.tokenStore = tokenStore;
     }
 
     public AuthChallenge getChallenge() {
@@ -200,7 +202,7 @@ public abstract class OAuthRequestAuthenticator {
             } else {
                 log.debug("redirecting to auth server");
                 challenge = loginRedirect();
-                saveRequest();
+                tokenStore.saveRequest();
                 return AuthOutcome.NOT_ATTEMPTED;
             }
         } else {
@@ -214,12 +216,6 @@ public abstract class OAuthRequestAuthenticator {
 
     }
 
-    /**
-     * Cache the request so that when we get redirected back, it gets invoked
-     *
-     */
-    protected abstract void saveRequest();
-
     protected AuthChallenge challenge(final int code) {
         return new AuthChallenge() {
             @Override
diff --git a/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java b/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java
new file mode 100755
index 0000000..e2dcfa5
--- /dev/null
+++ b/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java
@@ -0,0 +1,93 @@
+package org.keycloak.adapters.jetty;
+
+import org.eclipse.jetty.security.authentication.FormAuthenticator;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.MultiMap;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.util.MultivaluedHashMap;
+
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
+    public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
+    protected Request myRequest;
+
+    public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
+        super(request, deployment);
+        this.myRequest = request; // for IDE/compilation purposes
+    }
+
+    protected MultiMap<String> extractFormParameters(Request base_request) {
+        MultiMap<String> formParameters = new MultiMap<String>();
+        base_request.extractParameters();
+        return base_request.getParameters();
+    }
+    protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
+        base_request.setParameters(j_post);
+    }
+
+    public boolean restoreRequest() {
+        HttpSession session = myRequest.getSession(false);
+        if (session == null) return false;
+        synchronized (session) {
+            String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
+            if (j_uri != null) {
+                // check if the request is for the same url as the original and restore
+                // params if it was a post
+                StringBuffer buf = myRequest.getRequestURL();
+                if (myRequest.getQueryString() != null)
+                    buf.append("?").append(myRequest.getQueryString());
+                if (j_uri.equals(buf.toString())) {
+                    String method = (String)session.getAttribute(__J_METHOD);
+                    myRequest.setMethod(method);
+                    MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
+                    if (j_post != null) {
+                        MultiMap<String> map = new MultiMap<String>();
+                        for (String key : j_post.keySet()) {
+                            for (String val : j_post.getList(key)) {
+                                map.add(key, val);
+                            }
+                        }
+                        restoreFormParameters(map, myRequest);
+                    }
+                    session.removeAttribute(FormAuthenticator.__J_URI);
+                    session.removeAttribute(__J_METHOD);
+                    session.removeAttribute(FormAuthenticator.__J_POST);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void saveRequest() {
+        // remember the current URI
+        HttpSession session = myRequest.getSession();
+        synchronized (session) {
+            // But only if it is not set already, or we save every uri that leads to a login form redirect
+            if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
+                StringBuffer buf = myRequest.getRequestURL();
+                if (myRequest.getQueryString() != null)
+                    buf.append("?").append(myRequest.getQueryString());
+                session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
+                session.setAttribute(__J_METHOD, myRequest.getMethod());
+
+                if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
+                    MultiMap<String> formParameters = extractFormParameters(myRequest);
+                    MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
+                    for (String key : formParameters.keySet()) {
+                        for (Object value : formParameters.getValues(key)) {
+                            map.add(key, (String) value);
+                        }
+                    }
+                    session.setAttribute(CACHED_FORM_PARAMETERS, map);
+                }
+            }
+        }
+    }
+
+}
diff --git a/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
index 06e1d99..d1ae6e0 100755
--- a/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty8.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
@@ -21,8 +21,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
 
 
     @Override
-    protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
-        return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
+    public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
+        return new JettySessionTokenStore(request, resolvedDeployment);
     }
 
     @Override
diff --git a/integration/jetty/jetty9.1/pom.xml b/integration/jetty/jetty9.1/pom.xml
index 0ce853b..6c23f5a 100755
--- a/integration/jetty/jetty9.1/pom.xml
+++ b/integration/jetty/jetty9.1/pom.xml
@@ -36,6 +36,20 @@
             <groupId>org.keycloak</groupId>
             <artifactId>keycloak-jetty-core</artifactId>
             <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-server</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-util</artifactId>
+               </exclusion>
+                <exclusion>
+                    <groupId>org.eclipse.jetty</groupId>
+                    <artifactId>jetty-security</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
diff --git a/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java
new file mode 100755
index 0000000..24fc9b2
--- /dev/null
+++ b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java
@@ -0,0 +1,94 @@
+package org.keycloak.adapters.jetty;
+
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.security.authentication.FormAuthenticator;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.MultiMap;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.util.MultivaluedHashMap;
+
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
+    public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
+    protected Request myRequest;
+
+    public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
+        super(request, deployment);
+        this.myRequest = request; // for IDE/compilation purposes
+    }
+
+    protected MultiMap<String> extractFormParameters(Request base_request) {
+        MultiMap<String> formParameters = new MultiMap<String>();
+        base_request.extractParameters();
+        return base_request.getParameters();
+    }
+    protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
+        base_request.setParameters(j_post);
+    }
+
+    public boolean restoreRequest() {
+        HttpSession session = myRequest.getSession(false);
+        if (session == null) return false;
+        synchronized (session) {
+            String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
+            if (j_uri != null) {
+                // check if the request is for the same url as the original and restore
+                // params if it was a post
+                StringBuffer buf = myRequest.getRequestURL();
+                if (myRequest.getQueryString() != null)
+                    buf.append("?").append(myRequest.getQueryString());
+                if (j_uri.equals(buf.toString())) {
+                    String method = (String)session.getAttribute(__J_METHOD);
+                    myRequest.setMethod(HttpMethod.valueOf(method.toUpperCase()), method);
+                    MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
+                    if (j_post != null) {
+                        MultiMap<String> map = new MultiMap<String>();
+                        for (String key : j_post.keySet()) {
+                            for (String val : j_post.getList(key)) {
+                                map.add(key, val);
+                            }
+                        }
+                        restoreFormParameters(map, myRequest);
+                    }
+                    session.removeAttribute(FormAuthenticator.__J_URI);
+                    session.removeAttribute(__J_METHOD);
+                    session.removeAttribute(FormAuthenticator.__J_POST);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void saveRequest() {
+        // remember the current URI
+        HttpSession session = myRequest.getSession();
+        synchronized (session) {
+            // But only if it is not set already, or we save every uri that leads to a login form redirect
+            if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
+                StringBuffer buf = myRequest.getRequestURL();
+                if (myRequest.getQueryString() != null)
+                    buf.append("?").append(myRequest.getQueryString());
+                session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
+                session.setAttribute(__J_METHOD, myRequest.getMethod());
+
+                if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
+                    MultiMap<String> formParameters = extractFormParameters(myRequest);
+                    MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
+                    for (String key : formParameters.keySet()) {
+                        for (Object value : formParameters.getValues(key)) {
+                            map.add(key, (String) value);
+                        }
+                    }
+                    session.setAttribute(CACHED_FORM_PARAMETERS, map);
+                }
+            }
+        }
+    }
+
+}
diff --git a/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
index 7ce92f8..db15f33 100755
--- a/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty9.1/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
@@ -21,8 +21,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
 
 
     @Override
-    protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
-        return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
+    public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
+        return new JettySessionTokenStore(request, resolvedDeployment);
     }
 
     @Override
diff --git a/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java
new file mode 100755
index 0000000..4ff9af9
--- /dev/null
+++ b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/JettySessionTokenStore.java
@@ -0,0 +1,95 @@
+package org.keycloak.adapters.jetty;
+
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.security.authentication.FormAuthenticator;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.MultiMap;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.util.MultivaluedHashMap;
+
+import javax.servlet.http.HttpSession;
+import java.lang.reflect.Field;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
+    public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
+    protected Request myRequest;
+
+    public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
+        super(request, deployment);
+        this.myRequest = request; // for IDE/compilation purposes
+    }
+
+    protected MultiMap<String> extractFormParameters(Request base_request) {
+        MultiMap<String> formParameters = new MultiMap<String>();
+        base_request.extractFormParameters(formParameters);
+        return formParameters;
+    }
+    protected void restoreFormParameters(MultiMap<String> j_post, Request base_request) {
+        base_request.setContentParameters(j_post);
+    }
+
+    public boolean restoreRequest() {
+        HttpSession session = myRequest.getSession(false);
+        if (session == null) return false;
+        synchronized (session) {
+            String j_uri = (String) session.getAttribute(FormAuthenticator.__J_URI);
+            if (j_uri != null) {
+                // check if the request is for the same url as the original and restore
+                // params if it was a post
+                StringBuffer buf = myRequest.getRequestURL();
+                if (myRequest.getQueryString() != null)
+                    buf.append("?").append(myRequest.getQueryString());
+                if (j_uri.equals(buf.toString())) {
+                    String method = (String)session.getAttribute(__J_METHOD);
+                    myRequest.setMethod(HttpMethod.valueOf(method.toUpperCase()), method);
+                    MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
+                    if (j_post != null) {
+                        MultiMap<String> map = new MultiMap<String>();
+                        for (String key : j_post.keySet()) {
+                            for (String val : j_post.getList(key)) {
+                                map.add(key, val);
+                            }
+                        }
+                        restoreFormParameters(map, myRequest);
+                    }
+                    session.removeAttribute(FormAuthenticator.__J_URI);
+                    session.removeAttribute(__J_METHOD);
+                    session.removeAttribute(FormAuthenticator.__J_POST);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void saveRequest() {
+        // remember the current URI
+        HttpSession session = myRequest.getSession();
+        synchronized (session) {
+            // But only if it is not set already, or we save every uri that leads to a login form redirect
+            if (session.getAttribute(FormAuthenticator.__J_URI) == null) {
+                StringBuffer buf = myRequest.getRequestURL();
+                if (myRequest.getQueryString() != null)
+                    buf.append("?").append(myRequest.getQueryString());
+                session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
+                session.setAttribute(__J_METHOD, myRequest.getMethod());
+
+                if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
+                    MultiMap<String> formParameters = extractFormParameters(myRequest);
+                    MultivaluedHashMap<String, String> map = new MultivaluedHashMap<String, String>();
+                    for (String key : formParameters.keySet()) {
+                        for (Object value : formParameters.getValues(key)) {
+                            map.add(key, (String) value);
+                        }
+                    }
+                    session.setAttribute(CACHED_FORM_PARAMETERS, map);
+                }
+            }
+        }
+    }
+
+}
diff --git a/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
index d38e7d4..b43e27c 100755
--- a/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty9.2/src/main/java/org/keycloak/adapters/jetty/KeycloakJettyAuthenticator.java
@@ -20,12 +20,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
     }
 
 
-    @Override
-    protected AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
-        return new JettyRequestAuthenticator(deployment, this, tokenStore, facade, request);
-    }
-
-    @Override
+   @Override
     protected Request resolveRequest(ServletRequest req) {
         return (req instanceof Request) ? (Request)req : HttpChannel.getCurrentHttpChannel().getRequest();
     }
@@ -40,6 +35,8 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
         };
     }
 
-
-
+    @Override
+    public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
+        return new JettySessionTokenStore(request, resolvedDeployment);
+    }
 }
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractKeycloakJettyAuthenticator.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractKeycloakJettyAuthenticator.java
index 3d813e6..2e78eed 100755
--- a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractKeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractKeycloakJettyAuthenticator.java
@@ -65,14 +65,14 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
         return new ByteArrayInputStream(json.getBytes());
     }
 
-    public static AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) {
+    public AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) {
         AdapterTokenStore store = (AdapterTokenStore)request.getAttribute(TOKEN_STORE_NOTE);
         if (store != null) {
             return store;
         }
 
         if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
-            store = new JettySessionTokenStore(request, resolvedDeployment);
+            store = createSessionTokenStore(request, resolvedDeployment);
         } else {
             store = new JettyCookieTokenStore(request, facade, resolvedDeployment);
         }
@@ -81,7 +81,9 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
         return store;
     }
 
-    public static void logoutCurrent(Request request) {
+    public abstract AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment);
+
+    public void logoutCurrent(Request request) {
         AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext)request.getAttribute(AdapterDeploymentContext.class.getName());
         KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
         if (ksc != null) {
@@ -212,7 +214,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
         nodesRegistrationManagement.tryRegister(deployment);
 
         tokenStore.checkCurrentToken();
-        AbstractJettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore);
+        JettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore);
         AuthOutcome outcome = authenticator.authenticate();
         if (outcome == AuthOutcome.AUTHENTICATED) {
             if (facade.isEnded()) {
@@ -238,7 +240,10 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
 
     protected abstract Request resolveRequest(ServletRequest req);
 
-    protected abstract AbstractJettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore);
+    protected JettyRequestAuthenticator createRequestAuthenticator(Request request, JettyHttpFacade facade,
+                                                                           KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
+        return new JettyRequestAuthenticator(facade, deployment, tokenStore, -1, request);
+    }
 
     @Override
     public String getAuthMethod() {
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java
index c0b1685..3020f75 100755
--- a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyCookieTokenStore.java
@@ -99,4 +99,14 @@ public class JettyCookieTokenStore implements AdapterTokenStore {
         CookieTokenStore.removeCookie(facade);
         return null;
     }
+
+    @Override
+    public void saveRequest() {
+
+    }
+
+    @Override
+    public boolean restoreRequest() {
+        return false;
+    }
 }
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java
index 75520ab..5a8c4c3 100755
--- a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/JettyHttpFacade.java
@@ -2,6 +2,8 @@ package org.keycloak.adapters.jetty;
 
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.HttpFacade;
+import org.keycloak.util.MultivaluedHashMap;
+import org.keycloak.util.UriUtils;
 
 import javax.security.cert.X509Certificate;
 import javax.servlet.http.HttpServletResponse;
@@ -21,6 +23,7 @@ public class JettyHttpFacade implements HttpFacade {
     protected HttpServletResponse response;
     protected RequestFacade requestFacade = new RequestFacade();
     protected ResponseFacade responseFacade = new ResponseFacade();
+    protected MultivaluedHashMap<String, String> queryParameters;
 
     protected class RequestFacade implements Request {
         @Override
@@ -39,7 +42,10 @@ public class JettyHttpFacade implements HttpFacade {
 
         @Override
         public String getQueryParamValue(String paramName) {
-            return request.getParameter(paramName);
+            if (queryParameters == null) {
+                queryParameters = UriUtils.decodeQueryString(request.getQueryString());
+            }
+            return queryParameters.getFirst(paramName);
         }
 
         @Override
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
index cb05e72..acf33a4 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
@@ -5,9 +5,12 @@ import org.apache.catalina.Lifecycle;
 import org.apache.catalina.LifecycleEvent;
 import org.apache.catalina.LifecycleListener;
 import org.apache.catalina.Manager;
+import org.apache.catalina.authenticator.Constants;
 import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.authenticator.SavedRequest;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.adapters.AdapterDeploymentContext;
@@ -24,12 +27,15 @@ import org.keycloak.enums.TokenStore;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletResponse;
 import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Locale;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.keycloak.adapters.KeycloakConfigResolver;
@@ -180,7 +186,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
 
         nodesRegistrationManagement.tryRegister(deployment);
 
-        CatalinaRequestAuthenticator authenticator = new CatalinaRequestAuthenticator(deployment, this, tokenStore, facade, request, createPrincipalFactory());
+        CatalinaRequestAuthenticator authenticator = new CatalinaRequestAuthenticator(deployment, tokenStore, facade, request, createPrincipalFactory());
         AuthOutcome outcome = authenticator.authenticate();
         if (outcome == AuthOutcome.AUTHENTICATED) {
             if (facade.isEnded()) {
@@ -225,7 +231,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
         }
 
         if (resolvedDeployment.getTokenStore() == TokenStore.SESSION) {
-            store = new CatalinaSessionTokenStore(request, resolvedDeployment, userSessionManagement, createPrincipalFactory());
+            store = new CatalinaSessionTokenStore(request, resolvedDeployment, userSessionManagement, createPrincipalFactory(), this);
         } else {
             store = new CatalinaCookieTokenStore(request, facade, resolvedDeployment, createPrincipalFactory());
         }
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java
index 229efa3..b0b21ab 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java
@@ -84,6 +84,16 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
         CookieTokenStore.setTokenCookie(deployment, facade, secContext);
     }
 
+    @Override
+    public void saveRequest() {
+
+    }
+
+    @Override
+    public boolean restoreRequest() {
+        return false;
+    }
+
     /**
      * Verify if we already have authenticated and active principal in cookie. Perform refresh if it's not active
      *
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaHttpFacade.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaHttpFacade.java
index cf76d13..784a1fa 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaHttpFacade.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaHttpFacade.java
@@ -2,7 +2,9 @@ package org.keycloak.adapters.tomcat;
 
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.HttpFacade;
+import org.keycloak.util.MultivaluedHashMap;
 import org.keycloak.util.ServerCookie;
+import org.keycloak.util.UriUtils;
 
 import javax.security.cert.X509Certificate;
 import javax.servlet.http.HttpServletResponse;
@@ -22,6 +24,7 @@ public class CatalinaHttpFacade implements HttpFacade {
     protected HttpServletResponse response;
     protected RequestFacade requestFacade = new RequestFacade();
     protected ResponseFacade responseFacade = new ResponseFacade();
+    protected MultivaluedHashMap<String, String> queryParameters;
 
     protected class RequestFacade implements Request {
         @Override
@@ -40,7 +43,10 @@ public class CatalinaHttpFacade implements HttpFacade {
 
         @Override
         public String getQueryParamValue(String paramName) {
-            return request.getParameter(paramName);
+            if (queryParameters == null) {
+                queryParameters = UriUtils.decodeQueryString(request.getQueryString());
+            }
+            return queryParameters.getFirst(paramName);
         }
 
         @Override
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java
index 13d6835..58989ef 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java
@@ -27,36 +27,22 @@ import javax.servlet.http.HttpSession;
  */
 public class CatalinaRequestAuthenticator extends RequestAuthenticator {
     private static final Logger log = Logger.getLogger(""+CatalinaRequestAuthenticator.class);
-    protected AbstractKeycloakAuthenticatorValve valve;
     protected Request request;
     protected GenericPrincipalFactory principalFactory;
 
     public CatalinaRequestAuthenticator(KeycloakDeployment deployment,
-                                        AbstractKeycloakAuthenticatorValve valve, AdapterTokenStore tokenStore,
+                                        AdapterTokenStore tokenStore,
                                         CatalinaHttpFacade facade,
                                         Request request,
                                         GenericPrincipalFactory principalFactory) {
         super(facade, deployment, tokenStore, request.getConnector().getRedirectPort());
-        this.valve = valve;
         this.request = request;
         this.principalFactory = principalFactory;
     }
 
     @Override
     protected OAuthRequestAuthenticator createOAuthAuthenticator() {
-        return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort) {
-            @Override
-            protected void saveRequest() {
-                try {
-                    // Support saving request just for TokenStore.SESSION TODO: Add to tokenStore spi?
-                    if (deployment.getTokenStore() == TokenStore.SESSION) {
-                        valve.keycloakSaveRequest(request);
-                    }
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        };
+        return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
     }
 
     @Override
@@ -99,17 +85,6 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
         request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
     }
 
-    protected void restoreRequest() {
-        if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null) {
-            if (valve.keycloakRestoreRequest(request)) {
-                log.finer("restoreRequest");
-            } else {
-                log.finer("Restore of original request failed");
-                throw new RuntimeException("Restore of original request failed");
-            }
-        }
-    }
-
     @Override
     protected String getHttpSessionId(boolean create) {
         HttpSession session = request.getSession(create);
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java
index 761a1d5..0e9cce1 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaSessionTokenStore.java
@@ -1,5 +1,6 @@
 package org.keycloak.adapters.tomcat;
 
+import java.io.IOException;
 import java.util.Set;
 import java.util.logging.Logger;
 
@@ -24,12 +25,18 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
     private KeycloakDeployment deployment;
     private CatalinaUserSessionManagement sessionManagement;
     protected GenericPrincipalFactory principalFactory;
+    protected AbstractKeycloakAuthenticatorValve valve;
 
-    public CatalinaSessionTokenStore(Request request, KeycloakDeployment deployment, CatalinaUserSessionManagement sessionManagement, GenericPrincipalFactory principalFactory) {
+
+    public CatalinaSessionTokenStore(Request request, KeycloakDeployment deployment,
+                                     CatalinaUserSessionManagement sessionManagement,
+                                     GenericPrincipalFactory principalFactory,
+                                     AbstractKeycloakAuthenticatorValve valve) {
         this.request = request;
         this.deployment = deployment;
         this.sessionManagement = sessionManagement;
         this.principalFactory = principalFactory;
+        this.valve = valve;
     }
 
     @Override
@@ -81,7 +88,7 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
         request.setUserPrincipal(principal);
         request.setAuthType("KEYCLOAK");
 
-        ((CatalinaRequestAuthenticator)authenticator).restoreRequest();
+        restoreRequest();
         return true;
     }
 
@@ -112,4 +119,18 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
     public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
         // no-op
     }
+
+    @Override
+    public void saveRequest() {
+        try {
+            valve.keycloakSaveRequest(request);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean restoreRequest() {
+        return valve.keycloakRestoreRequest(request);
+    }
 }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
index 5ef0734..f154ba3 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
@@ -53,12 +53,7 @@ public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthen
 
     @Override
     protected OAuthRequestAuthenticator createOAuthAuthenticator() {
-        return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort) {
-            @Override
-            protected void saveRequest() {
-                // todo
-            }
-        };
+        return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
     }
 
     @Override
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/SavedRequest.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/SavedRequest.java
new file mode 100755
index 0000000..3aae9c6
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/SavedRequest.java
@@ -0,0 +1,131 @@
+package org.keycloak.adapters.undertow;
+import io.undertow.UndertowLogger;
+import io.undertow.UndertowOptions;
+import io.undertow.server.Connectors;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.session.Session;
+import io.undertow.servlet.handlers.ServletRequestContext;
+import io.undertow.servlet.spec.HttpSessionImpl;
+import io.undertow.util.HeaderMap;
+import io.undertow.util.HeaderValues;
+import io.undertow.util.Headers;
+import io.undertow.util.HttpString;
+import io.undertow.util.ImmediatePooled;
+
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.util.Iterator;
+
+/**
+ * Saved servlet request.
+ *
+ * Note bill burke: I had to fork this because Undertow was automatically restoring the request before the code could be processed and redirected.
+ *
+ * @author Stuart Douglas
+ */
+public class SavedRequest implements Serializable {
+
+    private static final String SESSION_KEY = SavedRequest.class.getName();
+
+    private final byte[] data;
+    private final int dataLength;
+    private final HttpString method;
+    private final String requestUri;
+    private final HeaderMap headerMap;
+
+    public SavedRequest(byte[] data, int dataLength, HttpString method, String requestUri, HeaderMap headerMap) {
+        this.data = data;
+        this.dataLength = dataLength;
+        this.method = method;
+        this.requestUri = requestUri;
+        this.headerMap = headerMap;
+    }
+
+    public static void trySaveRequest(final HttpServerExchange exchange) {
+        int maxSize = exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_BUFFERED_REQUEST_SIZE, 16384);
+        if (maxSize > 0) {
+            //if this request has a body try and cache the response
+            if (!exchange.isRequestComplete()) {
+                final long requestContentLength = exchange.getRequestContentLength();
+                if (requestContentLength > maxSize) {
+                    UndertowLogger.REQUEST_LOGGER.debugf("Request to %s was to large to save", exchange.getRequestURI());
+                    return;//failed to save the request, we just return
+                }
+                //TODO: we should really be used pooled buffers
+                //TODO: we should probably limit the number of saved requests at any given time
+                byte[] buffer = new byte[maxSize];
+                int read = 0;
+                int res = 0;
+                InputStream in = exchange.getInputStream();
+                try {
+                    while ((res = in.read(buffer, read, buffer.length - read)) > 0) {
+                        read += res;
+                        if (read == maxSize) {
+                            UndertowLogger.REQUEST_LOGGER.debugf("Request to %s was to large to save", exchange.getRequestURI());
+                            return;//failed to save the request, we just return
+                        }
+                    }
+                    HeaderMap headers = new HeaderMap();
+                    for(HeaderValues entry : exchange.getRequestHeaders()) {
+                        if(entry.getHeaderName().equals(Headers.CONTENT_LENGTH) ||
+                                entry.getHeaderName().equals(Headers.TRANSFER_ENCODING) ||
+                                entry.getHeaderName().equals(Headers.CONNECTION)) {
+                            continue;
+                        }
+                        headers.putAll(entry.getHeaderName(), entry);
+                    }
+                    SavedRequest request = new SavedRequest(buffer, read, exchange.getRequestMethod(), exchange.getRequestURI(), exchange.getRequestHeaders());
+                    final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+                    HttpSessionImpl session = sc.getCurrentServletContext().getSession(exchange, true);
+                    Session underlyingSession;
+                    if(System.getSecurityManager() == null) {
+                        underlyingSession = session.getSession();
+                    } else {
+                        underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(session));
+                    }
+                    underlyingSession.setAttribute(SESSION_KEY, request);
+                } catch (IOException e) {
+                    UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
+                }
+            }
+        }
+    }
+
+    public static void tryRestoreRequest(final HttpServerExchange exchange, HttpSession session) {
+        if(session instanceof HttpSessionImpl) {
+
+            Session underlyingSession;
+            if(System.getSecurityManager() == null) {
+                underlyingSession = ((HttpSessionImpl) session).getSession();
+            } else {
+                underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(session));
+            }
+            SavedRequest request = (SavedRequest) underlyingSession.getAttribute(SESSION_KEY);
+            if(request != null) {
+                if(request.requestUri.equals(exchange.getRequestURI()) && exchange.isRequestComplete()) {
+                    UndertowLogger.REQUEST_LOGGER.debugf("restoring request body for request to %s", request.requestUri);
+                    exchange.setRequestMethod(request.method);
+                    Connectors.ungetRequestBytes(exchange, new ImmediatePooled<ByteBuffer>(ByteBuffer.wrap(request.data, 0, request.dataLength)));
+                    underlyingSession.removeAttribute(SESSION_KEY);
+                    //clear the existing header map of everything except the connection header
+                    //TODO: are there other headers we should preserve?
+                    Iterator<HeaderValues> headerIterator = exchange.getRequestHeaders().iterator();
+                    while (headerIterator.hasNext()) {
+                        HeaderValues header = headerIterator.next();
+                        if(!header.getHeaderName().equals(Headers.CONNECTION)) {
+                            headerIterator.remove();
+                        }
+                    }
+                    for(HeaderValues header : request.headerMap) {
+                        exchange.getRequestHeaders().putAll(header.getHeaderName(), header);
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java
index d28ff29..806fb9f 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletRequestAuthenticator.java
@@ -19,12 +19,15 @@ package org.keycloak.adapters.undertow;
 import io.undertow.security.api.SecurityContext;
 import io.undertow.server.HttpServerExchange;
 import io.undertow.servlet.handlers.ServletRequestContext;
+import io.undertow.servlet.util.SavedRequest;
 import org.keycloak.KeycloakPrincipal;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.AdapterTokenStore;
 import org.keycloak.adapters.HttpFacade;
 import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OAuthRequestAuthenticator;
 import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.enums.TokenStore;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
@@ -44,6 +47,11 @@ public class ServletRequestAuthenticator extends AbstractUndertowRequestAuthenti
     }
 
     @Override
+    protected OAuthRequestAuthenticator createOAuthAuthenticator() {
+        return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
+    }
+
+    @Override
     protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
         super.propagateKeycloakContext(account);
         final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletSessionTokenStore.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletSessionTokenStore.java
index 2168d9d..74ebf1c 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletSessionTokenStore.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletSessionTokenStore.java
@@ -64,6 +64,7 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
             log.debug("Cached account found");
             securityContext.authenticationComplete(account, "KEYCLOAK", false);
             ((AbstractUndertowRequestAuthenticator)authenticator).propagateKeycloakContext(account);
+            restoreRequest();
             return true;
         } else {
             log.debug("Refresh failed. Account was not active. Returning null and invalidating Http session");
@@ -105,6 +106,20 @@ public class ServletSessionTokenStore implements AdapterTokenStore {
         // no-op
     }
 
+    @Override
+    public void saveRequest() {
+        SavedRequest.trySaveRequest(exchange);
+
+    }
+
+    @Override
+    public boolean restoreRequest() {
+        HttpSession session = getSession(false);
+        if (session == null) return false;
+        SavedRequest.tryRestoreRequest(exchange, session);
+        return false;
+    }
+
     protected HttpSession getSession(boolean create) {
         final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
         HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java
index ccd695f..7dddb74 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java
@@ -80,4 +80,14 @@ public class UndertowCookieTokenStore implements AdapterTokenStore {
     public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
         CookieTokenStore.setTokenCookie(deployment, facade, securityContext);
     }
+
+    @Override
+    public void saveRequest() {
+
+    }
+
+    @Override
+    public boolean restoreRequest() {
+        return false;
+    }
 }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowSessionTokenStore.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowSessionTokenStore.java
index e5f013c..0dae4c9 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowSessionTokenStore.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowSessionTokenStore.java
@@ -71,6 +71,16 @@ public class UndertowSessionTokenStore implements AdapterTokenStore {
     }
 
     @Override
+    public void saveRequest() {
+
+    }
+
+    @Override
+    public boolean restoreRequest() {
+        return false;
+    }
+
+    @Override
     public void saveAccountInfo(KeycloakAccount account) {
         Session session = Sessions.getOrCreateSession(exchange);
         session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 6932a81..cef068a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -74,7 +74,6 @@ import java.util.concurrent.atomic.AtomicInteger;
  */
 public class AdapterTest {
 
-    public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString();
     public static PublicKey realmPublicKey;
     @ClassRule
     public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
@@ -99,6 +98,8 @@ public class AdapterTest {
             System.setProperty("my.host.name", "localhost");
             url = getClass().getResource("/adapter-test/session-keycloak.json");
             deployApplication("session-portal", "/session-portal", SessionServlet.class, url.getPath(), "user");
+            url = getClass().getResource("/adapter-test/input-keycloak.json");
+            deployApplication("input-portal", "/input-portal", InputServlet.class, url.getPath(), "user", true, null, "/secured/*");
         }
     };
 
@@ -111,6 +112,11 @@ public class AdapterTest {
     }
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+    @Test
     public void testServletRequestLogout() throws Exception {
         testStrategy.testServletRequestLogout();
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index e32ff1f..d50fd71 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -87,6 +87,9 @@ public class AdapterTestStrategy extends ExternalResource {
     @WebResource
     protected LoginPage loginPage;
 
+    @WebResource
+    protected InputPage inputPage;
+
     protected String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri(AUTH_SERVER_URL)).build("demo").toString();
 
     public AdapterTestStrategy(String AUTH_SERVER_URL, String APP_SERVER_BASE_URL, AbstractKeycloakRule keycloakRule) {
@@ -133,6 +136,35 @@ public class AdapterTestStrategy extends ExternalResource {
     }
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        // test login to customer-portal which does a bearer request to customer-db
+        driver.navigate().to(APP_SERVER_BASE_URL + "/input-portal");
+        System.out.println("Current url: " + driver.getCurrentUrl());
+        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/input-portal" + slash);
+        inputPage.execute("hello");
+
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+        loginPage.login("bburke@redhat.com", "password");
+        System.out.println("Current url: " + driver.getCurrentUrl());
+        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/input-portal/secured/post");
+        String pageSource = driver.getPageSource();
+        System.out.println(pageSource);
+        Assert.assertTrue(pageSource.contains("parameter=hello"));
+
+        // test logout
+
+        String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri(AUTH_SERVER_URL))
+                .queryParam(OAuth2Constants.REDIRECT_URI, APP_SERVER_BASE_URL + "/customer-portal").build("demo").toString();
+        driver.navigate().to(logoutUri);
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+        driver.navigate().to(APP_SERVER_BASE_URL + "/product-portal");
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+        driver.navigate().to(APP_SERVER_BASE_URL + "/customer-portal");
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+    }
+
+
+    @Test
     public void testLoginSSOAndLogout() throws Exception {
         // test login to customer-portal which does a bearer request to customer-db
         driver.navigate().to(APP_SERVER_BASE_URL + "/customer-portal");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/InputPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/InputPage.java
new file mode 100755
index 0000000..bd09991
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/InputPage.java
@@ -0,0 +1,35 @@
+package org.keycloak.testsuite.adapter;
+
+import org.keycloak.testsuite.pages.AbstractPage;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class InputPage extends AbstractPage {
+    @FindBy(id = "parameter")
+    private WebElement parameter;
+
+    @FindBy(name = "submit")
+    private WebElement submit;
+
+    public void execute(String param) {
+        parameter.clear();
+        parameter.sendKeys(param);
+
+        submit.click();
+    }
+
+
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Input Page");
+    }
+
+    @Override
+    public void open() {
+    }
+
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/InputServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/InputServlet.java
new file mode 100755
index 0000000..db4c178
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/InputServlet.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.adapter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class InputServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        String appBase = System.getProperty("app.server.base.url", "http://localhost:8081");
+        String actionUrl = appBase + "/input-portal/secured/post";
+
+
+        resp.setContentType("text/html");
+        PrintWriter pw = resp.getWriter();
+        pw.printf("<html><head><title>%s</title></head><body>", "Input Page");
+        pw.printf("<form action=\"%s\" method=\"POST\">", actionUrl);
+        pw.println("<input id=\"parameter\" type=\"text\" name=\"parameter\">");
+        pw.println("<input name=\"submit\" type=\"submit\" value=\"Submit\"></form>");
+        pw.print("</body></html>");
+        pw.flush();
+
+
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        resp.setContentType("text/plain");
+        PrintWriter pw = resp.getWriter();
+        pw.printf("parameter="+req.getParameter("parameter"));
+        pw.flush();
+    }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/SessionServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/SessionServlet.java
old mode 100644
new mode 100755
index c7c4d85..f9c5d0b
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/SessionServlet.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/SessionServlet.java
@@ -1,38 +1,38 @@
-package org.keycloak.testsuite.adapter;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-/**
- * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
- */
-public class SessionServlet extends HttpServlet {
-
-    @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        String counter = increaseAndGetCounter(req);
-
-        resp.setContentType("text/html");
-        PrintWriter pw = resp.getWriter();
-        pw.printf("<html><head><title>%s</title></head><body>", "Session Test");
-        pw.printf("Counter=%s", counter);
-        pw.print("</body></html>");
-        pw.flush();
-
-
-    }
-
-    private String increaseAndGetCounter(HttpServletRequest req) {
-        HttpSession session = req.getSession();
-        Integer counter = (Integer)session.getAttribute("counter");
-        counter = (counter == null) ? 1 : counter + 1;
-        session.setAttribute("counter", counter);
-        return String.valueOf(counter);
-    }
-}
+package org.keycloak.testsuite.adapter;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SessionServlet extends HttpServlet {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        String counter = increaseAndGetCounter(req);
+
+        resp.setContentType("text/html");
+        PrintWriter pw = resp.getWriter();
+        pw.printf("<html><head><title>%s</title></head><body>", "Session Test");
+        pw.printf("Counter=%s", counter);
+        pw.print("</body></html>");
+        pw.flush();
+
+
+    }
+
+    private String increaseAndGetCounter(HttpServletRequest req) {
+        HttpSession session = req.getSession();
+        Integer counter = (Integer)session.getAttribute("counter");
+        counter = (counter == null) ? 1 : counter + 1;
+        session.setAttribute("counter", counter);
+        return String.valueOf(counter);
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractPage.java
old mode 100644
new mode 100755
index c17b86a..4de2ca2
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractPage.java
@@ -39,8 +39,8 @@ public abstract class AbstractPage {
                 isCurrent());
     }
 
-    abstract boolean isCurrent();
+    abstract public boolean isCurrent();
 
-    abstract void open() throws Exception;
+    abstract public void open() throws Exception;
 
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
old mode 100644
new mode 100755
index 7fdbe06..baa1ae9
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
@@ -49,7 +49,7 @@ public class OAuthGrantPage extends AbstractPage {
     }
 
     @Override
-    void open() {
+    public void open() {
     }
 
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index b89f2c3..d618567 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -150,6 +150,10 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     public void deployApplication(String name, String contextPath, Class<? extends Servlet> servletClass, String adapterConfigPath, String role, boolean isConstrained, Class<? extends KeycloakConfigResolver> keycloakConfigResolver) {
         String constraintUrl = "/*";
+        deployApplication(name, contextPath, servletClass, adapterConfigPath, role, isConstrained, keycloakConfigResolver, constraintUrl);
+    }
+
+    public void deployApplication(String name, String contextPath, Class<? extends Servlet> servletClass, String adapterConfigPath, String role, boolean isConstrained, Class<? extends KeycloakConfigResolver> keycloakConfigResolver, String constraintUrl) {
         DeploymentInfo di = createDeploymentInfo(name, contextPath, servletClass);
         if (null == keycloakConfigResolver) {
             di.addInitParameter("keycloak.config.file", adapterConfigPath);
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm.json b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
index 9a5da24..ec53bd7 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
@@ -125,6 +125,16 @@
                 "http://localhost:8081/session-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "input-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8081/input-portal",
+            "baseUrl": "http://localhost:8081/input-portal",
+            "redirectUris": [
+                "http://localhost:8081/input-portal/*"
+            ],
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/testsuite/integration/src/test/resources/adapter-test/input-keycloak.json b/testsuite/integration/src/test/resources/adapter-test/input-keycloak.json
new file mode 100755
index 0000000..0b4b165
--- /dev/null
+++ b/testsuite/integration/src/test/resources/adapter-test/input-keycloak.json
@@ -0,0 +1,10 @@
+{
+  "realm" : "demo",
+  "resource" : "input-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://${my.host.name}:8081/auth",
+  "ssl-required" : "external",
+  "credentials" : {
+      "secret": "password"
+   }
+}
\ No newline at end of file
diff --git a/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/Jetty9Test.java b/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/Jetty9Test.java
index 40da8ed..acf51ce 100755
--- a/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/Jetty9Test.java
+++ b/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/Jetty9Test.java
@@ -95,6 +95,7 @@ public class Jetty9Test {
         list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
         list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
         list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
+        list.add(new WebAppContext(new File(base, "input-portal").toString(), "/input-portal"));
         list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
 
 
@@ -118,6 +119,11 @@ public class Jetty9Test {
     public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+    @Test
     public void testLoginSSOAndLogout() throws Exception {
         testStrategy.testLoginSSOAndLogout();
     }
diff --git a/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json b/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json
index 9359cc9..ce40aec 100755
--- a/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json
@@ -125,6 +125,16 @@
                 "http://localhost:8082/session-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "input-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8082/input-portal",
+            "baseUrl": "http://localhost:8082/input-portal",
+            "redirectUris": [
+                "http://localhost:8082/input-portal/*"
+            ],
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml b/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
new file mode 100755
index 0000000..1ec566d
--- /dev/null
+++ b/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+    <Get name="securityHandler">
+        <Set name="authenticator">
+            <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
+                <!--
+                <Set name="adapterConfig">
+                    <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+                        <Set name="realm">tomcat</Set>
+                        <Set name="resource">customer-portal</Set>
+                        <Set name="authServerUrl">http://localhost:8081/auth</Set>
+                        <Set name="sslRequired">external</Set>
+                        <Set name="credentials">
+                            <Map>
+                                <Entry>
+                                    <Item>secret</Item>
+                                    <Item>password</Item>
+                                </Entry>
+                            </Map>
+                        </Set>
+                        <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+                    </New>
+                </Set>
+                -->
+            </New>
+        </Set>
+    </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
new file mode 100755
index 0000000..0b4b165
--- /dev/null
+++ b/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+  "realm" : "demo",
+  "resource" : "input-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://${my.host.name}:8081/auth",
+  "ssl-required" : "external",
+  "credentials" : {
+      "secret": "password"
+   }
+}
\ No newline at end of file
diff --git a/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml b/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
new file mode 100755
index 0000000..e4adb37
--- /dev/null
+++ b/testsuite/jetty/jetty81/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?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>adapter-test</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/Jetty9Test.java b/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/Jetty9Test.java
index 528a2e0..19a8efd 100755
--- a/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/Jetty9Test.java
+++ b/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/Jetty9Test.java
@@ -95,6 +95,7 @@ public class Jetty9Test {
         list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
         list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
         list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
+        list.add(new WebAppContext(new File(base, "input-portal").toString(), "/input-portal"));
         list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
 
 
@@ -118,6 +119,11 @@ public class Jetty9Test {
     public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule, true);
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+    @Test
     public void testLoginSSOAndLogout() throws Exception {
         testStrategy.testLoginSSOAndLogout();
     }
diff --git a/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json b/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json
index 9359cc9..ce40aec 100755
--- a/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json
@@ -125,6 +125,16 @@
                 "http://localhost:8082/session-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "input-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8082/input-portal",
+            "baseUrl": "http://localhost:8082/input-portal",
+            "redirectUris": [
+                "http://localhost:8082/input-portal/*"
+            ],
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml b/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
new file mode 100755
index 0000000..1ec566d
--- /dev/null
+++ b/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+    <Get name="securityHandler">
+        <Set name="authenticator">
+            <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
+                <!--
+                <Set name="adapterConfig">
+                    <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+                        <Set name="realm">tomcat</Set>
+                        <Set name="resource">customer-portal</Set>
+                        <Set name="authServerUrl">http://localhost:8081/auth</Set>
+                        <Set name="sslRequired">external</Set>
+                        <Set name="credentials">
+                            <Map>
+                                <Entry>
+                                    <Item>secret</Item>
+                                    <Item>password</Item>
+                                </Entry>
+                            </Map>
+                        </Set>
+                        <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+                    </New>
+                </Set>
+                -->
+            </New>
+        </Set>
+    </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
new file mode 100755
index 0000000..0b4b165
--- /dev/null
+++ b/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+  "realm" : "demo",
+  "resource" : "input-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://${my.host.name}:8081/auth",
+  "ssl-required" : "external",
+  "credentials" : {
+      "secret": "password"
+   }
+}
\ No newline at end of file
diff --git a/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml b/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
new file mode 100755
index 0000000..e4adb37
--- /dev/null
+++ b/testsuite/jetty/jetty91/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?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>adapter-test</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/Jetty9Test.java b/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/Jetty9Test.java
index 528a2e0..4d3c439 100755
--- a/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/Jetty9Test.java
+++ b/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/Jetty9Test.java
@@ -95,6 +95,7 @@ public class Jetty9Test {
         list.add(new WebAppContext(new File(base, "customer-db").toString(), "/customer-db"));
         list.add(new WebAppContext(new File(base, "product-portal").toString(), "/product-portal"));
         list.add(new WebAppContext(new File(base, "session-portal").toString(), "/session-portal"));
+        list.add(new WebAppContext(new File(base, "input-portal").toString(), "/input-portal"));
         list.add(new WebAppContext(new File(base, "secure-portal").toString(), "/secure-portal"));
 
 
@@ -123,6 +124,11 @@ public class Jetty9Test {
     }
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+    @Test
     public void testServletRequestLogout() throws Exception {
         testStrategy.testServletRequestLogout();
     }
diff --git a/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json b/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json
index 9359cc9..ce40aec 100755
--- a/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json
@@ -125,6 +125,16 @@
                 "http://localhost:8082/session-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "input-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8082/input-portal",
+            "baseUrl": "http://localhost:8082/input-portal",
+            "redirectUris": [
+                "http://localhost:8082/input-portal/*"
+            ],
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml b/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
new file mode 100755
index 0000000..1ec566d
--- /dev/null
+++ b/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+    <Get name="securityHandler">
+        <Set name="authenticator">
+            <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
+                <!--
+                <Set name="adapterConfig">
+                    <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+                        <Set name="realm">tomcat</Set>
+                        <Set name="resource">customer-portal</Set>
+                        <Set name="authServerUrl">http://localhost:8081/auth</Set>
+                        <Set name="sslRequired">external</Set>
+                        <Set name="credentials">
+                            <Map>
+                                <Entry>
+                                    <Item>secret</Item>
+                                    <Item>password</Item>
+                                </Entry>
+                            </Map>
+                        </Set>
+                        <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+                    </New>
+                </Set>
+                -->
+            </New>
+        </Set>
+    </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
new file mode 100755
index 0000000..0b4b165
--- /dev/null
+++ b/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+  "realm" : "demo",
+  "resource" : "input-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://${my.host.name}:8081/auth",
+  "ssl-required" : "external",
+  "credentials" : {
+      "secret": "password"
+   }
+}
\ No newline at end of file
diff --git a/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml b/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
new file mode 100755
index 0000000..e4adb37
--- /dev/null
+++ b/testsuite/jetty/jetty92/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?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>adapter-test</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatTest.java b/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatTest.java
index 4f2a29e..d24a80d 100755
--- a/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatTest.java
+++ b/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatTest.java
@@ -83,6 +83,7 @@ public class TomcatTest {
         tomcat.deploy("/product-portal", "product-portal");
         tomcat.deploy("/secure-portal", "secure-portal");
         tomcat.deploy("/session-portal", "session-portal");
+        tomcat.deploy("/input-portal", "input-portal");
 
 
         tomcat.start();
@@ -103,6 +104,11 @@ public class TomcatTest {
     }
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+    @Test
     public void testServletRequestLogout() throws Exception {
         // can't test this.  Servlet 2.5 doesn't have logout()
         //testStrategy.testServletRequestLogout();
diff --git a/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json b/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
index 9359cc9..ce40aec 100755
--- a/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
@@ -125,6 +125,16 @@
                 "http://localhost:8082/session-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "input-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8082/input-portal",
+            "baseUrl": "http://localhost:8082/input-portal",
+            "redirectUris": [
+                "http://localhost:8082/input-portal/*"
+            ],
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/META-INF/context.xml b/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/META-INF/context.xml
new file mode 100755
index 0000000..6f24639
--- /dev/null
+++ b/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+    <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
new file mode 100755
index 0000000..0b4b165
--- /dev/null
+++ b/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+  "realm" : "demo",
+  "resource" : "input-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://${my.host.name}:8081/auth",
+  "ssl-required" : "external",
+  "credentials" : {
+      "secret": "password"
+   }
+}
\ No newline at end of file
diff --git a/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml b/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
new file mode 100755
index 0000000..e4adb37
--- /dev/null
+++ b/testsuite/tomcat6/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?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>adapter-test</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
index 70da95d..b79b085 100755
--- a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
+++ b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
@@ -87,6 +87,7 @@ public class Tomcat7Test {
         tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
         tomcat.addWebapp("/secure-portal", new File(base, "secure-portal").toString());
         tomcat.addWebapp("/session-portal", new File(base, "session-portal").toString());
+        tomcat.addWebapp("/input-portal", new File(base, "input-portal").toString());
 
         tomcat.start();
         //tomcat.getServer().await();
@@ -107,6 +108,12 @@ public class Tomcat7Test {
     }
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+
+    @Test
     public void testServletRequestLogout() throws Exception {
         testStrategy.testServletRequestLogout();
     }
diff --git a/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json b/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json
index 9359cc9..ce40aec 100755
--- a/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json
@@ -125,6 +125,16 @@
                 "http://localhost:8082/session-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "input-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8082/input-portal",
+            "baseUrl": "http://localhost:8082/input-portal",
+            "redirectUris": [
+                "http://localhost:8082/input-portal/*"
+            ],
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/META-INF/context.xml b/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/META-INF/context.xml
new file mode 100755
index 0000000..6f24639
--- /dev/null
+++ b/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+    <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
new file mode 100755
index 0000000..0b4b165
--- /dev/null
+++ b/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+  "realm" : "demo",
+  "resource" : "input-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://${my.host.name}:8081/auth",
+  "ssl-required" : "external",
+  "credentials" : {
+      "secret": "password"
+   }
+}
\ No newline at end of file
diff --git a/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml b/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
new file mode 100755
index 0000000..e4adb37
--- /dev/null
+++ b/testsuite/tomcat7/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?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>adapter-test</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatTest.java b/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatTest.java
index 9ff92d8..e4834b6 100755
--- a/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatTest.java
+++ b/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatTest.java
@@ -87,6 +87,7 @@ public class TomcatTest {
         tomcat.addWebapp("/product-portal", new File(base, "product-portal").toString());
         tomcat.addWebapp("/secure-portal", new File(base, "secure-portal").toString());
         tomcat.addWebapp("/session-portal", new File(base, "session-portal").toString());
+        tomcat.addWebapp("/input-portal", new File(base, "input-portal").toString());
 
         tomcat.start();
         //tomcat.getServer().await();
@@ -107,6 +108,13 @@ public class TomcatTest {
     }
 
     @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+
+
+    @Test
     public void testServletRequestLogout() throws Exception {
         testStrategy.testServletRequestLogout();
     }
diff --git a/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json b/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json
index 9359cc9..ce40aec 100755
--- a/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json
@@ -125,6 +125,16 @@
                 "http://localhost:8082/session-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "input-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8082/input-portal",
+            "baseUrl": "http://localhost:8082/input-portal",
+            "redirectUris": [
+                "http://localhost:8082/input-portal/*"
+            ],
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/META-INF/context.xml b/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/META-INF/context.xml
new file mode 100755
index 0000000..6f24639
--- /dev/null
+++ b/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+    <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
new file mode 100755
index 0000000..0b4b165
--- /dev/null
+++ b/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+  "realm" : "demo",
+  "resource" : "input-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://${my.host.name}:8081/auth",
+  "ssl-required" : "external",
+  "credentials" : {
+      "secret": "password"
+   }
+}
\ No newline at end of file
diff --git a/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml b/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
new file mode 100755
index 0000000..e4adb37
--- /dev/null
+++ b/testsuite/tomcat8/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?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>adapter-test</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>