keycloak-uncached

Changes

integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java 133(+0 -133)

integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java 301(+0 -301)

integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakAuthenticationMechanism.java 133(+0 -133)

integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletOAuthAuthenticator.java 23(+0 -23)

services/src/main/java/org/keycloak/services/resources/Transaction.java 99(+0 -99)

Details

diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthChallenge.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthChallenge.java
new file mode 100755
index 0000000..4682808
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthChallenge.java
@@ -0,0 +1,14 @@
+package org.keycloak.adapters;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AuthChallenge {
+    /**
+     *
+     * @param exchange
+     * @return challenge sent
+     */
+    boolean challenge(HttpFacade exchange);
+}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthOutcome.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthOutcome.java
new file mode 100755
index 0000000..e19dfa0
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AuthOutcome.java
@@ -0,0 +1,11 @@
+package org.keycloak.adapters;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public enum AuthOutcome {
+    NOT_ATTEMPTED,
+    FAILED,
+    AUTHENTICATED
+}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/HttpFacade.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/HttpFacade.java
new file mode 100755
index 0000000..25b9a7f
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/HttpFacade.java
@@ -0,0 +1,87 @@
+package org.keycloak.adapters;
+
+import javax.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * Bridge between core adapter and HTTP Engine
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface HttpFacade {
+
+    public class Cookie {
+        protected String name;
+        protected String value;
+        protected int version;
+        protected String domain;
+        protected String path;
+
+        public Cookie(String name, String value, int version, String domain, String path) {
+            this.name = name;
+            this.value = value;
+            this.version = version;
+            this.domain = domain;
+            this.path = path;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public int getVersion() {
+            return version;
+        }
+
+        public String getDomain() {
+            return domain;
+        }
+
+        public String getPath() {
+            return path;
+        }
+    }
+
+    interface Request {
+        /**
+         * Full request URI with query params
+         *
+         * @return
+         */
+        String getURI();
+
+        /**
+         * HTTPS?
+         *
+         * @return
+         */
+        boolean isSecure();
+
+        String getQueryParamValue(String param);
+        Cookie getCookie(String cookieName);
+        List<String> getHeaders(String name);
+    }
+
+    interface Response {
+        void setStatus(int status);
+        void addHeader(String name, String value);
+        void setHeader(String name, String value);
+        void resetCookie(String name, String path);
+        void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly);
+
+        /**
+         * If the response is finished, end it.
+         *
+         */
+        void end();
+    }
+
+    Request getRequest();
+    Response getResponse();
+    X509Certificate[] getCertificateChain();
+}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
new file mode 100755
index 0000000..f0d697c
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
@@ -0,0 +1,95 @@
+package org.keycloak.adapters;
+
+import org.jboss.logging.Logger;
+import org.keycloak.KeycloakPrincipal;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class RequestAuthenticator {
+    protected Logger log = Logger.getLogger(RequestAuthenticator.class);
+
+    protected HttpFacade facade;
+    protected KeycloakDeployment deployment;
+    protected AuthChallenge challenge;
+    protected int sslRedirectPort;
+
+    public RequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort) {
+        this.facade = facade;
+        this.deployment = deployment;
+        this.sslRedirectPort = sslRedirectPort;
+    }
+
+    public RequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment) {
+        this.facade = facade;
+        this.deployment = deployment;
+    }
+
+    public AuthChallenge getChallenge() {
+        return challenge;
+    }
+
+    public AuthOutcome authenticate() {
+        log.info("--> authenticate()");
+        BearerTokenRequestAuthenticator bearer = createBearerTokenAuthenticator();
+        AuthOutcome outcome = bearer.authenticate(facade);
+        if (outcome == AuthOutcome.FAILED) {
+            challenge = bearer.getChallenge();
+            return AuthOutcome.FAILED;
+        } else if (outcome == AuthOutcome.AUTHENTICATED) {
+            completeAuthentication(bearer);
+            return AuthOutcome.AUTHENTICATED;
+        } else if (deployment.isBearerOnly()) {
+            challenge = bearer.getChallenge();
+            return AuthOutcome.NOT_ATTEMPTED;
+        }
+
+        if (isCached()) {
+            return AuthOutcome.AUTHENTICATED;
+        }
+
+        OAuthRequestAuthenticator oauth = createOAuthAuthenticator();
+        outcome = oauth.authenticate();
+        if (outcome == AuthOutcome.FAILED) {
+            challenge = oauth.getChallenge();
+            return AuthOutcome.FAILED;
+        } else if (outcome == AuthOutcome.NOT_ATTEMPTED) {
+            challenge = oauth.getChallenge();
+            return AuthOutcome.NOT_ATTEMPTED;
+
+        }
+        completeAuthentication(oauth);
+
+        // redirect to strip out access code and state query parameters
+        facade.getResponse().setHeader("Location", oauth.getStrippedOauthParametersRequestUri());
+        facade.getResponse().setStatus(302);
+        facade.getResponse().end();
+
+        log.info("AUTHENTICATED");
+        return AuthOutcome.AUTHENTICATED;
+    }
+
+    protected abstract OAuthRequestAuthenticator createOAuthAuthenticator();
+
+    protected BearerTokenRequestAuthenticator createBearerTokenAuthenticator() {
+        return new BearerTokenRequestAuthenticator(deployment);
+    }
+
+    protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
+        final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), null);
+        RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
+        completeOAuthAuthentication(principal, session);
+    }
+
+    protected abstract void completeOAuthAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session);
+    protected abstract void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session);
+    protected abstract boolean isCached();
+
+    protected void completeAuthentication(BearerTokenRequestAuthenticator bearer) {
+        final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), bearer.getSurrogate());
+        RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, bearer.getTokenString(), bearer.getToken(), null, null, null);
+        completeBearerAuthentication(principal, session);
+    }
+
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaHttpFacade.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaHttpFacade.java
new file mode 100755
index 0000000..62adcb6
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaHttpFacade.java
@@ -0,0 +1,136 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.keycloak.adapters.HttpFacade;
+
+import javax.security.cert.X509Certificate;
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CatalinaHttpFacade implements HttpFacade {
+    protected org.apache.catalina.connector.Request request;
+    protected HttpServletResponse response;
+    protected RequestFacade requestFacade = new RequestFacade();
+    protected ResponseFacade responseFacade = new ResponseFacade();
+
+    protected class RequestFacade implements Request {
+        @Override
+        public String getURI() {
+            StringBuffer buf = request.getRequestURL();
+            if (request.getQueryString() != null) {
+                buf.append('?').append(request.getQueryString());
+            }
+            return buf.toString();
+        }
+
+        @Override
+        public boolean isSecure() {
+            return request.isSecure();
+        }
+
+        @Override
+        public String getQueryParamValue(String paramName) {
+            return request.getParameter(paramName);
+        }
+
+        @Override
+        public Cookie getCookie(String cookieName) {
+            if (request.getCookies() == null) return null;
+            javax.servlet.http.Cookie cookie = null;
+            for (javax.servlet.http.Cookie c : request.getCookies()) {
+                if (c.getName().equals(cookieName)) {
+                    cookie = c;
+                    break;
+                }
+            }
+            if (cookie == null) return null;
+            return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
+        }
+
+        @Override
+        public List<String> getHeaders(String name) {
+            Enumeration<String> headers = request.getHeaders(name);
+            if (headers == null) return null;
+            List<String> list = new ArrayList<String>();
+            while (headers.hasMoreElements()) {
+                list.add(headers.nextElement());
+            }
+            return list;
+        }
+    }
+
+    protected class ResponseFacade implements Response {
+        protected boolean ended;
+
+        @Override
+        public void setStatus(int status) {
+            response.setStatus(status);
+        }
+
+        @Override
+        public void addHeader(String name, String value) {
+            response.addHeader(name, value);
+        }
+
+        @Override
+        public void setHeader(String name, String value) {
+            response.setHeader(name, value);
+        }
+
+        @Override
+        public void resetCookie(String name, String path) {
+            setCookie(name, "", null, path, 0, false, false);
+        }
+
+        @Override
+        public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
+            javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie(name, value);
+            if (domain != null) cookie.setDomain(domain);
+            if (path != null) cookie.setPath(path);
+            if (secure) cookie.setSecure(true);
+            if (httpOnly) cookie.setHttpOnly(httpOnly);
+            cookie.setMaxAge(maxAge);
+            response.addCookie(cookie);
+        }
+
+        @Override
+        public void end() {
+            ended = true;
+        }
+
+        public boolean isEnded() {
+            return ended;
+        }
+    }
+
+    public CatalinaHttpFacade(org.apache.catalina.connector.Request request, HttpServletResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    @Override
+    public Request getRequest() {
+        return requestFacade;
+    }
+
+    @Override
+    public Response getResponse() {
+        return responseFacade;
+    }
+
+    @Override
+    public X509Certificate[] getCertificateChain() {
+        throw new IllegalStateException("Not supported yet");
+    }
+
+    public boolean isEnded() {
+        return responseFacade.isEnded();
+    }
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
new file mode 100755
index 0000000..143df34
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
@@ -0,0 +1,118 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OAuthRequestAuthenticator;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.keycloak.representations.AccessToken;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CatalinaRequestAuthenticator extends RequestAuthenticator {
+    protected KeycloakAuthenticatorValve valve;
+    protected UserSessionManagement userSessionManagement;
+    protected Request request;
+
+    public CatalinaRequestAuthenticator(KeycloakDeployment deployment,
+                                        KeycloakAuthenticatorValve valve, UserSessionManagement userSessionManagement,
+                                        CatalinaHttpFacade facade,
+                                        Request request) {
+        super(facade, deployment, request.getConnector().getRedirectPort());
+        this.valve = valve;
+        this.userSessionManagement = userSessionManagement;
+        this.request = request;
+    }
+
+    @Override
+    protected OAuthRequestAuthenticator createOAuthAuthenticator() {
+        return new OAuthRequestAuthenticator(facade, deployment, sslRedirectPort) {
+            @Override
+            protected void saveRequest() {
+                try {
+                    valve.keycloakSaveRequest(request);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void completeOAuthAuthentication(KeycloakPrincipal skp, RefreshableKeycloakSecurityContext securityContext) {
+        Set<String> roles = getRolesFromToken(securityContext);
+        GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skp, roles);
+        Session session = request.getSessionInternal(true);
+        session.setPrincipal(principal);
+        session.setAuthType("OAUTH");
+        session.setNote(KeycloakSecurityContext.class.getName(), securityContext);
+        String username = securityContext.getToken().getSubject();
+        log.debug("userSessionManage.login: " + username);
+        userSessionManagement.login(session, username);
+    }
+
+    @Override
+    protected void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext securityContext) {
+        Set<String> roles = getRolesFromToken(securityContext);
+        Principal generalPrincipal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), principal, roles);
+        request.setUserPrincipal(generalPrincipal);
+        request.setAuthType("KEYCLOAK");
+        request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+    }
+
+    protected Set<String> getRolesFromToken(RefreshableKeycloakSecurityContext session) {
+        Set<String> roles = null;
+        if (deployment.isUseResourceRoleMappings()) {
+            AccessToken.Access access = session.getToken().getResourceAccess(deployment.getResourceName());
+            if (access != null) roles = access.getRoles();
+        } else {
+            AccessToken.Access access =  session.getToken().getRealmAccess();
+            if (access != null) roles = access.getRoles();
+        }
+        if (roles == null) roles = Collections.emptySet();
+        return roles;
+    }
+
+    @Override
+    protected boolean isCached() {
+        if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null)
+            return false;
+        log.debug("remote logged in already");
+        GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
+        request.setUserPrincipal(principal);
+        request.setAuthType("KEYCLOAK");
+        Session session = request.getSessionInternal();
+        if (session != null) {
+            RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) session.getNote(KeycloakSecurityContext.class.getName());
+            if (securityContext != null) {
+                securityContext.setDeployment(deployment);
+                request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+            }
+        }
+        restoreRequest();
+        return true;
+    }
+
+    protected void restoreRequest() {
+        if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null) {
+            if (valve.keycloakRestoreRequest(request)) {
+                log.debug("restoreRequest");
+            } else {
+                log.debug("Restore of original request failed");
+                throw new RuntimeException("Restore of original request failed");
+            }
+        }
+    }
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index ab380aa..2012c66 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -17,9 +17,11 @@ import org.jboss.logging.Logger;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.KeycloakPrincipal;
 import org.keycloak.adapters.AdapterConstants;
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.AuthOutcome;
 import org.keycloak.adapters.KeycloakDeployment;
 import org.keycloak.adapters.KeycloakDeploymentBuilder;
