keycloak-uncached

Merge pull request #1088 from patriot1burke/master oidc

3/26/2015 9:43:21 AM

Changes

Details

diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java b/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
old mode 100644
new mode 100755
index 761decc..4d58970
--- a/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
@@ -32,6 +32,8 @@ public class FederatedIdentity {
     private String email;
     private String token;
     private String identityProviderId;
+    private String brokerSessionId;
+    private String brokerUserId;
 
     public FederatedIdentity(String id) {
         if (id == null) {
@@ -102,6 +104,22 @@ public class FederatedIdentity {
         this.identityProviderId = identityProviderId;
     }
 
+    public String getBrokerSessionId() {
+        return brokerSessionId;
+    }
+
+    public void setBrokerSessionId(String brokerSessionId) {
+        this.brokerSessionId = brokerSessionId;
+    }
+
+    public String getBrokerUserId() {
+        return brokerUserId;
+    }
+
+    public void setBrokerUserId(String brokerUserId) {
+        this.brokerUserId = brokerUserId;
+    }
+
     @Override
     public String toString() {
         return "{" +
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
index a6b9030..78c890b 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
@@ -128,16 +128,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
     }
 
     protected FederatedIdentity getFederatedIdentity(Map<String, String> notes, String response) {
-        AccessTokenResponse tokenResponse = null;
-        try {
-            tokenResponse = JsonSerialization.readValue(response, AccessTokenResponse.class);
-        } catch (IOException e) {
-            throw new IdentityBrokerException("Could not decode access token response.", e);
-        }
-        String accessToken = tokenResponse.getToken();
-        notes.put(FEDERATED_ACCESS_TOKEN, accessToken);
-        notes.put(FEDERATED_REFRESH_TOKEN, tokenResponse.getRefreshToken());
-        notes.put(FEDERATED_TOKEN_EXPIRATION, Long.toString(tokenResponse.getExpiresIn()));
+        String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
 
         if (accessToken == null) {
             throw new IdentityBrokerException("No access token from server.");
@@ -146,6 +137,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
         return doGetFederatedIdentity(accessToken);
     }
 
+
     protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
         return null;
     }
@@ -212,12 +204,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
             try {
 
                 if (authorizationCode != null) {
-                    String response = SimpleHttp.doPost(getConfig().getTokenUrl())
-                            .param(OAUTH2_PARAMETER_CODE, authorizationCode)
-                            .param(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
-                            .param(OAUTH2_PARAMETER_CLIENT_SECRET, getConfig().getClientSecret())
-                            .param(OAUTH2_PARAMETER_REDIRECT_URI, uriInfo.getAbsolutePath().toString())
-                            .param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE).asString();
+                    String response = generateTokenRequest(authorizationCode).asString();
 
                     HashMap<String, String> userNotes = new HashMap<String, String>();
                     FederatedIdentity federatedIdentity = getFederatedIdentity(userNotes, response);
@@ -235,5 +222,14 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
             event.error(Errors.IDENTITY_PROVIDER_LOGIN_FAILURE);
             return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
         }
+
+        public SimpleHttp generateTokenRequest(String authorizationCode) {
+            return SimpleHttp.doPost(getConfig().getTokenUrl())
+                    .param(OAUTH2_PARAMETER_CODE, authorizationCode)
+                    .param(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
+                    .param(OAUTH2_PARAMETER_CLIENT_SECRET, getConfig().getClientSecret())
+                    .param(OAUTH2_PARAMETER_REDIRECT_URI, uriInfo.getAbsolutePath().toString())
+                    .param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE);
+        }
     }
 }
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java
new file mode 100755
index 0000000..637546b
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java
@@ -0,0 +1,96 @@
+package org.keycloak.broker.oidc;
+
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.constants.AdapterConstants;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.adapters.action.AdminAction;
+import org.keycloak.representations.adapters.action.LogoutAction;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
+
+    public KeycloakOIDCIdentityProvider(OIDCIdentityProviderConfig config) {
+        super(config);
+    }
+
+    @Override
+    public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
+        return new KeycloakEndpoint(callback, realm, event);
+    }
+
+    protected class KeycloakEndpoint extends OIDCEndpoint {
+        public KeycloakEndpoint(AuthenticationCallback callback, RealmModel realm, EventBuilder event) {
+            super(callback, realm, event);
+        }
+
+        @POST
+        @Path(AdapterConstants.K_LOGOUT)
+        public Response backchannelLogout(String input) {
+            JWSInput token = new JWSInput(input);
+            String signingCert = getConfig().getSigningCertificate();
+            if (signingCert != null && !signingCert.trim().equals("")) {
+                if (!token.verify(getConfig().getSigningCertificate())) {
+                    return Response.status(400).build();            }
+            }
+            LogoutAction action = null;
+            try {
+                action = JsonSerialization.readValue(token.getContent(), LogoutAction.class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+            if (!validateAction(action)) return Response.status(400).build();
+            if (action.getKeycloakSessionIds() != null) {
+                for (String sessionId : action.getKeycloakSessionIds()) {
+                    String brokerSessionId = getConfig().getAlias() + "." + sessionId;
+                    UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
+                    if (userSession != null) {
+                        AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
+                    }
+                }
+
+            }
+            return Response.ok().build();
+        }
+
+        protected boolean validateAction(AdminAction action)  {
+            if (!action.validate()) {
+                logger.warn("admin request failed, not validated" + action.getAction());
+                return false;
+            }
+            if (action.isExpired()) {
+                logger.warn("admin request failed, expired token");
+                return false;
+            }
+            if (!getConfig().getClientId().equals(action.getResource())) {
+                logger.warn("Resource name does not match");
+                return false;
+
+            }
+            return true;
+        }
+
+        @Override
+        public SimpleHttp generateTokenRequest(String authorizationCode) {
+            return super.generateTokenRequest(authorizationCode)
+                    .param(AdapterConstants.APPLICATION_SESSION_STATE, "n/a");  // hack to get backchannel logout to work
+
+        }
+
+
+
+    }
+}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
new file mode 100755
index 0000000..9c46cd8
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
@@ -0,0 +1,68 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.oidc;
+
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public class KeycloakOIDCIdentityProviderFactory extends AbstractIdentityProviderFactory<KeycloakOIDCIdentityProvider> {
+
+    public static final String PROVIDER_ID = "keycloak-oidc";
+
+    @Override
+    public String getName() {
+        return "Keycloak OpenID Connect";
+    }
+
+    @Override
+    public KeycloakOIDCIdentityProvider create(IdentityProviderModel model) {
+        return new KeycloakOIDCIdentityProvider(new OIDCIdentityProviderConfig(model));
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public Map<String, String> parseConfig(InputStream inputStream) {
+        OIDCConfigurationRepresentation rep = null;
+        try {
+            rep = JsonSerialization.readValue(inputStream, OIDCConfigurationRepresentation.class);
+        } catch (IOException e) {
+            throw new RuntimeException("failed to load openid connect metadata", e);
+        }
+        OIDCIdentityProviderConfig config = new OIDCIdentityProviderConfig(new IdentityProviderModel());
+        config.setIssuer(rep.getIssuer());
+        config.setLogoutUrl(rep.getLogoutEndpoint());
+        config.setAuthorizationUrl(rep.getAuthorizationEndpoint());
+        config.setTokenUrl(rep.getTokenEndpoint());
+        config.setUserInfoUrl(rep.getUserinfoEndpoint());
+        return config.getConfig();
+
+    }
+}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index 7bd80db..68b6929 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -18,19 +18,24 @@
 package org.keycloak.broker.oidc;
 
 import org.codehaus.jackson.JsonNode;
+import org.jboss.resteasy.logging.Logger;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.constants.AdapterConstants;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.IDToken;
+import org.keycloak.representations.adapters.action.AdminAction;
+import org.keycloak.representations.adapters.action.LogoutAction;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.EventsManager;
 import org.keycloak.services.messages.Messages;
@@ -39,10 +44,13 @@ import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.util.JsonSerialization;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
@@ -53,6 +61,7 @@ import java.util.Map;
  * @author Pedro Igor
  */
 public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIdentityProviderConfig> {
+    protected static final Logger logger = Logger.getLogger(OIDCIdentityProvider.class);
 
     public static final String OAUTH2_PARAMETER_PROMPT = "prompt";
     public static final String SCOPE_OPENID = "openid";
@@ -78,6 +87,8 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
             super(callback, realm, event);
         }
 
+
+
         @GET
         @Path("logout_response")
         public Response logoutResponse(@Context UriInfo uriInfo,
@@ -99,6 +110,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
             }
             return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
         }
+
     }
 
     @Override
@@ -175,6 +187,11 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
             identity.setName(name);
             identity.setEmail(email);
 
+            identity.setBrokerUserId(getConfig().getAlias() + "." + id);
+            if (tokenResponse.getSessionState() != null) {
+                identity.setBrokerSessionId(getConfig().getAlias() + "." + tokenResponse.getSessionState());
+            }
+
             if (preferredUsername == null) {
                 preferredUsername = email;
             }
@@ -229,10 +246,6 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
         }
     }
 
