keycloak-aplcache

Merge pull request #1702 from patriot1burke/master SAML

10/9/2015 8:02:18 PM

Changes

pom.xml 7(+6 -1)

Details

diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
index 54395eb..c377b1f 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
@@ -115,6 +115,11 @@ public class BearerTokenRequestAuthenticator {
             }
 
             @Override
+            public int getResponseCode() {
+                return 0;
+            }
+
+            @Override
             public boolean challenge(HttpFacade exchange) {
                 // do the same thing as client cert auth
                 return false;
@@ -140,6 +145,11 @@ public class BearerTokenRequestAuthenticator {
             }
 
             @Override
+            public int getResponseCode() {
+                return 401;
+            }
+
+            @Override
             public boolean challenge(HttpFacade facade) {
                 facade.getResponse().setStatus(401);
                 facade.getResponse().addHeader("WWW-Authenticate", challenge);
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 d077d7d..c6ffce5 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
@@ -181,6 +181,11 @@ public class OAuthRequestAuthenticator {
                 public boolean errorPage() {
                     return true;
                 }
+
+                @Override
+                public int getResponseCode() {
+                    return 403;
+                }
             };
         }
         return new AuthChallenge() {
@@ -191,6 +196,11 @@ public class OAuthRequestAuthenticator {
             }
 
             @Override
+            public int getResponseCode() {
+                return 0;
+            }
+
+            @Override
             public boolean challenge(HttpFacade exchange) {
                 tokenStore.saveRequest();
                 log.debug("Sending redirect to login page: " + redirect);
@@ -263,6 +273,11 @@ public class OAuthRequestAuthenticator {
             }
 
             @Override
+            public int getResponseCode() {
+                return code;
+            }
+
+            @Override
             public boolean challenge(HttpFacade exchange) {
                 exchange.getResponse().setStatus(code);
                 return true;
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java
index 94385f0..47c07c2 100755
--- a/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java
@@ -13,9 +13,17 @@ public interface AuthChallenge {
     boolean challenge(HttpFacade exchange);
 
     /**
-     * Whether or not an error page should be displayed if possible
+     * Whether or not an error page should be displayed if possible along with the challenge
      *
      * @return
      */
     boolean errorPage();
+
+    /**
+     * If errorPage is true, this is the response code the challenge will send.  This is used by platforms
+     * that call HttpServletResponse.sendError() to forward to error page.
+     *
+     * @return
+     */
+    int getResponseCode();
 }
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
index 1be7159..c4a8081 100755
--- a/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
@@ -23,6 +23,14 @@ public class InMemorySessionIdMapper implements SessionIdMapper {
     }
 
     @Override
+    public void clear() {
+        ssoToSession.clear();
+        sessionToSso.clear();
+        principalToSession.clear();
+        sessionToPrincipal.clear();
+    }
+
+    @Override
     public Set<String> getUserSessions(String principal) {
         Set<String> lookup = principalToSession.get(principal);
         if (lookup == null) return null;
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
index d52e029..0f87155 100755
--- a/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
@@ -9,6 +9,8 @@ import java.util.Set;
 public interface SessionIdMapper {
     boolean hasSession(String id);
 
+    void clear();
+
     Set<String> getUserSessions(String principal);
 
     String getSessionFromSSO(String sso);
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
index c11f30a..6b0233c 100755
--- a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
@@ -262,16 +262,16 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
         }
         AuthChallenge challenge = authenticator.getChallenge();
         if (challenge != null) {
+            challenge.challenge(facade);
             if (challenge.errorPage() && errorPage != null) {
                 Response response = (Response)res;
                 try {
-                    response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), errorPage)));
+                   response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), errorPage)));
                 } catch (IOException e) {
                     throw new RuntimeException(e);
                 }
 
             }
-            challenge.challenge(facade);
         }
         return Authentication.SEND_CONTINUE;
     }
diff --git a/integration/pom.xml b/integration/pom.xml
index c1d3346..6257c28 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -26,6 +26,7 @@
         <module>undertow-adapter-spi</module>
         <module>undertow</module>
         <module>wildfly</module>
+        <module>servlet-filter</module>
         <module>js</module>
         <module>installed</module>
         <module>admin-client</module>
diff --git a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java
index 51c6909..b6ddbc7 100755
--- a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java
+++ b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java
@@ -2,14 +2,32 @@ package org.keycloak.adapters.servlet;
 
 import org.keycloak.adapters.AdapterSessionStore;
 import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakAccount;