-import org.keycloak.adapters.RefreshableKeycloakSession;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.adapters.action.AdminAction;
 import org.keycloak.representations.adapters.action.PushNotBeforeAction;
@@ -154,29 +156,21 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
 
     @Override
     public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException {
-        try {
-            if (bearer(false, request, response)) return true;
-            else if (checkLoggedIn(request, response)) {
-                if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null) {
-                    if (restoreRequest(request, request.getSessionInternal())) {
-                        log.debug("restoreRequest");
-                        return (true);
-                    } else {
-                        log.debug("Restore of original request failed");
-                        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
-                        return (false);
-                    }
-                } else {
-                    return true;
-                }
+        CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
+        CatalinaRequestAuthenticator authenticator = new CatalinaRequestAuthenticator(deployment, this, userSessionManagement, facade, request);
+        AuthOutcome outcome = authenticator.authenticate();
+        if (outcome == AuthOutcome.AUTHENTICATED) {
+            if (facade.isEnded()) {
+                return false;
             }
-
-            // initiate or continue oauth2 protocol
-            if (!deployment.isBearerOnly()) oauth(request, response);
-        } catch (LoginException e) {
+            return true;
+        }
+        AuthChallenge challenge = authenticator.getChallenge();
+        if (challenge != null) {
+            challenge.challenge(facade);
         }
         return false;
-    }
+     }
 
     protected JWSInput verifyAdminRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
         String token = StreamUtil.readString(request.getInputStream());