-    private String decodeJWS(String token) {
-        return new JWSInput(token).readContentAsString();
-    }
-
     @Override
     protected String getDefaultScopes() {
         return "openid";
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
index 0bcdfa3..90707a7 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
@@ -47,5 +47,14 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
     public void setLogoutUrl(String url) {
         getConfig().put("logoutUrl", url);
     }
+    public String getSigningCertificate() {
+        return getConfig().get("signingCertificate");
+    }
+
+    public void setSigningCertificate(String signingCertificate) {
+        getConfig().put("signingCertificate", signingCertificate);
+    }
+
+
 
 }
diff --git a/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory
old mode 100644
new mode 100755
index 50071ed..faa4eb8
--- a/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory
+++ b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory
@@ -1 +1,2 @@
-org.keycloak.broker.oidc.OIDCIdentityProviderFactory
\ No newline at end of file
+org.keycloak.broker.oidc.OIDCIdentityProviderFactory
+org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory
\ No newline at end of file
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index 23299bd..a2a2e71 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -78,6 +78,8 @@ import java.util.Map;
 public class SAMLEndpoint {
     protected static final Logger logger = Logger.getLogger(SAMLEndpoint.class);
     public static final String SAML_FEDERATED_SESSION_INDEX = "SAML_FEDERATED_SESSION_INDEX";
+    public static final String SAML_FEDERATED_SUBJECT = "SAML_FEDERATED_SUBJECT";
+    public static final String SAML_FEDERATED_SUBJECT_NAMEFORMAT = "SAML_FEDERATED_SUBJECT_NAMEFORMAT";
     protected RealmModel realm;
     protected EventBuilder event;
     protected SAMLIdentityProviderConfig config;
@@ -179,7 +181,7 @@ public class SAMLEndpoint {
             SAMLDocumentHolder holder = extractRequestDocument(samlRequest);
             RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
             // validate destination
-            if (!uriInfo.getAbsolutePath().toString().equals(requestAbstractType.getDestination())) {
+            if (!uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
                 event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
                 event.error(Errors.INVALID_SAML_RESPONSE);
                 event.detail(Details.REASON, "invalid_destination");
@@ -210,66 +212,55 @@ public class SAMLEndpoint {
         }
 
         protected Response logoutRequest(LogoutRequestType request, String relayState) {
-            UserModel user = session.users().getUserByUsername(request.getNameID().getValue(), realm);
-            if (user == null) {
-                event.event(EventType.LOGOUT);
-                event.error(Errors.USER_SESSION_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
-            }
-            List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
-            if (sessions == null || sessions.size() == 0) {
-                event.event(EventType.LOGOUT);
-                event.error(Errors.USER_SESSION_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
-            }
-            for (UserSessionModel userSession : sessions) {
-                String brokerId = userSession.getNote(IdentityBrokerService.BROKER_PROVIDER_ID);
-                if (!config.getAlias().equals(brokerId)) continue;
-                boolean logout = false;
-                if (request.getSessionIndex() == null || request.getSessionIndex().size() == 0) {
-                    logout = true;
-                } else {
-                    for (String sessionIndex : request.getSessionIndex()) {
-                        if (sessionIndex.equals(userSession.getNote(SAML_FEDERATED_SESSION_INDEX))) {
-                            logout = true;
-                            break;
-                        }
-                    }
-                }
-                if (logout) {
+            String brokerUserId = config.getAlias() + "." + request.getNameID().getValue();
+            if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {
+                List<UserSessionModel> userSessions = session.sessions().getUserSessionByBrokerUserId(realm, brokerUserId);
+                for (UserSessionModel userSession : userSessions) {
                     try {
                         AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
                     } catch (Exception e) {
-                        logger.error("Failed to logout", e);
+                        logger.warn("failed to do backchannel logout for userSession", e);
                     }
                 }
 
-                String issuerURL = getEntityId(uriInfo, realm);
-                SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
-                builder.logoutRequestID(request.getID());
-                builder.destination(config.getSingleLogoutServiceUrl());
-                builder.issuer(issuerURL);
-                builder.relayState(relayState);
-                if (config.isWantAuthnRequestsSigned()) {
-                    builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
-                           .signDocument();
-                }
-                try {
-                    if (config.isPostBindingResponse()) {
-                        return builder.postBinding().response();
-                    } else {
-                        return builder.redirectBinding().response();
+            }  else {
+                for (String sessionIndex : request.getSessionIndex()) {
+                    String brokerSessionId = brokerUserId + "." + sessionIndex;
+                    UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
+                    if (userSession != null) {
+                        try {
+                            AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
+                        } catch (Exception e) {
+                            logger.warn("failed to do backchannel logout for userSession", e);
+                        }
                     }
-                } catch (ConfigurationException e) {
-                    throw new RuntimeException(e);
-                } catch (ProcessingException e) {
-                    throw new RuntimeException(e);
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
                 }
+            }
 
+            String issuerURL = getEntityId(uriInfo, realm);
+            SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
+            builder.logoutRequestID(request.getID());
+            builder.destination(config.getSingleLogoutServiceUrl());
+            builder.issuer(issuerURL);
+            builder.relayState(relayState);
+            if (config.isWantAuthnRequestsSigned()) {
+                builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
+                        .signDocument();
+            }
+            try {
+                if (config.isPostBindingResponse()) {
+                    return builder.postBinding().response();
+                } else {
+                    return builder.redirectBinding().response();
+                }
+            } catch (ConfigurationException e) {
+                throw new RuntimeException(e);
+            } catch (ProcessingException e) {
+                throw new RuntimeException(e);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
             }
-            throw new RuntimeException("Unreachable");
+
         }
 
         private String getEntityId(UriInfo uriInfo, RealmModel realm) {
@@ -283,8 +274,8 @@ public class SAMLEndpoint {
                 SubjectType.STSubType subType = subject.getSubType();
                 NameIDType subjectNameID = (NameIDType) subType.getBaseID();
                 Map<String, String> notes = new HashMap<>();
-                notes.put("SAML_FEDERATED_SUBJECT", subjectNameID.getValue());
-                if (subjectNameID.getFormat() != null) notes.put("SAML_FEDERATED_SUBJECT_NAMEFORMAT", subjectNameID.getFormat().toString());
+                notes.put(SAML_FEDERATED_SUBJECT, subjectNameID.getValue());
+                if (subjectNameID.getFormat() != null) notes.put(SAML_FEDERATED_SUBJECT_NAMEFORMAT, subjectNameID.getFormat().toString());
                 FederatedIdentity identity = new FederatedIdentity(subjectNameID.getValue());
 
                 identity.setUsername(subjectNameID.getValue());
@@ -304,7 +295,10 @@ public class SAMLEndpoint {
                         break;
                     }
                 }
+                String brokerUserId = config.getAlias() + "." + subjectNameID.getValue();
+                identity.setBrokerUserId(brokerUserId);
                 if (authn != null && authn.getSessionIndex() != null) {
+                    identity.setBrokerSessionId(identity.getBrokerUserId() + "." + authn.getSessionIndex());
                     notes.put(SAML_FEDERATED_SESSION_INDEX, authn.getSessionIndex());
                 }
                 return callback.authenticated(notes, config, identity, relayState);
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index fe381ca..60acd10 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -123,8 +123,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
 
         SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
                 .issuer(getEntityId(uriInfo, realm))
-                .sessionIndex(userSession.getNote("SAML_FEDERATED_SESSION_INDEX"))
-                .userPrincipal(userSession.getNote("SAML_FEDERATED_SUBJECT"), userSession.getNote("SAML_FEDERATED_SUBJECT_NAMEFORMAT"))
+                .sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
+                .userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
                 .destination(getConfig().getSingleLogoutServiceUrl());
         if (getConfig().isWantAuthnRequestsSigned()) {
             logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
diff --git a/core/src/main/java/org/keycloak/jose/jws/Algorithm.java b/core/src/main/java/org/keycloak/jose/jws/Algorithm.java
index 95b062b..4e0f47f 100755
--- a/core/src/main/java/org/keycloak/jose/jws/Algorithm.java
+++ b/core/src/main/java/org/keycloak/jose/jws/Algorithm.java
@@ -1,18 +1,32 @@
 package org.keycloak.jose.jws;
 
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.jose.jws.crypto.SignatureProvider;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public enum Algorithm {
-    none,
-    HS256,
-    HS384,
-    HS512,
-    RS256,
-    RS384,
-    RS512,
-    ES256,
-    ES384,
-    ES512
+
+    none(null),
+    HS256(null),
+    HS384(null),
+    HS512(null),
+    RS256(new RSAProvider()),
+    RS384(new RSAProvider()),
+    RS512(new RSAProvider()),
+    ES256(null),
+    ES384(null),
+    ES512(null)
+    ;
+    private SignatureProvider provider;
+
+    Algorithm(SignatureProvider provider) {
+        this.provider = provider;
+    }
+
+    public SignatureProvider getProvider() {
+        return provider;
+    }
 }
diff --git a/core/src/main/java/org/keycloak/jose/jws/crypto/HMACProvider.java b/core/src/main/java/org/keycloak/jose/jws/crypto/HMACProvider.java
index 4772280..fef959d 100755
--- a/core/src/main/java/org/keycloak/jose/jws/crypto/HMACProvider.java
+++ b/core/src/main/java/org/keycloak/jose/jws/crypto/HMACProvider.java
@@ -14,7 +14,7 @@ import java.security.NoSuchAlgorithmException;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class HMACProvider {
+public class HMACProvider implements SignatureProvider {
     private static String getJavaAlgorithm(Algorithm alg) {
         switch (alg) {
             case HS256:
@@ -82,5 +82,8 @@ public class HMACProvider {
         }
     }
 
-
+    @Override
+    public boolean verify(JWSInput input, String key) {
+        return false;
+    }
 }
diff --git a/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java b/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java
index 42baccf..fc68f6c 100755
--- a/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java
+++ b/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java
@@ -3,16 +3,18 @@ package org.keycloak.jose.jws.crypto;
 
 import org.keycloak.jose.jws.Algorithm;
 import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.util.PemUtils;
 
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.Signature;
+import java.security.cert.X509Certificate;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class RSAProvider {
+public class RSAProvider implements SignatureProvider {
     public static String getJavaAlgorithm(Algorithm alg) {
         switch (alg) {
             case RS256:
@@ -45,6 +47,16 @@ public class RSAProvider {
         }
     }
 
+    public static boolean verifyViaCertificate(JWSInput input, String cert) {
+        X509Certificate certificate = null;
+        try {
+            certificate = PemUtils.decodeCertificate(cert);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return verify(input, certificate.getPublicKey());
+    }
+
     public static boolean verify(JWSInput input, PublicKey publicKey) {
         try {
             Signature verifier = getSignature(input.getHeader().getAlgorithm());
@@ -57,5 +69,10 @@ public class RSAProvider {
 
     }
 
+    @Override
+    public boolean verify(JWSInput input, String key) {
+        return verifyViaCertificate(input, key);
+    }
+
 
 }
diff --git a/core/src/main/java/org/keycloak/jose/jws/crypto/SignatureProvider.java b/core/src/main/java/org/keycloak/jose/jws/crypto/SignatureProvider.java
new file mode 100755
index 0000000..8480b46
--- /dev/null
+++ b/core/src/main/java/org/keycloak/jose/jws/crypto/SignatureProvider.java
@@ -0,0 +1,11 @@
+package org.keycloak.jose.jws.crypto;
+
+import org.keycloak.jose.jws.JWSInput;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface SignatureProvider {
+    boolean verify(JWSInput input, String key);
+}
diff --git a/core/src/main/java/org/keycloak/jose/jws/JWSInput.java b/core/src/main/java/org/keycloak/jose/jws/JWSInput.java
index 6db10fa..d20da6d 100755
--- a/core/src/main/java/org/keycloak/jose/jws/JWSInput.java
+++ b/core/src/main/java/org/keycloak/jose/jws/JWSInput.java
@@ -2,6 +2,7 @@ package org.keycloak.jose.jws;
 
 import org.keycloak.util.Base64Url;
 import org.keycloak.util.JsonSerialization;
+import static org.keycloak.jose.jws.Algorithm.*;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -73,6 +74,13 @@ public class JWSInput {
         return signature;
     }
 
+    public boolean verify(String key) {
+        if (header.getAlgorithm().getProvider() == null) {
+            throw new RuntimeException("signing algorithm not supported");
+        }
+        return header.getAlgorithm().getProvider().verify(this, key);
+    }
+
     public <T> T readJsonContent(Class<T> type) throws IOException {
         return JsonSerialization.readValue(content, type);
     }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java
index a14b3f8..f85020e 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java
@@ -10,14 +10,16 @@ public class LogoutAction extends AdminAction {
     public static final String LOGOUT = "LOGOUT";
     protected List<String> adapterSessionIds;
     protected int notBefore;
+    protected List<String> keycloakSessionIds;
 
     public LogoutAction() {
     }
 
-    public LogoutAction(String id, int expiration, String resource, List<String> adapterSessionIds, int notBefore) {
+    public LogoutAction(String id, int expiration, String resource, List<String> adapterSessionIds, int notBefore, List<String> keycloakSessionIds) {
         super(id, expiration, resource, LOGOUT);
         this.adapterSessionIds = adapterSessionIds;
         this.notBefore = notBefore;
+        this.keycloakSessionIds = keycloakSessionIds;
     }
 
 
@@ -33,6 +35,14 @@ public class LogoutAction extends AdminAction {
         return adapterSessionIds;
     }
 
+    public List<String> getKeycloakSessionIds() {
+        return keycloakSessionIds;
+    }
+
+    public void setKeycloakSessionIds(List<String> keycloakSessionIds) {
+        this.keycloakSessionIds = keycloakSessionIds;
+    }
+
     @Override
     public boolean validate() {
         return LOGOUT.equals(action);
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index e2d089a..15bbb32 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -700,6 +700,9 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
     }
 
     $scope.hidePassword = true;
+    $scope.fromUrl = {
+        data: ''
+    };
 
     if (instance && instance.alias) {
         $scope.identityProvider = angular.copy(instance);
@@ -798,21 +801,22 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
             return;
         }
         var input = {
-            fromUrl: $scope.fromUrl,
+            fromUrl: $scope.fromUrl.data,
             providerId: providerFactory.id
         }
         $http.post(authUrl + '/admin/realms/' + realm.realm + '/identity-provider/import-config', input)
             .success(function(data, status, headers) {
                 setConfig(data);
-                $scope.fromUrl = null;
+                $scope.fromUrl.data = '';
                 $scope.importUrl = false;
                 Notifications.success("Imported config information from url.");
             }).error(function() {
                 Notifications.error("Config can not be imported. Please verify the url.");
             });
     };
-    $scope.$watch('fromUrl', function(newVal, oldVal){
-        if ($scope.fromUrl && $scope.fromUrl.length > 0) {
+    $scope.$watch('fromUrl.data', function(newVal, oldVal){
+        console.log('watch fromUrl: ' + newVal + " " + oldVal);
+        if ($scope.fromUrl.data && $scope.fromUrl.data.length > 0) {
             $scope.importUrl = true;
         } else{
             $scope.importUrl = false;
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-keycloak-oidc.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-keycloak-oidc.html
new file mode 100755
index 0000000..d380749
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-keycloak-oidc.html
@@ -0,0 +1 @@
+<div data-ng-include data-src="resourceUrl + '/partials/realm-identity-provider-oidc.html'"></div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
index be46714..25e7682 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
@@ -129,7 +129,7 @@
                     <div class="form-group" data-ng-show="newIdentityProvider">
                         <label class="col-sm-2 control-label" for="fromUrl">Import From Url</label>
                         <div class="col-sm-4">
-                            <input class="form-control" id="fromUrl" type="text" ng-model="fromUrl">
+                            <input class="form-control" id="fromUrl" type="text" ng-model="fromUrl.data">
                         </div>
                         <span tooltip-placement="right" tooltip="Import metadata from a remote IDP discovery descriptor." class="fa fa-info-circle"></span>
                         <div class="col-sm-4" data-ng-show="importUrl">
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
index 61767b5..d75e780 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
@@ -9,6 +9,15 @@ public interface UserSessionModel {
 
     String getId();
 
+    /**
+     * If created via a broker external login, this is an identifier that can be
+     * used to match external broker backchannel logout requests to a UserSession
+     *
+     * @return
+     */
+    String getBrokerSessionId();
+    String getBrokerUserId();
+
     UserModel getUser();
 
     String getLoginUsername();
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java b/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
index 56d4335..b7421de 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -15,11 +15,15 @@ public interface UserSessionProvider extends Provider {
     ClientSessionModel getClientSession(RealmModel realm, String id);
     ClientSessionModel getClientSession(String id);
 
-    UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe);
+    UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
     UserSessionModel getUserSession(RealmModel realm, String id);
     List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user);
     List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client);
     List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults);
+    List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId);
+    UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId);
+
+    List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue);
 
     int getActiveUserSessions(RealmModel realm, ClientModel client);
     void removeUserSession(RealmModel realm, UserSessionModel session);
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
index 99c23a9..5ab9dc5 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
@@ -12,6 +12,9 @@ public class UserSessionEntity extends SessionEntity {
 
     private String user;
 
+    private String brokerSessionId;
+    private String brokerUserId;
+
     private String loginUsername;
 
     private String ipAddress;
@@ -109,4 +112,20 @@ public class UserSessionEntity extends SessionEntity {
     public void setState(UserSessionModel.State state) {
         this.state = state;
     }
+
+    public String getBrokerSessionId() {
+        return brokerSessionId;
+    }
+
+    public void setBrokerSessionId(String brokerSessionId) {
+        this.brokerSessionId = brokerSessionId;
+    }
+
+    public String getBrokerUserId() {
+        return brokerUserId;
+    }
+
+    public void setBrokerUserId(String brokerUserId) {
+        this.brokerUserId = brokerUserId;
+    }
 }
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 85d7a60..e7ff079 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -70,7 +70,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
-    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
+    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
         String id = KeycloakModelUtils.generateId();
 
         UserSessionEntity entity = new UserSessionEntity();
@@ -81,6 +81,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         entity.setIpAddress(ipAddress);
         entity.setAuthMethod(authMethod);
         entity.setRememberMe(rememberMe);
+        entity.setBrokerSessionId(brokerSessionId);
+        entity.setBrokerUserId(brokerUserId);
 
         int currentTime = Time.currentTime();
 
@@ -125,6 +127,28 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
+    public List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
+        Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
+                .mappedWith(UserSessionMapper.create(realm.getId()).brokerUserId(brokerUserId))
+                .reducedWith(new FirstResultReducer())
+                .execute();
+
+        return wrapUserSessions(realm, sessions.values());
+    }
+
+    @Override
+    public UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
+        Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
+                .mappedWith(UserSessionMapper.create(realm.getId()).brokerSessionId(brokerSessionId))
+                .reducedWith(new FirstResultReducer())
+                .execute();
+
+        List<UserSessionModel> userSessionModels = wrapUserSessions(realm, sessions.values());
+        if (userSessionModels.isEmpty()) return null;
+        return userSessionModels.get(0);
+    }
+
+    @Override
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
         return getUserSessions(realm, client, -1, -1);
     }
@@ -173,7 +197,14 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         return userSessions;
     }
 
-    public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, Map<String, String> notes) {
+    @Override
+    public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
+        HashMap<String, String> notes = new HashMap<>();
+        notes.put(noteName, noteValue);
+        return getUserSessionsByNotes(realm, notes);
+    }
+
+    public List<UserSessionModel> getUserSessionsByNotes(RealmModel realm, Map<String, String> notes) {
         Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
                 .mappedWith(UserSessionNoteMapper.create(realm.getId()).notes(notes))
                 .reducedWith(new FirstResultReducer())
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/mapreduce/UserSessionMapper.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/mapreduce/UserSessionMapper.java
index f781b9e..210b6d5 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/mapreduce/UserSessionMapper.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/mapreduce/UserSessionMapper.java
@@ -30,6 +30,9 @@ public class UserSessionMapper implements Mapper<String, SessionEntity, String, 
 
     private Long expiredRefresh;
 
+    private String brokerSessionId;
+    private String brokerUserId;
+
     public static UserSessionMapper create(String realm) {
         return new UserSessionMapper(realm);
     }
@@ -50,6 +53,16 @@ public class UserSessionMapper implements Mapper<String, SessionEntity, String, 
         return this;
     }
 
+    public UserSessionMapper brokerSessionId(String id) {
+        this.brokerSessionId = id;
+        return this;
+    }
+
+    public UserSessionMapper brokerUserId(String id) {
+        this.brokerUserId = id;
+        return this;
+    }
+
     @Override
     public void map(String key, SessionEntity e, Collector collector) {
         if (!(e instanceof UserSessionEntity)) {
@@ -66,6 +79,9 @@ public class UserSessionMapper implements Mapper<String, SessionEntity, String, 
             return;
         }
 
+        if (brokerSessionId != null && !brokerSessionId.equals(entity.getBrokerSessionId())) return;
+        if (brokerUserId != null && !brokerUserId.equals(entity.getBrokerUserId())) return;
+
         if (expired != null && expiredRefresh != null && entity.getStarted() > expired && entity.getLastSessionRefresh() > expiredRefresh) {
             return;
         }
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
index dfd7914..6cfc121 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
@@ -42,6 +42,15 @@ public class UserSessionAdapter implements UserSessionModel {
         return entity.getId();
     }
 
+    @Override
+    public String getBrokerSessionId() {
+        return entity.getBrokerSessionId();
+    }
+
+    @Override
+    public String getBrokerUserId() {
+        return entity.getBrokerUserId();
+    }
     public UserModel getUser() {
         return session.users().getUserById(entity.getUser(), realm);
     }
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionEntity.java
index e822e75..4a40699 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionEntity.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionEntity.java
@@ -20,6 +20,8 @@ import java.util.Collection;
 @Table(name = "USER_SESSION")
 @NamedQueries({
         @NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId order by s.started, s.id"),
+        @NamedQuery(name = "getUserSessionByBrokerSessionId", query = "select s from UserSessionEntity s where s.realmId = :realmId and s.brokerSessionId = :brokerSessionId order by s.started, s.id"),
+        @NamedQuery(name = "getUserSessionByBrokerUserId", query = "select s from UserSessionEntity s where s.realmId = :realmId and s.brokerUserId = :brokerUserId order by s.started, s.id"),
         @NamedQuery(name = "getUserSessionByClient", query = "select s from UserSessionEntity s join s.clientSessions c where s.realmId = :realmId and c.clientId = :clientId order by s.started, s.id"),
         @NamedQuery(name = "getActiveUserSessionByClient", query = "select count(s) from UserSessionEntity s join s.clientSessions c where s.realmId = :realmId and c.clientId = :clientId"),
         @NamedQuery(name = "removeUserSessionByRealm", query = "delete from UserSessionEntity s where s.realmId = :realmId"),
@@ -35,6 +37,12 @@ public class UserSessionEntity {
     @Column(name="USER_ID")
     protected String userId;
 
+    @Column(name="BROKER_SESSION_ID")
+    protected String brokerSessionId;
+
+    @Column(name="BROKER_USER_ID")
+    protected String brokerUserId;
+
     @Column(name="LOGIN_USERNAME")
     protected String loginUsername;
 
@@ -156,4 +164,20 @@ public class UserSessionEntity {
     public void setNotes(Collection<UserSessionNoteEntity> notes) {
         this.notes = notes;
     }
+
+    public String getBrokerSessionId() {
+        return brokerSessionId;
+    }
+
+    public void setBrokerSessionId(String brokerSessionId) {
+        this.brokerSessionId = brokerSessionId;
+    }
+
+    public String getBrokerUserId() {
+        return brokerUserId;
+    }
+
+    public void setBrokerUserId(String brokerUserId) {
+        this.brokerUserId = brokerUserId;
+    }
 }
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionNoteEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionNoteEntity.java
index 762ce6a..d4bd889 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionNoteEntity.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/UserSessionNoteEntity.java
@@ -17,6 +17,7 @@ import java.io.Serializable;
  * @version $Revision: 1 $
  */
 @NamedQueries({
+        @NamedQuery(name = "selectNoteByNameValue", query="select r from UserSessionNoteEntity r where r.name = :name and r.value = :value"),
         @NamedQuery(name = "removeUserSessionNoteByUser", query="delete from UserSessionNoteEntity r where r.userSession IN (select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId)"),
         @NamedQuery(name = "removeUserSessionNoteByRealm", query="delete from UserSessionNoteEntity r where r.userSession IN (select c from UserSessionEntity c where c.realmId = :realmId)"),
         @NamedQuery(name = "removeUserSessionNoteByExpired", query = "delete from UserSessionNoteEntity r where r.userSession IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime))")
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
index dcae8e2..d70c326 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java
@@ -10,6 +10,7 @@ import org.keycloak.models.UserSessionProvider;
 import org.keycloak.models.UsernameLoginFailureModel;
 import org.keycloak.models.sessions.jpa.entities.ClientSessionEntity;
 import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
+import org.keycloak.models.sessions.jpa.entities.UserSessionNoteEntity;
 import org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.RealmInfoUtil;
@@ -19,6 +20,7 @@ import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -84,7 +86,7 @@ public class JpaUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
-    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
+    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
         UserSessionEntity entity = new UserSessionEntity();
         entity.setId(KeycloakModelUtils.generateId());
         entity.setRealmId(realm.getId());
@@ -93,6 +95,8 @@ public class JpaUserSessionProvider implements UserSessionProvider {
         entity.setIpAddress(ipAddress);
         entity.setAuthMethod(authMethod);
         entity.setRememberMe(rememberMe);
+        entity.setBrokerSessionId(brokerSessionId);
+        entity.setBrokerUserId(brokerUserId);
 
         int currentTime = Time.currentTime();
 
@@ -122,6 +126,44 @@ public class JpaUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
+    public List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
+        List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+        TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionByBrokerUserId", UserSessionEntity.class)
+                .setParameter("realmId", realm.getId())
+                .setParameter("brokerUserId", brokerUserId);
+        for (UserSessionEntity e : query.getResultList()) {
+            sessions.add(new UserSessionAdapter(session, em, realm, e));
+        }
+        return sessions;
+    }
+
+    @Override
+    public UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
+        List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+        TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionByBrokerSessionId", UserSessionEntity.class)
+                .setParameter("realmId", realm.getId())
+                .setParameter("brokerSessionId", brokerSessionId);
+        for (UserSessionEntity e : query.getResultList()) {
+            sessions.add(new UserSessionAdapter(session, em, realm, e));
+        }
+        if (sessions.isEmpty()) return null;
+        return sessions.get(0);
+    }
+
+    @Override
+    public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
+        List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+        TypedQuery<UserSessionNoteEntity> query = em.createNamedQuery("selectNoteByNameValue", UserSessionNoteEntity.class)
+                .setParameter("name", noteName)
+                .setParameter("value", noteValue);
+        for (UserSessionNoteEntity note : query.getResultList()) {
+            if (!note.getUserSession().getRealmId().equals(realm.getId())) continue;
+            sessions.add(new UserSessionAdapter(session, em, realm, note.getUserSession()));
+        }
+        return sessions;
+    }
+
+    @Override
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
         return getUserSessions(realm, client, -1, -1);
     }
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/UserSessionAdapter.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/UserSessionAdapter.java
index ca16427..28d4663 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/UserSessionAdapter.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/UserSessionAdapter.java
@@ -41,6 +41,16 @@ public class UserSessionAdapter implements UserSessionModel {
     }
 
     @Override
+    public String getBrokerSessionId() {
+        return entity.getBrokerSessionId();
+    }
+
+    @Override
+    public String getBrokerUserId() {
+        return entity.getBrokerUserId();
+    }
+
+    @Override
     public UserModel getUser() {
         return session.users().getUserById(entity.getUserId(), realm);
     }
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java
index 16b74db..c7d0e26 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/UserSessionEntity.java
@@ -14,6 +14,8 @@ import java.util.Map;
 public class UserSessionEntity {
 
     private String id;
+    private String brokerSessionId;
+    private String brokerUserId;
     private String realm;
     private String user;
     private String loginUsername;
@@ -126,4 +128,20 @@ public class UserSessionEntity {
     public void setState(UserSessionModel.State state) {
         this.state = state;
     }
+
+    public String getBrokerSessionId() {
+        return brokerSessionId;
+    }
+
+    public void setBrokerSessionId(String brokerSessionId) {
+        this.brokerSessionId = brokerSessionId;
+    }
+
+    public String getBrokerUserId() {
+        return brokerUserId;
+    }
+
+    public void setBrokerUserId(String brokerUserId) {
+        this.brokerUserId = brokerUserId;
+    }
 }
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
index 41081c0..c185a0b 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
@@ -19,10 +19,13 @@ import org.keycloak.util.Time;
 
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -31,14 +34,18 @@ public class MemUserSessionProvider implements UserSessionProvider {
 
     private final KeycloakSession session;
     private final ConcurrentHashMap<String, UserSessionEntity> userSessions;
+    private final ConcurrentHashMap<String, String> userSessionsByBrokerSessionId;
+    private final ConcurrentHashMap<String, Set<String>> userSessionsByBrokerUserId;
     private final ConcurrentHashMap<String, ClientSessionEntity> clientSessions;
     private final ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures;
 
-    public MemUserSessionProvider(KeycloakSession session, ConcurrentHashMap<String, UserSessionEntity> userSessions, ConcurrentHashMap<String, ClientSessionEntity> clientSessions, ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures) {
+    public MemUserSessionProvider(KeycloakSession session, ConcurrentHashMap<String, UserSessionEntity> userSessions, ConcurrentHashMap<String, String> userSessionsByBrokerSessionId, ConcurrentHashMap<String, Set<String>> userSessionsByBrokerUserId, ConcurrentHashMap<String, ClientSessionEntity> clientSessions, ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures) {
         this.session = session;
         this.userSessions = userSessions;
         this.clientSessions = clientSessions;
         this.loginFailures = loginFailures;
+        this.userSessionsByBrokerSessionId = userSessionsByBrokerSessionId;
+        this.userSessionsByBrokerUserId = userSessionsByBrokerUserId;
     }
 
     @Override
@@ -69,7 +76,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
-    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
+    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
         String id = KeycloakModelUtils.generateId();
 
         UserSessionEntity entity = new UserSessionEntity();
@@ -85,13 +92,56 @@ public class MemUserSessionProvider implements UserSessionProvider {
 
         entity.setStarted(currentTime);
         entity.setLastSessionRefresh(currentTime);
+        entity.setBrokerSessionId(brokerSessionId);
+        entity.setBrokerUserId(brokerUserId);
 
         userSessions.put(id, entity);
+        if (brokerSessionId != null) {
+            userSessionsByBrokerSessionId.put(brokerSessionId, id);
+        }
+        if (brokerUserId != null) {
+            while (true) {  // while loop gets around a race condition when a user session is removed
+                Set<String> set = userSessionsByBrokerUserId.get(brokerUserId);
+                if (set == null) {
+                    Set<String> value = new HashSet<>();
+                    set = userSessionsByBrokerUserId.putIfAbsent(brokerUserId, value);
+                    if (set == null) {
+                        set = value;
+                    }
+                }
+                synchronized (set) {
+                    set.add(id);
+                }
+                if (userSessionsByBrokerUserId.get(brokerUserId) == set) {
+                    // we are ensured set isn't deleted before the new id is added
+                    break;
+                }
+            }
+        }
 
         return new UserSessionAdapter(session, this, realm, entity);
     }
 
     @Override
+    public List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
+        Set<String> sessions = userSessionsByBrokerUserId.get(brokerUserId);
+        if (sessions == null) return Collections.emptyList();
+        List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
+        for (String id : sessions) {
+            UserSessionModel userSession = getUserSession(realm, id);
+            if (userSession != null) userSessions.add(userSession);
+        }
+        return userSessions;
+    }
+
+    @Override
+    public UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
+        String id = userSessionsByBrokerSessionId.get(brokerSessionId);
+        if (id == null) return null;
+        return getUserSession(realm, id);
+    }
+
+    @Override
     public UserSessionModel getUserSession(RealmModel realm, String id) {
         UserSessionEntity entity = getUserSessionEntity(realm, id);
         return entity != null ? new UserSessionAdapter(session, this, realm, entity) : null;
@@ -117,6 +167,17 @@ public class MemUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
+    public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
+        List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
+        for (UserSessionEntity s : this.userSessions.values()) {
+            if (s.getRealm().equals(realm.getId()) && noteValue.equals(s.getNotes().get(noteName))) {
+                userSessions.add(new UserSessionAdapter(session, this, realm, s));
+            }
+        }
+        return userSessions;
+    }
+
+    @Override
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
         List<UserSessionEntity> userSessionEntities = new LinkedList<UserSessionEntity>();
         for (ClientSessionEntity s : clientSessions.values()) {
@@ -158,9 +219,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
         UserSessionEntity entity = getUserSessionEntity(realm, session.getId());
         if (entity != null) {
             userSessions.remove(entity.getId());
-            for (ClientSessionEntity clientSession : entity.getClientSessions()) {
-                clientSessions.remove(clientSession.getId());
-            }
+            remove(entity);
         }
     }
 
@@ -171,12 +230,29 @@ public class MemUserSessionProvider implements UserSessionProvider {
             UserSessionEntity s = itr.next();
             if (s.getRealm().equals(realm.getId()) && s.getUser().equals(user.getId())) {
                 itr.remove();
+                remove(s);
+            }
+        }
+    }
 
-                for (ClientSessionEntity clientSession : s.getClientSessions()) {
-                    clientSessions.remove(clientSession.getId());
+    protected void remove(UserSessionEntity s) {
+        if (s.getBrokerSessionId() != null) {
+            userSessionsByBrokerSessionId.remove(s.getBrokerSessionId());
+        }
+        if (s.getBrokerUserId() != null) {
+            Set<String> set = userSessionsByBrokerUserId.get(s.getBrokerUserId());
+            if (set != null) {
+                synchronized (set) {
+                    set.remove(s.getId());
+                    // this is a race condition :(
+                    // Since it will be very rare for a user to have concurrent sessions, I'm hoping we never hit this
+                    if (set.isEmpty()) userSessionsByBrokerUserId.remove(s.getBrokerUserId());
                 }
             }
         }
+        for (ClientSessionEntity clientSession : s.getClientSessions()) {
+           clientSessions.remove(clientSession.getId());
+       }
     }
 
     @Override
@@ -187,9 +263,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
             if (s.getRealm().equals(realm.getId()) && (s.getLastSessionRefresh() < Time.currentTime() - realm.getSsoSessionIdleTimeout() || s.getStarted() < Time.currentTime() - realm.getSsoSessionMaxLifespan())) {
                 itr.remove();
 
-                for (ClientSessionEntity clientSession : s.getClientSessions()) {
-                    clientSessions.remove(clientSession.getId());
-                }
+                remove(s);
             }
         }
         int expired = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
@@ -210,9 +284,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
             if (s.getRealm().equals(realm.getId())) {
                 itr.remove();
 
-                for (ClientSessionEntity clientSession : s.getClientSessions()) {
-                    clientSessions.remove(clientSession.getId());
-                }
+                remove(s);
             }
         }
         Iterator<ClientSessionEntity> citr = clientSessions.values().iterator();
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java
index a943eca..73b7ebc 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProviderFactory.java
@@ -10,6 +10,7 @@ import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
 import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureEntity;
 import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureKey;
 
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -24,10 +25,12 @@ public class MemUserSessionProviderFactory implements UserSessionProviderFactory
     private ConcurrentHashMap<String, ClientSessionEntity> clientSessions = new ConcurrentHashMap<String, ClientSessionEntity>();
 
     private ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures = new ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity>();
+    private final ConcurrentHashMap<String, String> userSessionsByBrokerSessionId = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<String, Set<String>> userSessionsByBrokerUserId = new ConcurrentHashMap<>();
 
     @Override
     public UserSessionProvider create(KeycloakSession session) {
-        return new MemUserSessionProvider(session, userSessions, clientSessions, loginFailures);
+        return new MemUserSessionProvider(session, userSessions, userSessionsByBrokerSessionId, userSessionsByBrokerUserId, clientSessions, loginFailures);
     }
 
     @Override
@@ -43,6 +46,8 @@ public class MemUserSessionProviderFactory implements UserSessionProviderFactory
     public void close() {
         userSessions.clear();
         loginFailures.clear();
+        userSessionsByBrokerSessionId.clear();
+        userSessionsByBrokerUserId.clear();
     }
 
     @Override
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java
index 5b215a0..86b0187 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/UserSessionAdapter.java
@@ -38,6 +38,16 @@ public class UserSessionAdapter implements UserSessionModel {
         return entity.getId();
     }
 
+    @Override
+    public String getBrokerSessionId() {
+        return entity.getBrokerSessionId();
+    }
+
+    @Override
+    public String getBrokerUserId() {
+        return entity.getBrokerUserId();
+    }
+
     public void setId(String id) {
         entity.setId(id);
     }
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java
index ccf0f49..5b7e8d3 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java
@@ -21,6 +21,9 @@ public class MongoUserSessionEntity extends AbstractIdentifiableEntity implement
 
     private String realmId;
 
+    private String brokerSessionId;
+    private String brokerUserId;
+
     private String user;
 
     private String loginUsername;
@@ -136,4 +139,20 @@ public class MongoUserSessionEntity extends AbstractIdentifiableEntity implement
     public void setState(UserSessionModel.State state) {
         this.state = state;
     }
+
+    public String getBrokerSessionId() {
+        return brokerSessionId;
+    }
+
+    public void setBrokerSessionId(String brokerSessionId) {
+        this.brokerSessionId = brokerSessionId;
+    }
+
+    public String getBrokerUserId() {
+        return brokerUserId;
+    }
+
+    public void setBrokerUserId(String brokerUserId) {
+        this.brokerUserId = brokerUserId;
+    }
 }
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
index 6630855..0bc5849 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java
@@ -74,7 +74,7 @@ public class MongoUserSessionProvider implements UserSessionProvider {
 
 
     @Override
-    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
+    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
         MongoUserSessionEntity entity = new MongoUserSessionEntity();
         entity.setRealmId(realm.getId());
         entity.setUser(user.getId());
@@ -83,6 +83,8 @@ public class MongoUserSessionProvider implements UserSessionProvider {
         entity.setAuthMethod(authMethod);
         entity.setRememberMe(rememberMe);
         entity.setRealmId(realm.getId());
+        entity.setBrokerSessionId(brokerSessionId);
+        entity.setBrokerUserId(brokerUserId);
 
         int currentTime = Time.currentTime();
 
@@ -122,6 +124,39 @@ public class MongoUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
+    public List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
+        DBObject query = new BasicDBObject("brokerUserId", brokerUserId);
+        List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+        for (MongoUserSessionEntity e : mongoStore.loadEntities(MongoUserSessionEntity.class, query, invocationContext)) {
+            sessions.add(new UserSessionAdapter(session, this, e, realm, invocationContext));
+        }
+        return sessions;
+    }
+
+    @Override
+    public UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
+        DBObject query = new BasicDBObject("brokerSessionId", brokerSessionId);
+        List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+        for (MongoUserSessionEntity e : mongoStore.loadEntities(MongoUserSessionEntity.class, query, invocationContext)) {
+            sessions.add(new UserSessionAdapter(session, this, e, realm, invocationContext));
+        }
+        if (sessions.isEmpty()) return null;
+        return sessions.get(0);
+    }
+
+    @Override
+    public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
+        DBObject query = new QueryBuilder()
+                          .and("realmId").is(realm.getId())
+                          .and("notes." + noteName).is(noteValue).get();
+        List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+        for (MongoUserSessionEntity e : mongoStore.loadEntities(MongoUserSessionEntity.class, query, invocationContext)) {
+            sessions.add(new UserSessionAdapter(session, this, e, realm, invocationContext));
+        }
+        return sessions;
+    }
+
+    @Override
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
         return getUserSessions(realm, client, -1, -1);
     }
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java
index c12f377..644bbdb 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java
@@ -42,6 +42,16 @@ public class UserSessionAdapter extends AbstractMongoAdapter<MongoUserSessionEnt
     }
 
     @Override
+    public String getBrokerSessionId() {
+        return entity.getBrokerSessionId();
+    }
+
+    @Override
+    public String getBrokerUserId() {
+        return entity.getBrokerUserId();
+    }
+
+    @Override
     public UserModel getUser() {
         return keycloakSession.users().getUserById(entity.getUser(), realm);
     }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 5072e1c..b2772d0 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -437,14 +437,15 @@ public class SamlProtocol implements LoginProtocol {
     public Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
         if (!(client instanceof ApplicationModel)) return null;
-        SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(clientSession, client);
         try {
             if (isLogoutPostBindingForClient(clientSession)) {
                 String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
+                SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
                 return logoutBuilder.postBinding().request(bindingUri);
             } else {
                 logger.debug("frontchannel redirect binding");
                 String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
+                SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
                 return logoutBuilder.redirectBinding().request(bindingUri);
             }
         } catch (ConfigurationException e) {
@@ -504,7 +505,7 @@ public class SamlProtocol implements LoginProtocol {
             logger.warnv("Can't do backchannel logout. No SingleLogoutService POST Binding registered for client: {1}", client.getClientId());
             return;
         }
-        SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(clientSession, client);
+        SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(logoutUrl, clientSession, client);
 
 
         String logoutRequestString = null;
@@ -549,12 +550,12 @@ public class SamlProtocol implements LoginProtocol {
 
     }
 
-    protected SAML2LogoutRequestBuilder createLogoutRequest(ClientSessionModel clientSession, ClientModel client) {
+    protected SAML2LogoutRequestBuilder createLogoutRequest(String logoutUrl, ClientSessionModel clientSession, ClientModel client) {
         // build userPrincipal with subject used at login
         SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
                                          .issuer(getResponseIssuer(realm))
                                          .userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
-                                         .destination(client.getClientId());
+                                         .destination(logoutUrl);
         if (requiresRealmSignature(client)) {
             logoutBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
                          .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
old mode 100644
new mode 100755
index d8f9db2..d796f8f
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -314,7 +314,7 @@ public class TokenEndpoint {
 
         UserSessionProvider sessions = session.sessions();
 
-        UserSessionModel userSession = sessions.createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "oauth_credentials", false);
+        UserSessionModel userSession = sessions.createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "oauth_credentials", false, null, null);
         event.session(userSession);
 
         ClientSessionModel clientSession = sessions.createClientSession(realm, client);
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 625845f..bdc583a 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -90,8 +90,8 @@ public class AuthenticationManager {
         userSession.setState(UserSessionModel.State.LOGGING_OUT);
 
         logger.debugv("Logging out: {0} ({1})", user.getUsername(), userSession.getId());
-        expireIdentityCookie(realm, uriInfo, connection);
-        expireRememberMeCookie(realm, uriInfo, connection);
+        //expireIdentityCookie(realm, uriInfo, connection);
+        //expireRememberMeCookie(realm, uriInfo, connection);
 
         for (ClientSessionModel clientSession : userSession.getClientSessions()) {
             ClientModel client = clientSession.getClient();
diff --git a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
old mode 100644
new mode 100755
index 702b684..1cca55c
--- a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
@@ -116,7 +116,7 @@ public class HttpAuthenticationManager {
             event.error(Errors.USER_DISABLED);
             response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.ACCOUNT_DISABLED);
         } else {
-            UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false);
+            UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false, null, null);
 
             // Propagate state (like kerberos delegation credentials etc) as attributes of userSession
             for (Map.Entry<String, String> entry : authState.entrySet()) {
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index 25a3414..1bd7fdc 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -131,23 +131,6 @@ public class ResourceAdminManager {
         }
     }
 
-    public void logoutSession(URI requestUri, RealmModel realm, UserSessionModel session) {
-        ApacheHttpClient4Executor executor = createExecutor();
-
-        try {
-            // Map from "app" to clientSessions for this app
-            MultivaluedHashMap<ApplicationModel, ClientSessionModel> clientSessions = new MultivaluedHashMap<ApplicationModel, ClientSessionModel>();
-            putClientSessions(clientSessions, session);
-
-            logger.debugv("logging out {0} resources ", clientSessions.size());
-            for (Map.Entry<ApplicationModel, List<ClientSessionModel>> entry : clientSessions.entrySet()) {
-                logoutClientSessions(requestUri, realm, entry.getKey(), entry.getValue(), executor);
-            }
-        } finally {
-            executor.getHttpClient().getConnectionManager().shutdown();
-        }
-    }
-
     public void logoutUserFromApplication(URI requestUri, RealmModel realm, ApplicationModel resource, UserModel user, KeycloakSession session) {
         ApacheHttpClient4Executor executor = createExecutor();
 
@@ -179,6 +162,7 @@ public class ResourceAdminManager {
 
             // Key is host, value is list of http sessions for this host
             MultivaluedHashMap<String, String> adapterSessionIds = null;
+            List<String> userSessions = new LinkedList<>();
             if (clientSessions != null && clientSessions.size() > 0) {
                 adapterSessionIds = new MultivaluedHashMap<String, String>();
                 for (ClientSessionModel clientSession : clientSessions) {
@@ -187,6 +171,7 @@ public class ResourceAdminManager {
                         String host = clientSession.getNote(AdapterConstants.APPLICATION_SESSION_HOST);
                         adapterSessionIds.add(host, adapterSessionId);
                     }
+                    if (clientSession.getUserSession() != null) userSessions.add(clientSession.getUserSession().getId());
                 }
             }
 
@@ -202,7 +187,7 @@ public class ResourceAdminManager {
                     String host = entry.getKey();
                     List<String> sessionIds = entry.getValue();
                     String currentHostMgmtUrl = managementUrl.replace(APPLICATION_SESSION_HOST_PROPERTY, host);
-                    allPassed = sendLogoutRequest(realm, resource, sessionIds, client, 0, currentHostMgmtUrl) && allPassed;
+                    allPassed = sendLogoutRequest(realm, resource, sessionIds, userSessions, client, 0, currentHostMgmtUrl) && allPassed;
                 }
 
                 return allPassed;
@@ -213,7 +198,7 @@ public class ResourceAdminManager {
                     allSessionIds.addAll(currentIds);
                 }
 
-                return sendLogoutRequest(realm, resource, allSessionIds, client, 0, managementUrl);
+                return sendLogoutRequest(realm, resource, allSessionIds, userSessions, client, 0, managementUrl);
             }
         } else {
             logger.debugv("Can't logout {0}: no management url", resource.getName());
@@ -265,7 +250,7 @@ public class ResourceAdminManager {
         // Propagate this to all hosts
         GlobalRequestResult result = new GlobalRequestResult();
         for (String mgmtUrl : mgmtUrls) {
-            if (sendLogoutRequest(realm, resource, null, executor, notBefore, mgmtUrl)) {
+            if (sendLogoutRequest(realm, resource, null, null, executor, notBefore, mgmtUrl)) {
                 result.addSuccessRequest(mgmtUrl);
             } else {
                 result.addFailedRequest(mgmtUrl);
@@ -274,8 +259,8 @@ public class ResourceAdminManager {
         return result;
     }
 
-    protected boolean sendLogoutRequest(RealmModel realm, ApplicationModel resource, List<String> adapterSessionIds, ApacheHttpClient4Executor client, int notBefore, String managementUrl) {
-        LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getName(), adapterSessionIds, notBefore);
+    protected boolean sendLogoutRequest(RealmModel realm, ApplicationModel resource, List<String> adapterSessionIds, List<String> userSessions, ApacheHttpClient4Executor client, int notBefore, String managementUrl) {
+        LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getName(), adapterSessionIds, notBefore, userSessions);
         String token = new TokenManager().encodeToken(realm, adminAction);
         if (logger.isDebugEnabled()) logger.debugv("logout resource {0} url: {1} sessionIds: " + adapterSessionIds, resource.getName(), managementUrl);
         ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_LOGOUT).build().toString());
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 7f155e0..f694661 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -615,8 +615,7 @@ public class AccountService {
         List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
         for (UserSessionModel s : sessions) {
             if (!s.getId().equals(auth.getSession().getId())) {
-                new ResourceAdminManager().logoutSession(uriInfo.getRequestUri(), realm, s);
-                session.sessions().removeUserSession(realm, s);
+                AuthenticationManager.backchannelLogout(session, realm, s, uriInfo, clientConnection, headers);
             }
         }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 0befaa4..e62b4ba 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -15,6 +15,7 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -23,6 +24,7 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.ClientConnection;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventQuery;
 import org.keycloak.events.EventStoreProvider;
@@ -42,6 +44,7 @@ import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.adapters.action.GlobalRequestResult;
 import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.LDAPConnectionTestManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
@@ -67,6 +70,12 @@ public class RealmAdminResource {
     @Context
     protected UriInfo uriInfo;
 
+    @Context
+    protected ClientConnection connection;
+
+    @Context
+    protected HttpHeaders headers;
+
     public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager) {
         this.auth = auth;
         this.realm = realm;
@@ -297,8 +306,7 @@ public class RealmAdminResource {
     public void deleteSession(@PathParam("session") String sessionId) {
         UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
         if (userSession == null) throw new NotFoundException("Sesssion not found");
-        session.sessions().removeUserSession(realm, userSession);
-        new ResourceAdminManager().logoutSession(uriInfo.getRequestUri(), realm, userSession);
+        AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers);
     }
 
     /**
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index f271a2e..e860ea8 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -34,6 +34,7 @@ import org.keycloak.representations.idm.MappingsRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.UserSessionRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
@@ -51,6 +52,7 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -87,6 +89,9 @@ public class UsersResource {
     @Context
     protected KeycloakSession session;
 
+    @Context
+    protected HttpHeaders headers;
+
     public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager) {
         this.auth = auth;
         this.realm = realm;
@@ -321,8 +326,10 @@ public class UsersResource {
         if (user == null) {
             throw new NotFoundException("User not found");
         }
-        new ResourceAdminManager().logoutUser(uriInfo.getRequestUri(), realm, user, session);
-        session.sessions().removeUserSessions(realm, user);
+        List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
+        for (UserSessionModel userSession : userSessions) {
+            AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
+        }
     }
 
     /**
@@ -728,7 +735,7 @@ public class UsersResource {
         }
 
 
-        UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false);
+        UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false, null, null);
         //audit.session(userSession);
         ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
         clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 11a49c4..7a4bea5 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -285,7 +285,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         updateFederatedIdentity(federatedIdentity, federatedUser);
 
         UserSessionModel userSession = this.session.sessions()
-                .createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false);
+                .createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false, federatedIdentity.getBrokerSessionId(), federatedIdentity.getBrokerUserId());
 
         this.event.user(federatedUser);
         this.event.session(userSession);
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index f9ee68b..5b40535 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -344,7 +344,7 @@ public class LoginActionsService {
         switch (status) {
             case SUCCESS:
             case ACTIONS_REQUIRED:
-                UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", remember);
+                UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", remember, null, null);
                 TokenManager.attachClientSession(userSession, clientSession);
                 event.session(userSession);
                 return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
@@ -885,7 +885,7 @@ public class LoginActionsService {
         } else{
             event.user(user);
 
-            UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false);
+            UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false, null, null);
             event.session(userSession);
             TokenManager.attachClientSession(userSession, clientSession);
 
diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
index 8ebea50..de54cc5 100755
--- a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
+++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
@@ -1,73 +1,73 @@
-package org.keycloak.social.facebook;
-
-import org.codehaus.jackson.JsonNode;
-import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
-import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
-import org.keycloak.broker.oidc.util.SimpleHttp;
-import org.keycloak.broker.provider.FederatedIdentity;
-import org.keycloak.broker.provider.IdentityBrokerException;
-import org.keycloak.social.SocialIdentityProvider;
-
-/**
- * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
- */
-public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
-
-    public static final String AUTH_URL = "https://graph.facebook.com/oauth/authorize";
-    public static final String TOKEN_URL = "https://graph.facebook.com/oauth/access_token";
-    public static final String PROFILE_URL = "https://graph.facebook.com/me";
-    public static final String DEFAULT_SCOPE = "email";
-
-    public FacebookIdentityProvider(OAuth2IdentityProviderConfig config) {
-        super(config);
-        config.setAuthorizationUrl(AUTH_URL);
-        config.setTokenUrl(TOKEN_URL);
-        config.setUserInfoUrl(PROFILE_URL);
-    }
-
-    protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
-        try {
-            JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
-
-            String id = getJsonProperty(profile, "id");
-
-            FederatedIdentity user = new FederatedIdentity(id);
-
-            String email = getJsonProperty(profile, "email");
-
-            user.setEmail(email);
-
-            String username = getJsonProperty(profile, "username");
-
-            if (username == null) {
-                if (email != null) {
-                    username = email;
-                } else {
-                    username = id;
-                }
-            }
-
-            user.setUsername(username);
-
-            String firstName = getJsonProperty(profile, "first_name");
-            String lastName = getJsonProperty(profile, "last_name");
-
-            if (lastName == null) {
-                lastName = "";
-            } else {
-                lastName = " " + lastName;
-            }
-
-            user.setName(firstName + lastName);
-
-            return user;
-        } catch (Exception e) {
-            throw new IdentityBrokerException("Could not obtain user profile from facebook.", e);
-        }
-    }
-
-    @Override
-    protected String getDefaultScopes() {
-        return DEFAULT_SCOPE;
-    }
-}
+package org.keycloak.social.facebook;
+
+import org.codehaus.jackson.JsonNode;
+import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
+import org.keycloak.social.SocialIdentityProvider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
+
+    public static final String AUTH_URL = "https://graph.facebook.com/oauth/authorize";
+    public static final String TOKEN_URL = "https://graph.facebook.com/oauth/access_token";
+    public static final String PROFILE_URL = "https://graph.facebook.com/me";
+    public static final String DEFAULT_SCOPE = "email";
+
+    public FacebookIdentityProvider(OAuth2IdentityProviderConfig config) {
+        super(config);
+        config.setAuthorizationUrl(AUTH_URL);
+        config.setTokenUrl(TOKEN_URL);
+        config.setUserInfoUrl(PROFILE_URL);
+    }
+
+    protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+        try {
+            JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
+
+            String id = getJsonProperty(profile, "id");
+
+            FederatedIdentity user = new FederatedIdentity(id);
+
+            String email = getJsonProperty(profile, "email");
+
+            user.setEmail(email);
+
+            String username = getJsonProperty(profile, "username");
+
+            if (username == null) {
+                if (email != null) {
+                    username = email;
+                } else {
+                    username = id;
+                }
+            }
+
+            user.setUsername(username);
+
+            String firstName = getJsonProperty(profile, "first_name");
+            String lastName = getJsonProperty(profile, "last_name");
+
+            if (lastName == null) {
+                lastName = "";
+            } else {
+                lastName = " " + lastName;
+            }
+
+            user.setName(firstName + lastName);
+
+            return user;
+        } catch (Exception e) {
+            throw new IdentityBrokerException("Could not obtain user profile from facebook.", e);
+        }
+    }
+
+    @Override
+    protected String getDefaultScopes() {
+        return DEFAULT_SCOPE;
+    }
+}
diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
index df6a4d0..d3ff667 100755
--- a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
+++ b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
@@ -1,49 +1,49 @@
-package org.keycloak.social.github;
-
-import org.codehaus.jackson.JsonNode;
-import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
-import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
-import org.keycloak.broker.oidc.util.SimpleHttp;
-import org.keycloak.broker.provider.FederatedIdentity;
-import org.keycloak.broker.provider.IdentityBrokerException;
-import org.keycloak.social.SocialIdentityProvider;
-
-/**
- * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
- */
-public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
-
-    public static final String AUTH_URL = "https://github.com/login/oauth/authorize";
-    public static final String TOKEN_URL = "https://github.com/login/oauth/access_token";
-    public static final String PROFILE_URL = "https://api.github.com/user";
-    public static final String DEFAULT_SCOPE = "user:email";
-
-    public GitHubIdentityProvider(OAuth2IdentityProviderConfig config) {
-        super(config);
-        config.setAuthorizationUrl(AUTH_URL);
-        config.setTokenUrl(TOKEN_URL);
-        config.setUserInfoUrl(PROFILE_URL);
-    }
-
-    @Override
-    protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
-        try {
-            JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
-
-            FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "id"));
-
-            user.setUsername(getJsonProperty(profile, "login"));
-            user.setName(getJsonProperty(profile, "name"));
-            user.setEmail(getJsonProperty(profile, "email"));
-
-            return user;
-        } catch (Exception e) {
-            throw new IdentityBrokerException("Could not obtain user profile from github.", e);
-        }
-    }
-
-    @Override
-    protected String getDefaultScopes() {
-        return DEFAULT_SCOPE;
-    }
-}
+package org.keycloak.social.github;
+
+import org.codehaus.jackson.JsonNode;
+import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
+import org.keycloak.social.SocialIdentityProvider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
+
+    public static final String AUTH_URL = "https://github.com/login/oauth/authorize";
+    public static final String TOKEN_URL = "https://github.com/login/oauth/access_token";
+    public static final String PROFILE_URL = "https://api.github.com/user";
+    public static final String DEFAULT_SCOPE = "user:email";
+
+    public GitHubIdentityProvider(OAuth2IdentityProviderConfig config) {
+        super(config);
+        config.setAuthorizationUrl(AUTH_URL);
+        config.setTokenUrl(TOKEN_URL);
+        config.setUserInfoUrl(PROFILE_URL);
+    }
+
+    @Override
+    protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+        try {
+            JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
+
+            FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "id"));
+
+            user.setUsername(getJsonProperty(profile, "login"));
+            user.setName(getJsonProperty(profile, "name"));
+            user.setEmail(getJsonProperty(profile, "email"));
+
+            return user;
+        } catch (Exception e) {
+            throw new IdentityBrokerException("Could not obtain user profile from github.", e);
+        }
+    }
+
+    @Override
+    protected String getDefaultScopes() {
+        return DEFAULT_SCOPE;
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 00b071e..c8371bd 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -157,7 +157,7 @@ public class AccountTest {
         });
     }
 
-    @Test @Ignore
+    //@Test @Ignore
     public void runit() throws Exception {
         Thread.sleep(10000000);
     }
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 cfc9534..b7d39fc 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
@@ -39,6 +39,7 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.resources.admin.AdminRoot;
@@ -62,6 +63,7 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import java.net.URI;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -138,7 +140,7 @@ public class AdapterTestStrategy extends ExternalResource {
             ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
             TokenManager tm = new TokenManager();
             UserModel admin = session.users().getUserByUsername("admin", adminRealm);
-            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false);
+            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
             AccessToken token = tm.createClientAccessToken(session, TokenManager.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, null);
             return tm.encodeToken(adminRealm, token);
         } finally {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
index 01a5861..56f8b8f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
@@ -86,7 +86,7 @@ public class RelativeUriAdapterTest {
             ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
             TokenManager tm = new TokenManager();
             UserModel admin = session.users().getUserByUsername("admin", adminRealm);
-            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "user", null, "form", false);
+            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "user", null, "form", false, null, null);
             AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, null);
             adminToken = tm.encodeToken(adminRealm, token);
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
index 302dd8d..873bbff 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -78,7 +78,7 @@ public class AdminAPITest {
             ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
             TokenManager tm = new TokenManager();
             UserModel admin = session.users().getUserByUsername("admin", adminRealm);
-            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false);
+            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
             AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, null);
             return tm.encodeToken(adminRealm, token);
         } finally {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 2d173ae..35cf6fc 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -251,11 +251,11 @@ public class UserSessionProviderTest {
             Set<String> expiredClientSessions = new HashSet<String>();
 
             Time.setOffset(-(realm.getSsoSessionMaxLifespan() + 1));
-            expired.add(session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true).getId());
+            expired.add(session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null).getId());
             expiredClientSessions.add(session.sessions().createClientSession(realm, client).getId());
 
             Time.setOffset(0);
-            UserSessionModel s = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.1", "form", true);
+            UserSessionModel s = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.1", "form", true, null, null);
             //s.setLastSessionRefresh(Time.currentTime() - (realm.getSsoSessionIdleTimeout() + 1));
             s.setLastSessionRefresh(0);
             expired.add(s.getId());
@@ -267,7 +267,7 @@ public class UserSessionProviderTest {
             Set<String> valid = new HashSet<String>();
             Set<String> validClientSessions = new HashSet<String>();
 
-            valid.add(session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true).getId());
+            valid.add(session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null).getId());
             validClientSessions.add(session.sessions().createClientSession(realm, client).getId());
 
             resetSession();
@@ -376,7 +376,7 @@ public class UserSessionProviderTest {
         try {
             for (int i = 0; i < 25; i++) {
                 Time.setOffset(i);
-                UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false);
+                UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false, null, null);
                 ClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.findClient("test-app"));
                 clientSession.setUserSession(userSession);
                 clientSession.setRedirectUri("http://redirect");
@@ -481,7 +481,7 @@ public class UserSessionProviderTest {
 
     private UserSessionModel[] createSessions() {
         UserSessionModel[] sessions = new UserSessionModel[3];
-        sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true);
+        sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
 
         Set<String> roles = new HashSet<String>();
         roles.add("one");
@@ -490,10 +490,10 @@ public class UserSessionProviderTest {
         createClientSession(realm.findClient("test-app"), sessions[0], "http://redirect", "state", roles);
         createClientSession(realm.findClient("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>());
 
-        sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true);
+        sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
         createClientSession(realm.findClient("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>());
 
-        sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true);
+        sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
         createClientSession(realm.findClient("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>());
 
         resetSession();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 34342e0..7120b36 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -454,6 +454,17 @@ public class AccessTokenTest {
             Assert.assertEquals(401, response.getStatus());
             response.close();
         }
+        {   // test no password
+            String header = BasicAuthHelper.createHeader("test-app", "password");
+            Form form = new Form();
+            form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+            form.param("username", "test-user@localhost");
+            Response response = grantTarget.request()
+                    .header(HttpHeaders.AUTHORIZATION, header)
+                    .post(Entity.form(form));
+            Assert.assertEquals(401, response.getStatus());
+            response.close();
+        }
 
         {   // test bearer-only
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
index b8e9e1c..91970e6 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
@@ -420,7 +420,7 @@ public class SamlBindingTest {
             ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
             TokenManager tm = new TokenManager();
             UserModel admin = session.users().getUserByUsername("admin", adminRealm);
-            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false);
+            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
             AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, null);
             return tm.encodeToken(adminRealm, token);
         } finally {