+import org.keycloak.util.Encode;
 import org.keycloak.util.MultivaluedHashMap;
 
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpSession;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -24,6 +42,7 @@ public class FilterSessionStore implements AdapterSessionStore {
     protected final HttpFacade facade;
     protected final int maxBuffer;
     protected byte[] restoredBuffer = null;
+    protected boolean needRequestRestore;
 
     public FilterSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer) {
         this.request = request;
@@ -38,6 +57,249 @@ public class FilterSessionStore implements AdapterSessionStore {
         session.removeAttribute(SAVED_BODY);
     }
 
+    public void servletRequestLogout() {
+
+    }
+
+    public static String getCharsetFromContentType(String contentType) {
+
+        if (contentType == null)
+            return (null);
+        int start = contentType.indexOf("charset=");
+        if (start < 0)
+            return (null);
+        String encoding = contentType.substring(start + 8);
+        int end = encoding.indexOf(';');
+        if (end >= 0)
+            encoding = encoding.substring(0, end);
+        encoding = encoding.trim();
+        if ((encoding.length() > 2) && (encoding.startsWith("\""))
+                && (encoding.endsWith("\"")))
+            encoding = encoding.substring(1, encoding.length() - 1);
+        return (encoding.trim());
+
+    }
+
+
+    public HttpServletRequestWrapper buildWrapper(HttpSession session, final KeycloakAccount account) {
+        if (needRequestRestore) {
+            final String method = (String)session.getAttribute(SAVED_METHOD);
+            final byte[] body = (byte[])session.getAttribute(SAVED_BODY);
+            final MultivaluedHashMap<String, String> headers = (MultivaluedHashMap<String, String>)session.getAttribute(SAVED_HEADERS);
+            clearSavedRequest(session);
+            HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
+                protected MultivaluedHashMap<String, String> parameters;
+
+                MultivaluedHashMap<String, String> getParams() {
+                    if (parameters != null) return parameters;
+                    String contentType = getContentType();
+                    contentType = contentType.toLowerCase();
+                    if (contentType.startsWith("application/x-www-form-urlencoded")) {
+                        ByteArrayInputStream is = new ByteArrayInputStream(body);
+                        try {
+                            parameters = parseForm(is);
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                    return parameters;
+
+                }
+                @Override
+                public boolean isUserInRole(String role) {
+                    return account.getRoles().contains(role);
+                }
+
+                @Override
+                public Principal getUserPrincipal() {
+                    return account.getPrincipal();
+                }
+
+                @Override
+                public String getMethod() {
+                    if (needRequestRestore) {
+                        return method;
+                    } else {
+                        return super.getMethod();
+
+                    }
+                }
+
+                @Override
+                public String getHeader(String name) {
+                    if (needRequestRestore && headers != null) {
+                        return headers.getFirst(name.toLowerCase());
+                    }
+                    return super.getHeader(name);
+                }
+
+                @Override
+                public Enumeration<String> getHeaders(String name) {
+                    if (needRequestRestore && headers != null) {
+                        List<String> values = headers.getList(name.toLowerCase());
+                        if (values == null) return Collections.emptyEnumeration();
+                        else return Collections.enumeration(values);
+                    }
+                    return super.getHeaders(name);
+                }
+
+                @Override
+                public Enumeration<String> getHeaderNames() {
+                    if (needRequestRestore && headers != null) {
+                        return Collections.enumeration(headers.keySet());
+                    }
+                    return super.getHeaderNames();
+                }
+
+                @Override
+                public ServletInputStream getInputStream() throws IOException {
+
+                    if (needRequestRestore && body != null) {
+                        final ByteArrayInputStream is = new ByteArrayInputStream(body);
+                        return new ServletInputStream() {
+                            @Override
+                            public int read() throws IOException {
+                                return is.read();
+                            }
+                        };
+                    }
+                    return super.getInputStream();
+                }
+
+                @Override
+                public void logout() throws ServletException {
+                    servletRequestLogout();
+                }
+
+                @Override
+                public long getDateHeader(String name) {
+                   if (!needRequestRestore) return super.getDateHeader(name);
+                   throw new RuntimeException("This method is not supported in a restored authenticated request");
+                }
+
+                @Override
+                public int getIntHeader(String name) {
+                    if (!needRequestRestore) return super.getIntHeader(name);
+                    String value = getHeader(name);
+                    if (value == null) return -1;
+                    return Integer.valueOf(value);
+
+                }
+
+                @Override
+                public String[] getParameterValues(String name) {
+                    if (!needRequestRestore) return super.getParameterValues(name);
+                    MultivaluedHashMap<String, String> formParams = getParams();
+                    if (formParams == null) {
+                        return super.getParameterValues(name);
+                    }
+                    String[] values = request.getParameterValues(name);
+                    List<String> list = new LinkedList<>();
+                    if (values != null) {
+                        for (String val : values) list.add(val);
+                    }
+                    List<String> vals = formParams.get(name);
+                    if (vals != null) list.addAll(vals);
+                    return list.toArray(new String[list.size()]);
+                }
+
+                @Override
+                public Enumeration<String> getParameterNames() {
+                    if (!needRequestRestore) return super.getParameterNames();
+                    MultivaluedHashMap<String, String> formParams = getParams();
+                    if (formParams == null) {
+                        return super.getParameterNames();
+                    }
+                    Set<String> names = new HashSet<>();
+                    Enumeration<String> qnames = super.getParameterNames();
+                    while (qnames.hasMoreElements()) names.add(qnames.nextElement());
+                    names.addAll(formParams.keySet());
+                    return Collections.enumeration(names);
+
+                }
+
+                @Override
+                public Map<String, String[]> getParameterMap() {
+                    if (!needRequestRestore) return super.getParameterMap();
+                    MultivaluedHashMap<String, String> formParams = getParams();
+                    if (formParams == null) {
+                        return super.getParameterMap();
+                    }
+                    Map<String, String[]> map = new HashMap<>();
+                    Enumeration<String> names = getParameterNames();
+                    while (names.hasMoreElements()) {
+                        String name = names.nextElement();
+                        String[] values = getParameterValues(name);
+                        if (values != null) {
+                            map.put(name, values);
+                        }
+                    }
+                    return map;
+                }
+
+                @Override
+                public String getParameter(String name) {
+                    if (!needRequestRestore) return super.getParameter(name);
+                    String param = super.getParameter(name);
+                    if (param != null) return param;
+                    MultivaluedHashMap<String, String> formParams = getParams();
+                    if (formParams == null) {
+                        return null;
+                    }
+                    return formParams.getFirst(name);
+
+                }
+
+                @Override
+                public BufferedReader getReader() throws IOException {
+                    if (!needRequestRestore) return super.getReader();
+                    return new BufferedReader(new InputStreamReader(getInputStream()));
+                }
+
+                @Override
+                public int getContentLength() {
+                    if (!needRequestRestore) return super.getContentLength();
+                    String header = getHeader("content-length");
+                    if (header == null) return -1;
+                    return Integer.valueOf(header);
+                }
+
+                @Override
+                public String getContentType() {
+                    if (!needRequestRestore) return super.getContentType();
+                    return getHeader("content-type");
+                }
+
+                @Override
+                public String getCharacterEncoding() {
+                    if (!needRequestRestore) return super.getCharacterEncoding();
+                    return getCharsetFromContentType(getContentType());
+                }
+
+            };
+            return wrapper;
+        } else {
+            return new HttpServletRequestWrapper(request) {
+                @Override
+                public boolean isUserInRole(String role) {
+                    return account.getRoles().contains(role);
+                }
+
+                @Override
+                public Principal getUserPrincipal() {
+                    return account.getPrincipal();
+                }
+
+                @Override
+                public void logout() throws ServletException {
+                    servletRequestLogout();
+                }
+
+
+            };
+        }
+    }
+
     public String getRedirectUri() {
         HttpSession session = request.getSession(true);
         return (String)session.getAttribute(REDIRECT_URI);
@@ -50,6 +312,44 @@ public class FilterSessionStore implements AdapterSessionStore {
         return session.getAttribute(REDIRECT_URI) != null;
     }
 
+    public static MultivaluedHashMap<String, String> parseForm(InputStream entityStream)
+            throws IOException
+    {
+        char[] buffer = new char[100];
+        StringBuffer buf = new StringBuffer();
+        BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream));
+
+        int wasRead = 0;
+        do
+        {
+            wasRead = reader.read(buffer, 0, 100);
+            if (wasRead > 0) buf.append(buffer, 0, wasRead);
+        } while (wasRead > -1);
+
+        String form = buf.toString();
+
+        MultivaluedHashMap<String, String> formData = new MultivaluedHashMap<String, String>();
+        if ("".equals(form)) return formData;
+
+        String[] params = form.split("&");
+
+        for (String param : params)
+        {
+            if (param.indexOf('=') >= 0)
+            {
+                String[] nv = param.split("=");
+                String val = nv.length > 1 ? nv[1] : "";
+                formData.add(Encode.decode(nv[0]), Encode.decode(val));
+            }
+            else
+            {
+                formData.add(Encode.decode(param), "");
+            }
+        }
+        return formData;
+    }
+
+
 
     @Override
     public void saveRequest() {
@@ -62,7 +362,7 @@ public class FilterSessionStore implements AdapterSessionStore {
             String name = names.nextElement();
             Enumeration<String> values = request.getHeaders(name);
             while (values.hasMoreElements()) {
-                headers.add(name, values.nextElement());
+                headers.add(name.toLowerCase(), values.nextElement());
             }
         }
         session.setAttribute(SAVED_HEADERS, headers);
@@ -93,6 +393,8 @@ public class FilterSessionStore implements AdapterSessionStore {
         if (body.length > 0) {
             session.setAttribute(SAVED_BODY, body);
         }
+
+
     }
 
 }
diff --git a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java
index bc4eaa3..f12c9ca 100755
--- a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java
+++ b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java
@@ -67,6 +67,13 @@ public class ServletHttpFacade implements HttpFacade {
             return queryParameters.getFirst(param);
         }
 
+        public MultivaluedHashMap<String, String> getQueryParameters() {
+            if (queryParameters == null) {
+                queryParameters = UriUtils.decodeQueryString(request.getQueryString());
+            }
+            return queryParameters;
+        }
+
         @Override
         public Cookie getCookie(String cookieName) {
             if (request.getCookies() == null) return null;
diff --git a/integration/servlet-filter/pom.xml b/integration/servlet-filter/pom.xml
new file mode 100755
index 0000000..8448d59
--- /dev/null
+++ b/integration/servlet-filter/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.6.0.Final-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-servlet-filter-adapter</artifactId>
+    <name>Keycloak Servlet Filter Adapter Integration</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <version>${jboss.logging.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-adapter-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-servlet-adapter-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-adapter-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-mapper-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-xc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/FilterRequestAuthenticator.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/FilterRequestAuthenticator.java
new file mode 100755
index 0000000..70cb7e9
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/FilterRequestAuthenticator.java
@@ -0,0 +1,86 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.AdapterUtils;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OAuthRequestAuthenticator;
+import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.OidcKeycloakAccount;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.security.Principal;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a>
+ * @version $Revision: 1 $
+ */
+public class FilterRequestAuthenticator extends RequestAuthenticator {
+    private static final Logger log = Logger.getLogger(""+FilterRequestAuthenticator.class);
+    protected HttpServletRequest request;
+
+    public FilterRequestAuthenticator(KeycloakDeployment deployment,
+                                      AdapterTokenStore tokenStore,
+                                      OIDCHttpFacade facade,
+                                      HttpServletRequest request,
+                                      int sslRedirectPort) {
+        super(facade, deployment, tokenStore, sslRedirectPort);
+        this.request = request;
+    }
+
+    @Override
+    protected OAuthRequestAuthenticator createOAuthAuthenticator() {
+        return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
+    }
+
+    @Override
+    protected void completeOAuthAuthentication(final KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
+        final RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
+        final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
+        OidcKeycloakAccount account = new OidcKeycloakAccount() {
+
+            @Override
+            public Principal getPrincipal() {
+                return skp;
+            }
+
+            @Override
+            public Set<String> getRoles() {
+                return roles;
+            }
+
+            @Override
+            public KeycloakSecurityContext getKeycloakSecurityContext() {
+                return securityContext;
+            }
+
+        };
+
+        request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+        this.tokenStore.saveAccountInfo(account);
+    }
+
+    @Override
+    protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
+        RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
+        Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
+        if (log.isLoggable(Level.FINE)) {
+            log.fine("Completing bearer authentication. Bearer roles: " + roles);
+        }
+        request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+    }
+
+    @Override
+    protected String getHttpSessionId(boolean create) {
+        HttpSession session = request.getSession(create);
+        return session != null ? session.getId() : null;
+    }
+
+}
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/KeycloakOIDCFilter.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/KeycloakOIDCFilter.java
new file mode 100755
index 0000000..1dab8c3
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/KeycloakOIDCFilter.java
@@ -0,0 +1,162 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.AuthOutcome;
+import org.keycloak.adapters.AuthenticatedActionsHandler;
+import org.keycloak.adapters.InMemorySessionIdMapper;
+import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.NodesRegistrationManagement;
+import org.keycloak.adapters.PreAuthActionsHandler;
+import org.keycloak.adapters.SessionIdMapper;
+import org.keycloak.adapters.UserSessionManagement;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+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.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakOIDCFilter implements Filter {
+    protected AdapterDeploymentContext deploymentContext;
+    protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
+    protected NodesRegistrationManagement nodesRegistrationManagement;
+    private final static Logger log = Logger.getLogger(""+KeycloakOIDCFilter.class);
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+        String configResolverClass = filterConfig.getInitParameter("keycloak.config.resolver");
+        if (configResolverClass != null) {
+            try {
+                KeycloakConfigResolver configResolver = (KeycloakConfigResolver) getClass().getClassLoader().loadClass(configResolverClass).newInstance();
+                deploymentContext = new AdapterDeploymentContext(configResolver);
+                log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
+            } catch (Exception ex) {
+                log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
+                deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+            }
+        } else {
+            String fp = filterConfig.getInitParameter("keycloak.config.file");
+            InputStream is = null;
+            if (fp != null) {
+                try {
+                    is = new FileInputStream(fp);
+                } catch (FileNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+            } else {
+                String path = "/WEB-INF/keycloak.json";
+                String pathParam = filterConfig.getInitParameter("keycloak.config.path");
+                if (pathParam != null) path = pathParam;
+                is = filterConfig.getServletContext().getResourceAsStream(path);
+            }
+            KeycloakDeployment kd;
+            if (is == null) {
+                log.fine("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+                kd = new KeycloakDeployment();
+            } else {
+                kd = KeycloakDeploymentBuilder.build(is);
+            }
+            deploymentContext = new AdapterDeploymentContext(kd);
+            log.fine("Keycloak is using a per-deployment configuration.");
+        }
+        filterConfig.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
+        nodesRegistrationManagement = new NodesRegistrationManagement();
+    }
+
+
+    @Override
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) req;
+        HttpServletResponse response = (HttpServletResponse) res;
+        OIDCServletHttpFacade facade = new OIDCServletHttpFacade(request, response);
+        KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
+        if (deployment == null || !deployment.isConfigured()) {
+            response.sendError(403);
+            log.fine("deployment not configured");
+            return;
+        }
+
+        PreAuthActionsHandler preActions = new PreAuthActionsHandler(new UserSessionManagement() {
+            @Override
+            public void logoutAll() {
+                if (idMapper != null) {
+                    idMapper.clear();
+                }
+            }
+
+            @Override
+            public void logoutHttpSessions(List<String> ids) {
+                for (String id : ids) {
+                    idMapper.removeSession(id);
+                }
+
+            }
+        }, deploymentContext, facade);
+
+        if (preActions.handleRequest()) {
+            return;
+        }
+
+
+        nodesRegistrationManagement.tryRegister(deployment);
+        OIDCFilterSessionStore tokenStore = new OIDCFilterSessionStore(request, facade, 100000, deployment, idMapper);
+        tokenStore.checkCurrentToken();
+
+
+        FilterRequestAuthenticator authenticator = new FilterRequestAuthenticator(deployment, tokenStore, facade, request, 8443);
+        AuthOutcome outcome = authenticator.authenticate();
+        if (outcome == AuthOutcome.AUTHENTICATED) {
+            log.fine("AUTHENTICATED");
+            if (facade.isEnded()) {
+                return;
+            }
+            AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, facade);
+            if (actions.handledRequest()) {
+                return;
+            } else {
+                HttpServletRequestWrapper wrapper = tokenStore.buildWrapper();
+                chain.doFilter(wrapper, res);
+                return;
+            }
+        }
+        AuthChallenge challenge = authenticator.getChallenge();
+        if (challenge != null) {
+            log.fine("challenge");
+            challenge.challenge(facade);
+            if (challenge.errorPage()) {
+                response.sendError(challenge.getResponseCode());
+                return;
+            }
+            log.fine("sending challenge");
+            return;
+        }
+        response.sendError(403);
+
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCFilterSessionStore.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCFilterSessionStore.java
new file mode 100755
index 0000000..bab5cbe
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCFilterSessionStore.java
@@ -0,0 +1,167 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakAccount;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OidcKeycloakAccount;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.keycloak.adapters.SessionIdMapper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCFilterSessionStore extends FilterSessionStore implements AdapterTokenStore {
+    protected final KeycloakDeployment deployment;
+    private static final Logger log = Logger.getLogger("" + OIDCFilterSessionStore.class);
+    protected final SessionIdMapper idMapper;
+
+    public OIDCFilterSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer, KeycloakDeployment deployment, SessionIdMapper idMapper) {
+        super(request, facade, maxBuffer);
+        this.deployment = deployment;
+        this.idMapper = idMapper;
+    }
+
+    public HttpServletRequestWrapper buildWrapper() {
+        HttpSession session = request.getSession();
+        KeycloakAccount account = (KeycloakAccount)session.getAttribute((KeycloakAccount.class.getName()));
+        return buildWrapper(session, account);
+    }
+
+    @Override
+    public void checkCurrentToken() {
+        HttpSession httpSession = request.getSession(false);
+        if (httpSession == null) return;
+        SerializableKeycloakAccount account = (SerializableKeycloakAccount)httpSession.getAttribute(KeycloakAccount.class.getName());
+        if (account == null) {
+            return;
+        }
+
+        RefreshableKeycloakSecurityContext session = account.getKeycloakSecurityContext();
+        if (session == null) return;
+
+        // just in case session got serialized
+        if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
+
+        if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
+
+        // FYI: A refresh requires same scope, so same roles will be set.  Otherwise, refresh will fail and token will
+        // not be updated
+        boolean success = session.refreshExpiredToken(false);
+        if (success && session.isActive()) return;
+
+        // Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
+        //log.fine("Cleanup and expire session " + httpSession.getId() + " after failed refresh");
+        cleanSession(httpSession);
+        httpSession.invalidate();
+    }
+
+    protected void cleanSession(HttpSession session) {
+        session.removeAttribute(KeycloakAccount.class.getName());
+        clearSavedRequest(session);
+    }
+
+    @Override
+    public boolean isCached(RequestAuthenticator authenticator) {
+        HttpSession httpSession = request.getSession(false);
+        if (httpSession == null) return false;
+        SerializableKeycloakAccount account = (SerializableKeycloakAccount) httpSession.getAttribute(KeycloakAccount.class.getName());
+        if (account == null) {
+            return false;
+        }
+
+        log.fine("remote logged in already. Establish state from session");
+
+        RefreshableKeycloakSecurityContext securityContext = account.getKeycloakSecurityContext();
+
+        if (!deployment.getRealm().equals(securityContext.getRealm())) {
+            log.fine("Account from cookie is from a different realm than for the request.");
+            cleanSession(httpSession);
+            return false;
+        }
+
+        if (idMapper != null && !idMapper.hasSession(httpSession.getId())) {
+            cleanSession(httpSession);
+            return false;
+        }
+
+
+        securityContext.setCurrentRequestInfo(deployment, this);
+        request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+        needRequestRestore = restoreRequest();
+        return true;
+    }
+
+    public static class SerializableKeycloakAccount implements OidcKeycloakAccount, Serializable {
+        protected Set<String> roles;
+        protected Principal principal;
+        protected RefreshableKeycloakSecurityContext securityContext;
+
+        public SerializableKeycloakAccount(Set<String> roles, Principal principal, RefreshableKeycloakSecurityContext securityContext) {
+            this.roles = roles;
+            this.principal = principal;
+            this.securityContext = securityContext;
+        }
+
+        @Override
+        public Principal getPrincipal() {
+            return principal;
+        }
+
+        @Override
+        public Set<String> getRoles() {
+            return roles;
+        }
+
+        @Override
+        public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
+            return securityContext;
+        }
+    }
+
+    @Override
+    public void saveAccountInfo(OidcKeycloakAccount account) {
+        RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) account.getKeycloakSecurityContext();
+        Set<String> roles = account.getRoles();
+
+        SerializableKeycloakAccount sAccount = new SerializableKeycloakAccount(roles, account.getPrincipal(), securityContext);
+        HttpSession httpSession = request.getSession();
+        httpSession.setAttribute(KeycloakAccount.class.getName(), sAccount);
+        if (idMapper != null) idMapper.map(account.getKeycloakSecurityContext().getToken().getClientSession(),  account.getPrincipal().getName(), httpSession.getId());
+        //String username = securityContext.getToken().getSubject();
+        //log.fine("userSessionManagement.login: " + username);
+    }
+
+    @Override
+    public void logout() {
+        HttpSession httpSession = request.getSession(false);
+        if (httpSession != null) {
+            SerializableKeycloakAccount account = (SerializableKeycloakAccount) httpSession.getAttribute(KeycloakAccount.class.getName());
+            if (account != null) {
+                account.getKeycloakSecurityContext().logout(deployment);
+            }
+            cleanSession(httpSession);
+        }
+    }
+
+    @Override
+    public void servletRequestLogout() {
+        logout();
+    }
+
+    @Override
+    public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
+        // no-op
+    }
+}
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCServletHttpFacade.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCServletHttpFacade.java
new file mode 100755
index 0000000..1232625
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCServletHttpFacade.java
@@ -0,0 +1,23 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.OIDCHttpFacade;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCServletHttpFacade extends ServletHttpFacade implements OIDCHttpFacade {
+
+    public OIDCServletHttpFacade(HttpServletRequest request, HttpServletResponse response) {
+        super(request, response);
+    }
+
+    @Override
+    public KeycloakSecurityContext getSecurityContext() {
+        return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
+    }
+}
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 3347978..51b89f8 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
@@ -77,6 +77,13 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
         request.setUserPrincipal(null);
     }
 