@@ -303,15 +297,6 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
         response.setStatus(HttpServletResponse.SC_NO_CONTENT);
     }
 
-    protected boolean bearer(boolean challenge, Request request, HttpServletResponse response) throws LoginException, IOException {
-        boolean useResourceRoleMappings = deployment.isUseResourceRoleMappings();
-        CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(deployment, challenge);
-        if (bearer.login(request, response)) {
-            return true;
-        }
-        return false;
-    }
-
     /**
      * Checks that access token is still valid.  Will attempt refresh of token if it is not.
      *
@@ -319,7 +304,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
      */
     protected void checkKeycloakSession(Request request) {
         if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null) return;
-        RefreshableKeycloakSession session = (RefreshableKeycloakSession)request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
+        RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext)request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
         if (session == null) return;
         // just in case session got serialized
         session.setDeployment(deployment);
@@ -337,62 +322,15 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
         request.getSessionInternal().setAuthType(null);
     }
 
-    protected boolean checkLoggedIn(Request request, HttpServletResponse response) {
-        if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null)
-            return false;
-        log.debug("remote logged in already");
-        GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
-        request.setUserPrincipal(principal);
-        request.setAuthType("KEYCLOAK");
-        Session session = request.getSessionInternal();
-        if (session != null) {
-            KeycloakSecurityContext skSession = (KeycloakSecurityContext) session.getNote(KeycloakSecurityContext.class.getName());
-            if (skSession != null) {
-                request.setAttribute(KeycloakSecurityContext.class.getName(), skSession);
-            }
-        }
-        return true;
+    public void keycloakSaveRequest(Request request) throws IOException {
+        saveRequest(request, request.getSessionInternal(true));
     }
 