+    protected void beforeStop() {
+        if (nodesRegistrationManagement != null) {
+            nodesRegistrationManagement.stop();
+        }
+    }
+
+
     @SuppressWarnings("UseSpecificCatch")
     public void keycloakInit() {
         // Possible scenarios:
@@ -119,11 +126,6 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
         nodesRegistrationManagement = new NodesRegistrationManagement();
     }
 
-    protected void beforeStop() {
-        if (nodesRegistrationManagement != null) {
-            nodesRegistrationManagement.stop();
-        }
-    }
 
     private static InputStream getJSONFromServletContext(ServletContext servletContext) {
         String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);

pom.xml 7(+6 -1)

diff --git a/pom.xml b/pom.xml
index 1a22d53..65968cb 100755
--- a/pom.xml
+++ b/pom.xml
@@ -894,7 +894,12 @@
             </dependency>
             <dependency>
                 <groupId>org.keycloak</groupId>
-                <artifactId>keycloak-saml-server-filter-adapter</artifactId>
+                <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.keycloak</groupId>
+                <artifactId>keycloak-servlet-filter-adapter</artifactId>
                 <version>${project.version}</version>
             </dependency>
             <dependency>
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
index bcba3d0..c0f8dc4 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
@@ -32,6 +32,11 @@ public class InitiateLogin implements AuthChallenge {
     }
 
     @Override
+    public int getResponseCode() {
+        return 0;
+    }
+
+    @Override
     public boolean challenge(HttpFacade httpFacade) {
         try {
             String issuerURL = deployment.getEntityID();
diff --git a/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java b/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java
index a63b934..ec97fc9 100755
--- a/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java
+++ b/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java
@@ -259,6 +259,7 @@ public abstract class AbstractSamlAuthenticator extends LoginAuthenticator {
 
         AuthChallenge challenge = authenticator.getChallenge();
         if (challenge != null) {
+            challenge.challenge(facade);
             if (challenge.errorPage() && errorPage != null) {
                 Response response = (Response)res;
                 try {
@@ -268,7 +269,6 @@ public abstract class AbstractSamlAuthenticator extends LoginAuthenticator {
                 }
 
             }
-            challenge.challenge(facade);
         }
         return Authentication.SEND_CONTINUE;
     }
diff --git a/saml/client-adapter/servlet-filter/pom.xml b/saml/client-adapter/servlet-filter/pom.xml
index 219110c..c2de715 100755
--- a/saml/client-adapter/servlet-filter/pom.xml
+++ b/saml/client-adapter/servlet-filter/pom.xml
@@ -9,7 +9,7 @@
 	</parent>
 	<modelVersion>4.0.0</modelVersion>
 
-	<artifactId>keycloak-saml-server-filter-adapter</artifactId>
+	<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
 	<name>Keycloak SAML Servlet Filter</name>
 	<description />
 
diff --git a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
index dc4cd32..2d3a7fd 100755
--- a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
+++ b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
@@ -2,6 +2,7 @@ package org.keycloak.adapters.saml.servlet;
 
 import org.jboss.logging.Logger;
 import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakAccount;
 import org.keycloak.adapters.SessionIdMapper;
 import org.keycloak.adapters.saml.SamlSession;
 import org.keycloak.adapters.saml.SamlSessionStore;
@@ -34,8 +35,6 @@ public class FilterSamlSessionStore extends FilterSessionStore implements SamlSe
         this.idMapper = idMapper;
     }
 
-    protected boolean needRequestRestore;
-
     @Override
     public void logoutAccount() {
         HttpSession session = request.getSession(false);
@@ -107,91 +106,8 @@ public class FilterSamlSessionStore extends FilterSessionStore implements SamlSe
     public HttpServletRequestWrapper getWrap() {
         HttpSession session = request.getSession(true);
         final SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
-        if (needRequestRestore) {
-            final String method = (String)session.getAttribute(SAVED_METHOD);
-            final byte[] body = (byte[])session.getAttribute(SAVED_BODY);
-            final MultivaluedHashMap<String, String> headers = (MultivaluedHashMap<String, String>)session.getAttribute(SAVED_HEADERS);
-            clearSavedRequest(session);
-            HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
-                @Override
-                public boolean isUserInRole(String role) {
-                    return samlSession.getRoles().contains(role);
-                }
-
-                @Override
-                public Principal getUserPrincipal() {
-                    return samlSession.getPrincipal();
-                }
-
-                @Override
-                public String getMethod() {
-                    if (needRequestRestore) {
-                        return method;
-                    } else {
-                        return super.getMethod();
-
-                    }
-                }
-
-                @Override
-                public String getHeader(String name) {
-                    if (needRequestRestore && headers != null) {
-                        return headers.getFirst(name);
-                    }
-                    return super.getHeader(name);
-                }
-
-                @Override
-                public Enumeration<String> getHeaders(String name) {
-                    if (needRequestRestore && headers != null) {
-                        List<String> values = headers.getList(name);
-                        if (values == null) return Collections.emptyEnumeration();
-                        else return Collections.enumeration(values);
-                    }
-                    return super.getHeaders(name);
-                }
-
-                @Override
-                public Enumeration<String> getHeaderNames() {
-                    if (needRequestRestore && headers != null) {
-                        return Collections.enumeration(headers.keySet());
-                    }
-                    return super.getHeaderNames();
-                }
-
-                @Override
-                public ServletInputStream getInputStream() throws IOException {
-
-                    if (needRequestRestore && body != null) {
-                        final ByteArrayInputStream is = new ByteArrayInputStream(body);
-                        return new ServletInputStream() {
-                            @Override
-                            public int read() throws IOException {
-                                return is.read();
-                            }
-                        };
-                    }
-                    return super.getInputStream();
-                }
-            };
-            return wrapper;
-        } else {
-            return new HttpServletRequestWrapper(request) {
-                @Override
-                public boolean isUserInRole(String role) {
-                    return samlSession.getRoles().contains(role);
-                }
-
-                @Override
-                public Principal getUserPrincipal() {
-                    return samlSession.getPrincipal();
-                }
-
-            };
-        }
-
-
-
+        final KeycloakAccount account = samlSession;
+        return buildWrapper(session, account);
     }
 
     @Override
diff --git a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
index c1b6ad5..8188066 100755
--- a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
+++ b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
@@ -24,6 +24,8 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.logging.Level;
@@ -52,10 +54,20 @@ public class SamlFilter implements Filter {
                 //deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
             }
         } else {
-            String path = "/WEB-INF/keycloak-saml.xml";
-            String pathParam = filterConfig.getInitParameter("keycloak.config.file");
-            if (pathParam != null) path = pathParam;
-            InputStream is = filterConfig.getServletContext().getResourceAsStream(path);
+            String fp = filterConfig.getInitParameter("keycloak.config.file");
+            InputStream is = null;
+            if (fp != null) {
+                try {
+                    is = new FileInputStream(fp);
+                } catch (FileNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+            } else {
+                String path = "/WEB-INF/keycloak-saml.xml";
+                String pathParam = filterConfig.getInitParameter("keycloak.config.path");
+                if (pathParam != null) path = pathParam;
+                is = filterConfig.getServletContext().getResourceAsStream(path);
+            }
             final SamlDeployment deployment;
             if (is == null) {
                 log.info("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
@@ -124,18 +136,16 @@ public class SamlFilter implements Filter {
         AuthChallenge challenge = authenticator.getChallenge();
         if (challenge != null) {
             log.fine("challenge");
+            challenge.challenge(facade);
             if (challenge.errorPage()) {
-                response.sendError(403);
+                response.sendError(challenge.getResponseCode());
                 return;
             }
             log.fine("sending challenge");
-            challenge.challenge(facade);
+            return;
         }
-        if (outcome == AuthOutcome.FAILED) {
+        if (!facade.isEnded()) {
             response.sendError(403);
-        } else if (!facade.isEnded()) {
-            chain.doFilter(req, res);
-            return;
         }
 
     }
diff --git a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
index febbb72..cfc21a0 100755
--- a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
+++ b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
@@ -212,14 +212,12 @@ public abstract class AbstractSamlAuthenticatorValve extends FormAuthenticator i
             if (loginConfig == null) {
                 loginConfig = request.getContext().getLoginConfig();
             }
+            challenge.challenge(facade);
             if (challenge.errorPage()) {
                 log.fine("error page");
                 if (forwardToErrorPageInternal(request, response, loginConfig))return false;
             }
-            log.fine("sending challenge");
-            challenge.challenge(facade);
         }
-        log.fine("No challenge, but failed authentication");
         return false;
     }
 
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 7587247..dad4114 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -107,7 +107,11 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-saml-server-filter-adapter</artifactId>
+            <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-servlet-filter-adapter</artifactId>
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
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 a63132f..d5f9072 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
@@ -604,6 +604,7 @@ public class AdapterTestStrategy extends ExternalResource {
         // logout sessions in account management
         accountSessionsPage.realm("demo");
         accountSessionsPage.open();
+        Assert.assertTrue(accountSessionsPage.isCurrent());
         accountSessionsPage.logoutAll();
 
         // Assert I need to login again (logout was propagated to the app)
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/FilterAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/FilterAdapterTest.java
new file mode 100755
index 0000000..39c0368
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/FilterAdapterTest.java
@@ -0,0 +1,214 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.adapter;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+import java.net.URL;
+import java.security.PublicKey;
+
+/**
+ * Tests Undertow Adapter
+ *
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class FilterAdapterTest {
+
+    public static PublicKey realmPublicKey;
+    @ClassRule
+    public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
+        @Override
+        protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+            RealmModel realm = AdapterTestStrategy.baseAdapterTestInitialization(session, manager, adminRealm, getClass());
+            realmPublicKey = realm.getPublicKey();
+
+            URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json");
+            createApplicationDeployment()
+                    .name("customer-portal").contextPath("/customer-portal")
+                    .servletClass(CustomerServlet.class).adapterConfigPath(url.getPath())
+                    .role("user").deployApplicationWithFilter();
+
+            url = getClass().getResource("/adapter-test/secure-portal-keycloak.json");
+            createApplicationDeployment()
+                    .name("secure-portal").contextPath("/secure-portal")
+                    .servletClass(CallAuthenticatedServlet.class).adapterConfigPath(url.getPath())
+                    .role("user")
+                    .isConstrained(false).deployApplicationWithFilter();
+
+            url = getClass().getResource("/adapter-test/customer-db-keycloak.json");
+            createApplicationDeployment()
+                    .name("customer-db").contextPath("/customer-db")
+                    .servletClass(CustomerDatabaseServlet.class).adapterConfigPath(url.getPath())
+                    .role("user")
+                    .errorPage(null).deployApplicationWithFilter();
+
+            createApplicationDeployment()
+                    .name("customer-db-error-page").contextPath("/customer-db-error-page")
+                    .servletClass(CustomerDatabaseServlet.class).adapterConfigPath(url.getPath())
+                    .role("user").deployApplicationWithFilter();
+
+            url = getClass().getResource("/adapter-test/product-keycloak.json");
+            createApplicationDeployment()
+                    .name("product-portal").contextPath("/product-portal")
+                    .servletClass(ProductServlet.class).adapterConfigPath(url.getPath())
+                    .role("user").deployApplicationWithFilter();
+
+            // Test that replacing system properties works for adapters
+            System.setProperty("app.server.base.url", "http://localhost:8081");
+            System.setProperty("my.host.name", "localhost");
+            url = getClass().getResource("/adapter-test/session-keycloak.json");
+            createApplicationDeployment()
+                    .name("session-portal").contextPath("/session-portal")
+                    .servletClass(SessionServlet.class).adapterConfigPath(url.getPath())
+                    .role("user").deployApplicationWithFilter();
+
+            url = getClass().getResource("/adapter-test/input-keycloak.json");
+            createApplicationDeployment()
+                    .name("input-portal").contextPath("/input-portal")
+                    .servletClass(InputServlet.class).adapterConfigPath(url.getPath())
+                    .role("user").constraintUrl("/secured/*").deployApplicationWithFilter();
+        }
+    };
+
+    @Rule
+    public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8081", keycloakRule);
+
+    @Test
+    public void testLoginSSOAndLogout() throws Exception {
+        testStrategy.testLoginSSOAndLogout();
+    }
+
+    @Test
+    public void testSavedPostRequest() throws Exception {
+        testStrategy.testSavedPostRequest();
+    }
+
+    @Test
+    public void testServletRequestLogout() throws Exception {
+        testStrategy.testServletRequestLogout();
+    }
+
+    @Test
+    public void testLoginSSOIdle() throws Exception {
+        testStrategy.testLoginSSOIdle();
+
+    }
+
+    @Test
+    public void testLoginSSOIdleRemoveExpiredUserSessions() throws Exception {
+        testStrategy.testLoginSSOIdleRemoveExpiredUserSessions();
+    }
+
+    @Test
+    public void testLoginSSOMax() throws Exception {
+        testStrategy.testLoginSSOMax();
+    }
+
+    /**
+     * KEYCLOAK-518
+     * @throws Exception
+     */
+    @Test
+    public void testNullBearerToken() throws Exception {
+        testStrategy.testNullBearerToken();
+    }
+
+    /**
+     * KEYCLOAK-1368
+     * @throws Exception
+     */
+    /*
+    can't test because of the way filter works
+    @Test
+    public void testNullBearerTokenCustomErrorPage() throws Exception {
+        testStrategy.testNullBearerTokenCustomErrorPage();
+    }
+     */
+
+    /**
+     * KEYCLOAK-518
+     * @throws Exception
+     */
+    @Test
+    public void testBadUser() throws Exception {
+        testStrategy.testBadUser();
+    }
+
+    @Test
+    public void testVersion() throws Exception {
+        testStrategy.testVersion();
+    }
+
+    /*
+      Don't need to test this because HttpServletRequest.authenticate doesn't make sense with filter implementation
+
+    @Test
+    public void testAuthenticated() throws Exception {
+        testStrategy.testAuthenticated();
+    }
+    */
+
+    /**
+     * KEYCLOAK-732
+     *
+     * @throws Throwable
+     */
+    @Test
+    public void testSingleSessionInvalidated() throws Throwable {
+        testStrategy.testSingleSessionInvalidated();
+    }
+
+    /**
+     * KEYCLOAK-741
+     */
+    @Test
+    public void testSessionInvalidatedAfterFailedRefresh() throws Throwable {
+        testStrategy.testSessionInvalidatedAfterFailedRefresh();
+
+    }
+
+    /**
+     * KEYCLOAK-942
+     */
+    @Test
+    public void testAdminApplicationLogout() throws Throwable {
+        testStrategy.testAdminApplicationLogout();
+    }
+
+    /**
+     * KEYCLOAK-1216
+     */
+    /*
+        Can't test this because backchannel logout for filter does not invalidate the session
+    @Test
+    public void testAccountManagementSessionsLogout() throws Throwable {
+        testStrategy.testAccountManagementSessionsLogout();
+    }
+     */
+
+}
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 d18b615..8c673a0 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
@@ -1,6 +1,7 @@
 package org.keycloak.testsuite.rule;
 
 import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.FilterInfo;
 import io.undertow.servlet.api.LoginConfig;
 import io.undertow.servlet.api.SecurityConstraint;
 import io.undertow.servlet.api.SecurityInfo;
@@ -11,6 +12,8 @@ import org.junit.rules.ExternalResource;
 import org.junit.rules.TemporaryFolder;
 import org.keycloak.Config;
 import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.saml.servlet.SamlFilter;
+import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.RealmModel;
@@ -24,6 +27,7 @@ import org.keycloak.testsuite.KeycloakServer;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.Time;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.Servlet;
 import javax.ws.rs.core.Application;
 import java.io.ByteArrayOutputStream;
@@ -350,6 +354,22 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
             server.getServer().deploy(di);
         }
 
+        public void deployApplicationWithFilter() {
+            DeploymentInfo di = createDeploymentInfo(name, contextPath, servletClass);
+            FilterInfo filter = new FilterInfo("keycloak-filter", KeycloakOIDCFilter.class);
+            if (null == keycloakConfigResolver) {
+                filter.addInitParam("keycloak.config.file", adapterConfigPath);
+            } else {
+                filter.addInitParam("keycloak.config.resolver", keycloakConfigResolver.getCanonicalName());
+            }
+            di.addFilter(filter);
+            di.addFilterUrlMapping("keycloak-filter", constraintUrl, DispatcherType.REQUEST);
+            server.getServer().deploy(di);
+
+
+
+        }
+
     }
 
 }