-    /**
-     * This method always set the HTTP response, so do not continue after invoking
-     */
-    protected void oauth(Request request, HttpServletResponse response) throws IOException {
-        ServletOAuthLogin oauth = new ServletOAuthLogin(deployment, request, response, request.getConnector().getRedirectPort());
-        String code = oauth.getCode();
-        if (code == null) {
-            String error = oauth.getError();
-            if (error != null) {
-                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "OAuth " + error);
-                return;
-            } else {
-                saveRequest(request, request.getSessionInternal(true));
-                oauth.loginRedirect();
-            }
-            return;
-        } else {
-            if (!oauth.resolveCode(code)) return;
-
-            AccessToken token = oauth.getToken();
-            Set<String> roles = new HashSet<String>();
-            if (deployment.isUseResourceRoleMappings()) {
-                AccessToken.Access access = token.getResourceAccess(deployment.getResourceName());
-                if (access != null) roles.addAll(access.getRoles());
-            } else {
-                AccessToken.Access access = token.getRealmAccess();
-                if (access != null) roles.addAll(access.getRoles());
-            }
-            KeycloakPrincipal skp = new KeycloakPrincipal(token.getSubject(), null);
-            GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(context.getRealm(), skp, roles);
-            Session session = request.getSessionInternal(true);
-            session.setPrincipal(principal);
-            session.setAuthType("OAUTH");
-            KeycloakSecurityContext skSession = new RefreshableKeycloakSession(deployment, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
-            session.setNote(KeycloakSecurityContext.class.getName(), skSession);
-
-            String username = token.getSubject();
-            log.debug("userSessionManage.login: " + username);
-            userSessionManagement.login(session, username);
+    public boolean keycloakRestoreRequest(Request request) {
+        try {
+            return restoreRequest(request, request.getSessionInternal());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
         }
     }
 
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
index 5e829bf..6d23569 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
@@ -38,10 +38,6 @@ public class UserSessionManagement implements SessionListener {
         }
     }
 
-    public int getNumUserLogins() {
-        return userSessionMap.size();
-    }
-
     public int getActiveSessions() {
         int active = 0;
         synchronized (userSessionMap) {
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
index 7b630c1..9b0a911 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
@@ -65,22 +65,18 @@ public class KeycloakServletExtension implements ServletExtension {
         KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
         PreflightCorsHandler.Wrapper preflight = new PreflightCorsHandler.Wrapper(deployment);
         UserSessionManagement userSessionManagement = new UserSessionManagement(deployment);
-        ServletKeycloakAuthenticationMechanism auth = null;
-        auth = new ServletKeycloakAuthenticationMechanism(
-                userSessionManagement,
-                deployment,
-                deploymentInfo.getConfidentialPortManager());
+        final ServletKeycloakAuthMech mech = new ServletKeycloakAuthMech(deployment, userSessionManagement, deploymentInfo.getConfidentialPortManager());
+
         AuthenticatedActionsHandler.Wrapper actions = new AuthenticatedActionsHandler.Wrapper(deployment);
 
         // setup handlers
 
         deploymentInfo.addInitialHandlerChainWrapper(preflight); // cors preflight
         deploymentInfo.addOuterHandlerChainWrapper(new ServletAdminActionsHandler.Wrapper(deployment, userSessionManagement));
-        final ServletKeycloakAuthenticationMechanism theAuth = auth;
         deploymentInfo.addAuthenticationMechanism("KEYCLOAK", new AuthenticationMechanismFactory() {
             @Override
             public AuthenticationMechanism create(String s, FormParserFactory formParserFactory, Map<String, String> stringStringMap) {
-                return theAuth;
+                return mech;
             }
         }); // authentication
         deploymentInfo.addInnerHandlerChainWrapper(actions); // handles authenticated actions and cors.
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java
index ab5e514..f66cd01 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakUndertowAccount.java
@@ -4,7 +4,7 @@ import io.undertow.security.idm.Account;
 import org.jboss.logging.Logger;
 import org.keycloak.KeycloakPrincipal;
 import org.keycloak.adapters.KeycloakDeployment;
-import org.keycloak.adapters.RefreshableKeycloakSession;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
 import org.keycloak.representations.AccessToken;
 
 import java.io.Serializable;
@@ -18,11 +18,11 @@ import java.util.Set;
 */
 public class KeycloakUndertowAccount implements Account, Serializable {
     protected static Logger log = Logger.getLogger(KeycloakUndertowAccount.class);
-    protected RefreshableKeycloakSession session;
+    protected RefreshableKeycloakSecurityContext session;
     protected KeycloakPrincipal principal;
     protected Set<String> accountRoles;
 
-    public KeycloakUndertowAccount(KeycloakPrincipal principal, RefreshableKeycloakSession session, KeycloakDeployment deployment) {
+    public KeycloakUndertowAccount(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session, KeycloakDeployment deployment) {
         this.principal = principal;
         this.session = session;
         setRoles(session.getToken(), deployment);
@@ -59,7 +59,7 @@ public class KeycloakUndertowAccount implements Account, Serializable {
         return session.getTokenString();
     }
 
-    public RefreshableKeycloakSession getSession() {
+    public RefreshableKeycloakSecurityContext getSession() {
         return session;
     }
 
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
new file mode 100755
index 0000000..8160592
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
@@ -0,0 +1,60 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.security.api.AuthenticationMechanism;
+import io.undertow.security.api.SecurityContext;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.api.ConfidentialPortManager;
+import io.undertow.util.AttachmentKey;
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.AuthOutcome;
+import org.keycloak.adapters.KeycloakDeployment;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletKeycloakAuthMech implements AuthenticationMechanism {
+    public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
+
+    protected KeycloakDeployment deployment;
+    protected UserSessionManagement userSessionManagement;
+    protected ConfidentialPortManager portManager;
+
+    public ServletKeycloakAuthMech(KeycloakDeployment deployment, UserSessionManagement userSessionManagement, ConfidentialPortManager portManager) {
+        this.deployment = deployment;
+        this.userSessionManagement = userSessionManagement;
+        this.portManager = portManager;
+    }
+
+    @Override
+    public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
+        UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+        ServletRequestAuthenticator authenticator = new ServletRequestAuthenticator(facade, deployment,
+                portManager.getConfidentialPort(exchange), securityContext, exchange, userSessionManagement);
+        AuthOutcome outcome = authenticator.authenticate();
+        if (outcome == AuthOutcome.AUTHENTICATED) {
+            return AuthenticationMechanismOutcome.AUTHENTICATED;
+        }
+        AuthChallenge challenge = authenticator.getChallenge();
+        if (challenge != null) {
+            exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, challenge);
+        }
+
+        if (outcome == AuthOutcome.FAILED) {
+            return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
+        }
+        return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+    }
+
+    @Override
+    public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
+        AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
+        if (challenge != null) {
+            UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
+            if (challenge.challenge(facade)) {
+                return new ChallengeResult(true, exchange.getResponseCode());
+            }
+        }
+        return new ChallengeResult(false);
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowHttpFacade.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowHttpFacade.java
new file mode 100755
index 0000000..d08864d
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowHttpFacade.java
@@ -0,0 +1,127 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.Cookie;
+import io.undertow.server.handlers.CookieImpl;
+import io.undertow.util.HttpString;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.util.KeycloakUriBuilder;
+
+import javax.security.cert.X509Certificate;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UndertowHttpFacade implements HttpFacade {
+    protected HttpServerExchange exchange;
+    protected RequestFacade requestFacade = new RequestFacade();
+    protected ResponseFacade responseFacade = new ResponseFacade();
+
+    protected class RequestFacade implements Request {
+        @Override
+        public String getURI() {
+            KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(exchange.getRequestURI())
+                    .replaceQuery(exchange.getQueryString());
+            if (!exchange.isHostIncludedInRequestURI()) uriBuilder.scheme(exchange.getRequestScheme()).host(exchange.getHostAndPort());
+            return uriBuilder.build().toString();
+        }
+
+        @Override
+        public boolean isSecure() {
+            return exchange.getProtocol().toString().equalsIgnoreCase("https");
+        }
+
+        @Override
+        public String getQueryParamValue(String param) {
+            Map<String,Deque<String>> queryParameters = exchange.getQueryParameters();
+            if (queryParameters == null) return null;
+            Deque<String> strings = queryParameters.get(param);
+            if (strings == null) return null;
+            return strings.getFirst();
+        }
+
+        @Override
+        public Cookie getCookie(String cookieName) {
+            Map<String, io.undertow.server.handlers.Cookie> requestCookies = exchange.getRequestCookies();
+            if (requestCookies == null) return null;
+            io.undertow.server.handlers.Cookie cookie = requestCookies.get(cookieName);
+            if (cookie == null) return null;
+            return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
+        }
+
+        @Override
+        public List<String> getHeaders(String name) {
+            return exchange.getRequestHeaders().get(name);
+        }
+    }
+
+    protected class ResponseFacade implements Response {
+        @Override
+        public void setStatus(int status) {
+            exchange.setResponseCode(status);
+        }
+
+        @Override
+        public void addHeader(String name, String value) {
+            exchange.getResponseHeaders().add(new HttpString(name), value);
+        }
+
+        @Override
+        public void setHeader(String name, String value) {
+            exchange.getResponseHeaders().put(new HttpString(name), value);
+        }
+
+        @Override
+        public void resetCookie(String name, String path) {
+            CookieImpl cookie = new CookieImpl(name, "");
+            cookie.setMaxAge(0);
+            cookie.setPath(path);
+            exchange.setResponseCookie(cookie);
+        }
+
+        @Override
+        public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
+            CookieImpl cookie = new CookieImpl(name, value);
+            cookie.setPath(path);
+            cookie.setDomain(domain);
+            cookie.setMaxAge(maxAge);
+            cookie.setSecure(secure);
+            cookie.setHttpOnly(httpOnly);
+            exchange.setResponseCookie(cookie);
+        }
+
+        @Override
+        public void end() {
+            exchange.endExchange();
+        }
+    }
+
+    public UndertowHttpFacade(HttpServerExchange exchange) {
+        this.exchange = exchange;
+    }
+
+    @Override
+    public Request getRequest() {
+        return requestFacade;
+    }
+
+    @Override
+    public Response getResponse() {
+        return responseFacade;
+    }
+
+    @Override
+    public X509Certificate[] getCertificateChain() {
+        X509Certificate[] chain = new X509Certificate[0];
+        try {
+            chain = exchange.getConnection().getSslSessionInfo().getPeerCertificateChain();
+        } catch (Exception ignore) {
+
+        }
+        return chain;
+    }
+}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowRequestAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowRequestAuthenticator.java
new file mode 100755
index 0000000..14b4f72
--- /dev/null
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowRequestAuthenticator.java
@@ -0,0 +1,63 @@
+package org.keycloak.adapters.undertow;
+
+import io.undertow.security.api.SecurityContext;
+import io.undertow.server.HttpServerExchange;
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OAuthRequestAuthenticator;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UndertowRequestAuthenticator extends RequestAuthenticator {
+    protected SecurityContext securityContext;
+    protected HttpServerExchange exchange;
+
+
+    public UndertowRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort, SecurityContext securityContext, HttpServerExchange exchange) {
+        super(facade, deployment, sslRedirectPort);
+        this.securityContext = securityContext;
+        this.exchange = exchange;
+    }
+
+    protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
+    }
+
+    @Override
+    protected OAuthRequestAuthenticator createOAuthAuthenticator() {
+        return new OAuthRequestAuthenticator(facade, deployment, sslRedirectPort) {
+            @Override
+            protected void saveRequest() {
+                // todo
+            }
+        };
+    }
+
+    @Override
+    protected void completeOAuthAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session) {
+        KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, deployment);
+        securityContext.authenticationComplete(account, "KEYCLOAK", false);
+        login(account);
+    }
+
+    protected void login(KeycloakUndertowAccount account) {
+
+    }
+
+
+    @Override
+    protected void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session) {
+        KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, deployment);
+        securityContext.authenticationComplete(account, "KEYCLOAK", false);
+        propagateKeycloakContext(account);
+    }
+
+    @Override
+    protected boolean isCached() {
+        return false;
+    }
+}