keycloak-aplcache

Changes

services/pom.xml 5(+5 -0)

Details

diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index ec843ec..a506953 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -21,6 +21,7 @@
         <!-- JpaUserSessionProvider -->
         <class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>
         <class>org.keycloak.models.sessions.jpa.entities.ClientSessionRoleEntity</class>
+        <class>org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity</class>
         <class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
         <class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
 
diff --git a/events/api/src/main/java/org/keycloak/events/Errors.java b/events/api/src/main/java/org/keycloak/events/Errors.java
index a6932ec..e7d7e1a 100755
--- a/events/api/src/main/java/org/keycloak/events/Errors.java
+++ b/events/api/src/main/java/org/keycloak/events/Errors.java
@@ -35,6 +35,8 @@ public interface Errors {
 
     String SOCIAL_PROVIDER_NOT_FOUND = "social_provider_not_found";
     String SOCIAL_ID_IN_USE = "social_id_in_use";
+    String STATE_PARAM_NOT_FOUND = "state_param_not_found";
+    String SSL_REQUIRED = "ssl_required";
 
     String USER_NOT_LOGGED_IN = "user_not_logged_in";
     String USER_SESSION_NOT_FOUND = "user_session_not_found";
diff --git a/events/api/src/main/java/org/keycloak/events/EventType.java b/events/api/src/main/java/org/keycloak/events/EventType.java
index 1e7a2c0..917b772 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -39,6 +39,8 @@ public enum EventType {
     SEND_VERIFY_EMAIL,
     SEND_VERIFY_EMAIL_ERROR,
     SEND_RESET_PASSWORD,
-    SEND_RESET_PASSWORD_ERROR
+    SEND_RESET_PASSWORD_ERROR,
+    SOCIAL_LOGIN,
+    SOCIAL_LOGIN_ERROR
 
 }
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
index 1e0fb4d..1aede52 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
@@ -50,6 +50,8 @@ public interface LoginFormsProvider extends Provider {
 
     public LoginFormsProvider setClient(ClientModel client);
 
+    LoginFormsProvider setVerifyCode(String code);
+
     public LoginFormsProvider setQueryParams(MultivaluedMap<String, String> queryParams);
 
     public LoginFormsProvider setFormData(MultivaluedMap<String, String> formData);
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 34168cd..343c3c1 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -1,6 +1,7 @@
 package org.keycloak.login.freemarker;
 
 import org.jboss.logging.Logger;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
@@ -50,6 +51,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
 
     private static final Logger logger = Logger.getLogger(FreeMarkerLoginFormsProvider.class);
 
+    private String verifyCode;
     private String message;
     private String accessCode;
     private Response.Status status = Response.Status.OK;
@@ -108,7 +110,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
             case VERIFY_EMAIL:
                 try {
                     UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
-                    builder.queryParam("key", accessCode);
+                    builder.queryParam("code", accessCode);
+                    builder.queryParam("key", verifyCode);
 
                     String link = builder.build(realm.getName()).toString();
                     long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
@@ -134,7 +137,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
     }
 
     private Response createResponse(LoginFormsPages page) {
-        MultivaluedMap<String, String> queryParameterMap = queryParams != null ? queryParams : uriInfo.getQueryParameters();
+        MultivaluedMap<String, String> queryParameterMap = queryParams != null ? queryParams : new MultivaluedMapImpl<String, String>();
 
         String requestURI = uriInfo.getBaseUri().getPath();
         UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
@@ -309,6 +312,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
     }
 
     @Override
+    public LoginFormsProvider setVerifyCode(String code) {
+        this.verifyCode = code;
+        return this;
+    }
+
+    @Override
     public LoginFormsProvider setQueryParams(MultivaluedMap<String, String> queryParams) {
         this.queryParams = queryParams;
         return this;
diff --git a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
old mode 100644
new mode 100755
index 66f2131..2c0f9dd
--- a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
@@ -8,14 +8,14 @@ import java.util.Set;
 public interface ClientSessionModel {
 
     public String getId();
-
+    public RealmModel getRealm();
     public ClientModel getClient();
 
-    public String getState();
-
     public UserSessionModel getUserSession();
+    public void setUserSession(UserSessionModel userSession);
 
     public String getRedirectUri();
+    public void setRedirectUri(String uri);
 
     public int getTimestamp();
 
@@ -26,6 +26,19 @@ public interface ClientSessionModel {
     public void setAction(Action action);
 
     public Set<String> getRoles();
+    public void setRoles(Set<String> roles);
+
+    /**
+     * Authentication request type, i.e. OAUTH, SAML 2.0, SAML 1.1, etc.
+     *
+     * @return
+     */
+    public String getAuthMethod();
+    public void setAuthMethod(String method);
+
+    public String getNote(String name);
+    public void setNote(String name, String value);
+    public void removeNote(String name);
 
     public static enum Action {
         OAUTH_GRANT,
@@ -33,7 +46,9 @@ public interface ClientSessionModel {
         VERIFY_EMAIL,
         UPDATE_PROFILE,
         CONFIGURE_TOTP,
-        UPDATE_PASSWORD
+        UPDATE_PASSWORD,
+        AUTHENTICATE,
+        SOCIAL_CALLBACK
     }
 
 }
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 d2c66d0..466717d 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -11,8 +11,9 @@ import java.util.Set;
  */
 public interface UserSessionProvider extends Provider {
 
-    ClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession, String redirectUri, String state, Set<String> roles);
+    ClientSessionModel createClientSession(RealmModel realm, ClientModel client);
     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 getUserSession(RealmModel realm, String id);
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
old mode 100644
new mode 100755
index ebcef17..8dd9763
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
@@ -6,10 +6,15 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.sessions.jpa.entities.ClientSessionEntity;
+import org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity;
 import org.keycloak.models.sessions.jpa.entities.ClientSessionRoleEntity;
+import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
 
 import javax.persistence.EntityManager;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -30,6 +35,49 @@ public class ClientSessionAdapter implements ClientSessionModel {
     }
 
     @Override
+    public RealmModel getRealm() {
+        return session.realms().getRealm(entity.getRealmId());
+    }
+
+    @Override
+    public void setNote(String name, String value) {
+        for (ClientSessionNoteEntity attr : entity.getNotes()) {
+            if (attr.getName().equals(name)) {
+                attr.setValue(value);
+                return;
+            }
+        }
+        ClientSessionNoteEntity attr = new ClientSessionNoteEntity();
+        attr.setName(name);
+        attr.setValue(value);
+        attr.setClientSession(entity);
+        em.persist(attr);
+        entity.getNotes().add(attr);
+    }
+
+    @Override
+    public void removeNote(String name) {
+        Iterator<ClientSessionNoteEntity> it = entity.getNotes().iterator();
+        while (it.hasNext()) {
+            ClientSessionNoteEntity attr = it.next();
+            if (attr.getName().equals(name)) {
+                it.remove();
+                em.remove(attr);
+            }
+        }
+    }
+
+    @Override
+    public String getNote(String name) {
+        for (ClientSessionNoteEntity attr : entity.getNotes()) {
+            if (attr.getName().equals(name)) {
+                return attr.getValue();
+            }
+        }
+        return null;
+    }
+
+    @Override
     public String getId() {
         return entity.getId();
     }
@@ -40,12 +88,47 @@ public class ClientSessionAdapter implements ClientSessionModel {
     }
 
     @Override
-    public String getState() {
-        return entity.getState();
+    public void setUserSession(UserSessionModel userSession) {
+        UserSessionAdapter adapter = (UserSessionAdapter)userSession;
+        UserSessionEntity userSessionEntity = adapter.getEntity();
+        entity.setSession(userSessionEntity);
+        userSessionEntity.getClientSessions().add(entity);
+    }
+
+    @Override
+    public void setRedirectUri(String uri) {
+       entity.setRedirectUri(uri);
+    }
+
+    @Override
+    public void setRoles(Set<String> roles) {
+        if (roles != null) {
+            List<ClientSessionRoleEntity> roleEntities = new LinkedList<ClientSessionRoleEntity>();
+            for (String r : roles) {
+                ClientSessionRoleEntity roleEntity = new ClientSessionRoleEntity();
+                roleEntity.setClientSession(entity);
+                roleEntity.setRoleId(r);
+                em.persist(roleEntity);
+
+                roleEntities.add(roleEntity);
+            }
+            entity.setRoles(roleEntities);
+        }
+    }
+
+    @Override
+    public String getAuthMethod() {
+        return entity.getAuthMethod();
+    }
+
+    @Override
+    public void setAuthMethod(String method) {
+        entity.setAuthMethod(method);
     }
 
     @Override
     public UserSessionModel getUserSession() {
+        if (entity.getSession() == null) return null;
         return new UserSessionAdapter(session, em, realm, entity.getSession());
     }
 
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java
index ffd62c3..c4bbe06 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java
@@ -23,10 +23,11 @@ import java.util.Collection;
 @Entity
 @Table(name = "CLIENT_SESSION")
 @NamedQueries({
-        @NamedQuery(name = "removeClientSessionByRealm", query = "delete from ClientSessionEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId)"),
+        @NamedQuery(name = "removeClientSessionByRealm", query = "delete from ClientSessionEntity a where a.realmId = :realmId"),
         @NamedQuery(name = "removeClientSessionByUser", query = "delete from ClientSessionEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId)"),
-        @NamedQuery(name = "removeClientSessionByClient", query = "delete from ClientSessionEntity a where a.clientId = :clientId and a.session IN (select s from UserSessionEntity s where s.realmId = :realmId)"),
-        @NamedQuery(name = "removeClientSessionByExpired", query = "delete from ClientSessionEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime))")
+        @NamedQuery(name = "removeClientSessionByClient", query = "delete from ClientSessionEntity a where a.clientId = :clientId and a.realmId = :realmId"),
+        @NamedQuery(name = "removeClientSessionByExpired", query = "delete from ClientSessionEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime))"),
+        @NamedQuery(name = "removeDetachedClientSessionByExpired", query = "delete from ClientSessionEntity a where a.session IS NULL and a.timestamp < :maxTime and a.realmId = :realmId")
 })
 public class ClientSessionEntity {
 
@@ -41,14 +42,17 @@ public class ClientSessionEntity {
     @Column(name="CLIENT_ID",length = 36)
     protected String clientId;
 
+    @Column(name="REALM_ID")
+    protected String realmId;
+
     @Column(name="TIMESTAMP")
     protected int timestamp;
 
     @Column(name="REDIRECT_URI")
     protected String redirectUri;
 
-    @Column(name="STATE")
-    protected String state;
+    @Column(name="AUTH_METHOD")
+    protected String authMethod;
 
     @Column(name="ACTION")
     protected ClientSessionModel.Action action;
@@ -56,6 +60,9 @@ public class ClientSessionEntity {
     @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="clientSession")
     protected Collection<ClientSessionRoleEntity> roles = new ArrayList<ClientSessionRoleEntity>();
 
+    @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="clientSession")
+    protected Collection<ClientSessionNoteEntity> notes = new ArrayList<ClientSessionNoteEntity>();
+
     public String getId() {
         return id;
     }
@@ -80,6 +87,14 @@ public class ClientSessionEntity {
         this.clientId = clientId;
     }
 
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
     public int getTimestamp() {
         return timestamp;
     }
@@ -96,14 +111,6 @@ public class ClientSessionEntity {
         this.redirectUri = redirectUri;
     }
 
-    public String getState() {
-        return state;
-    }
-
-    public void setState(String state) {
-        this.state = state;
-    }
-
     public ClientSessionModel.Action getAction() {
         return action;
     }
@@ -120,4 +127,19 @@ public class ClientSessionEntity {
         this.roles = roles;
     }
 
+    public Collection<ClientSessionNoteEntity> getNotes() {
+        return notes;
+    }
+
+    public void setNotes(Collection<ClientSessionNoteEntity> notes) {
+        this.notes = notes;
+    }
+
+    public String getAuthMethod() {
+        return authMethod;
+    }
+
+    public void setAuthMethod(String authMethod) {
+        this.authMethod = authMethod;
+    }
 }
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionNoteEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionNoteEntity.java
new file mode 100755
index 0000000..d1025e0
--- /dev/null
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionNoteEntity.java
@@ -0,0 +1,109 @@
+package org.keycloak.models.sessions.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name = "removeClientSessionNoteByUser", query="delete from ClientSessionNoteEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId))"),
+        @NamedQuery(name = "removeClientSessionNoteByClient", query="delete from ClientSessionNoteEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.clientId = :clientId and c.realmId = :realmId)"),
+        @NamedQuery(name = "removeClientSessionNoteByRealm", query="delete from ClientSessionNoteEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.realmId = :realmId)"),
+        @NamedQuery(name = "removeClientSessionNoteByExpired", query = "delete from ClientSessionNoteEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime)))"),
+        @NamedQuery(name = "removeDetachedClientSessionNoteByExpired", query = "delete from ClientSessionNoteEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IS NULL and c.realmId = :realmId and c.timestamp < :maxTime )")
+})
+@Table(name="CLIENT_SESSION_NOTE")
+@Entity
+@IdClass(ClientSessionNoteEntity.Key.class)
+public class ClientSessionNoteEntity {
+
+    @Id
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name = "CLIENT_SESSION")
+    protected ClientSessionEntity clientSession;
+
+    @Id
+    @Column(name = "NAME")
+    protected String name;
+    @Column(name = "VALUE")
+    protected String value;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public ClientSessionEntity getClientSession() {
+        return clientSession;
+    }
+
+    public void setClientSession(ClientSessionEntity clientSession) {
+        this.clientSession = clientSession;
+    }
+
+    public static class Key implements Serializable {
+
+        protected ClientSessionEntity clientSession;
+
+        protected String name;
+
+        public Key() {
+        }
+
+        public Key(ClientSessionEntity clientSession, String name) {
+            this.clientSession = clientSession;
+            this.name = name;
+        }
+
+        public ClientSessionEntity getClientSession() {
+            return clientSession;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Key key = (Key) o;
+
+            if (name != null ? !name.equals(key.name) : key.name != null) return false;
+            if (clientSession != null ? !clientSession.getId().equals(key.clientSession != null ? key.clientSession.getId() : null) : key.clientSession != null) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = clientSession != null ? clientSession.getId().hashCode() : 0;
+            result = 31 * result + (name != null ? name.hashCode() : 0);
+            return result;
+        }
+    }
+
+}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionRoleEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionRoleEntity.java
index e1be9d3..fe79cad 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionRoleEntity.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionRoleEntity.java
@@ -17,9 +17,10 @@ import java.io.Serializable;
  */
 @NamedQueries({
         @NamedQuery(name = "removeClientSessionRoleByUser", query="delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId))"),
-        @NamedQuery(name = "removeClientSessionRoleByClient", query="delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.clientId = :clientId and c.session IN (select s from UserSessionEntity s where s.realmId = :realmId))"),
-        @NamedQuery(name = "removeClientSessionRoleByRealm", query="delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId))"),
-        @NamedQuery(name = "removeClientSessionRoleByExpired", query = "delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime)))")
+        @NamedQuery(name = "removeClientSessionRoleByClient", query="delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.clientId = :clientId and c.realmId = :realmId)"),
+        @NamedQuery(name = "removeClientSessionRoleByRealm", query="delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.realmId = :realmId)"),
+        @NamedQuery(name = "removeClientSessionRoleByExpired", query = "delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime)))"),
+        @NamedQuery(name = "removeDetachedClientSessionRoleByExpired", query = "delete from ClientSessionRoleEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IS NULL and c.realmId = :realmId and c.timestamp < :maxTime )")
 })
 @Table(name="CLIENT_SESSION_ROLE")
 @Entity
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
old mode 100644
new mode 100755
index 92e416b..335a48f
--- 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
@@ -37,33 +37,14 @@ public class JpaUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
-    public ClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession, String redirectUri, String state, Set<String> roles) {
-        UserSessionEntity userSessionEntity = em.find(UserSessionEntity.class, userSession.getId());
-
+    public ClientSessionModel createClientSession(RealmModel realm, ClientModel client) {
         ClientSessionEntity entity = new ClientSessionEntity();
         entity.setId(KeycloakModelUtils.generateId());
         entity.setTimestamp(Time.currentTime());
         entity.setClientId(client.getId());
-        entity.setSession(userSessionEntity);
-        entity.setRedirectUri(redirectUri);
-        entity.setState(state);
+        entity.setRealmId(realm.getId());
         em.persist(entity);
 
-        if (roles != null) {
-            List<ClientSessionRoleEntity> roleEntities = new LinkedList<ClientSessionRoleEntity>();
-            for (String r : roles) {
-                ClientSessionRoleEntity roleEntity = new ClientSessionRoleEntity();
-                roleEntity.setClientSession(entity);
-                roleEntity.setRoleId(r);
-                em.persist(roleEntity);
-
-                roleEntities.add(roleEntity);
-            }
-            entity.setRoles(roleEntities);
-        }
-
-        userSessionEntity.getClientSessions().add(entity);
-
         return new ClientSessionAdapter(session, em, realm, entity);
     }
 
@@ -77,6 +58,16 @@ public class JpaUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
+    public ClientSessionModel getClientSession(String id) {
+        ClientSessionEntity clientSession = em.find(ClientSessionEntity.class, id);
+        if (clientSession != null) {
+            RealmModel realm = session.realms().getRealm(clientSession.getRealmId());
+            return new ClientSessionAdapter(session, em, realm, clientSession);
+        }
+        return null;
+    }
+
+    @Override
     public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
         String id = username + "-" + realm;
         UsernameLoginFailureEntity entity = em.find(UsernameLoginFailureEntity.class, id);
@@ -185,6 +176,10 @@ public class JpaUserSessionProvider implements UserSessionProvider {
 
     @Override
     public void removeUserSessions(RealmModel realm, UserModel user) {
+        em.createNamedQuery("removeClientSessionNoteByUser")
+                .setParameter("realmId", realm.getId())
+                .setParameter("userId", user.getId())
+                .executeUpdate();
         em.createNamedQuery("removeClientSessionRoleByUser")
                 .setParameter("realmId", realm.getId())
                 .setParameter("userId", user.getId())
@@ -204,11 +199,28 @@ public class JpaUserSessionProvider implements UserSessionProvider {
         int maxTime = Time.currentTime() - realm.getSsoSessionMaxLifespan();
         int idleTime = Time.currentTime() - realm.getSsoSessionIdleTimeout();
 
+        em.createNamedQuery("removeDetachedClientSessionRoleByExpired")
+                .setParameter("realmId", realm.getId())
+                .setParameter("maxTime", idleTime)
+                .executeUpdate();
+        em.createNamedQuery("removeDetachedClientSessionNoteByExpired")
+                .setParameter("realmId", realm.getId())
+                .setParameter("maxTime", idleTime)
+                .executeUpdate();
+        em.createNamedQuery("removeDetachedClientSessionByExpired")
+                .setParameter("realmId", realm.getId())
+                .setParameter("maxTime", idleTime)
+                .executeUpdate();
         em.createNamedQuery("removeClientSessionRoleByExpired")
                 .setParameter("realmId", realm.getId())
                 .setParameter("maxTime", maxTime)
                 .setParameter("idleTime", idleTime)
                 .executeUpdate();
+        em.createNamedQuery("removeClientSessionNoteByExpired")
+                .setParameter("realmId", realm.getId())
+                .setParameter("maxTime", maxTime)
+                .setParameter("idleTime", idleTime)
+                .executeUpdate();
         em.createNamedQuery("removeClientSessionByExpired")
                 .setParameter("realmId", realm.getId())
                 .setParameter("maxTime", maxTime)
@@ -223,6 +235,7 @@ public class JpaUserSessionProvider implements UserSessionProvider {
 
     @Override
     public void removeUserSessions(RealmModel realm) {
+        em.createNamedQuery("removeClientSessionNoteByRealm").setParameter("realmId", realm.getId()).executeUpdate();
         em.createNamedQuery("removeClientSessionRoleByRealm").setParameter("realmId", realm.getId()).executeUpdate();
         em.createNamedQuery("removeClientSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
         em.createNamedQuery("removeUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
@@ -236,6 +249,7 @@ public class JpaUserSessionProvider implements UserSessionProvider {
 
     @Override
     public void onClientRemoved(RealmModel realm, ClientModel client) {
+        em.createNamedQuery("removeClientSessionNoteByClient").setParameter("realmId", realm.getId()).setParameter("clientId", client.getId()).executeUpdate();
         em.createNamedQuery("removeClientSessionRoleByClient").setParameter("realmId", realm.getId()).setParameter("clientId", client.getId()).executeUpdate();
         em.createNamedQuery("removeClientSessionByClient").setParameter("realmId", realm.getId()).setParameter("clientId", client.getId()).executeUpdate();
     }
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
old mode 100644
new mode 100755
index 81918b1..1328171
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
@@ -6,6 +6,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.sessions.mem.entities.ClientSessionEntity;
+import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
 
 import java.util.Set;
 
@@ -32,21 +33,42 @@ public class ClientSessionAdapter implements ClientSessionModel {
     }
 
     @Override
-    public ClientModel getClient() {
-        return realm.findClientById(entity.getClientId());
+    public RealmModel getRealm() {
+        return session.realms().getRealm(entity.getRealmId());
     }
 
+
+
     @Override
-    public String getState() {
-        return entity.getState();
+    public ClientModel getClient() {
+        return realm.findClientById(entity.getClientId());
     }
 
     @Override
     public UserSessionModel getUserSession() {
+        if (entity.getSession() == null) return null;
         return new UserSessionAdapter(session, provider, realm, entity.getSession());
     }
 
     @Override
+    public void setUserSession(UserSessionModel userSession) {
+        UserSessionAdapter adapter = (UserSessionAdapter)userSession;
+        UserSessionEntity userSessionEntity = adapter.getEntity();
+        entity.setSession(userSessionEntity);
+        userSessionEntity.getClientSessions().add(entity);
+    }
+
+    @Override
+    public void setRedirectUri(String uri) {
+        entity.setRedirectUri(uri);
+    }
+
+    @Override
+    public void setRoles(Set<String> roles) {
+        entity.setRoles(roles);
+    }
+
+    @Override
     public String getRedirectUri() {
         return entity.getRedirectUri();
     }
@@ -76,4 +98,30 @@ public class ClientSessionAdapter implements ClientSessionModel {
         return entity.getRoles();
     }
 
+    @Override
+    public String getNote(String name) {
+        return entity.getNotes().get(name);
+    }
+
+    @Override
+    public void setNote(String name, String value) {
+        entity.getNotes().put(name, value);
+
+    }
+
+    @Override
+    public void removeNote(String name) {
+        entity.getNotes().remove(name);
+
+    }
+
+    @Override
+    public String getAuthMethod() {
+        return entity.getAuthMethod();
+    }
+
+    @Override
+    public void setAuthMethod(String method) {
+        entity.setAuthMethod(method);
+    }
 }
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java
old mode 100644
new mode 100755
index f56829d..dd945de
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java
@@ -2,6 +2,8 @@ package org.keycloak.models.sessions.mem.entities;
 
 import org.keycloak.models.ClientSessionModel;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -11,15 +13,17 @@ public class ClientSessionEntity {
 
     private String id;
     private String clientId;
+    private String realmId;
 
     private UserSessionEntity session;
 
     private String redirectUri;
-    private String state;
+    private String authMethod;
 
     private int timestamp;
     private ClientSessionModel.Action action;
     private Set<String> roles;
+    private Map<String, String> notes = new HashMap<String, String>();
 
     public String getId() {
         return id;
@@ -37,6 +41,14 @@ public class ClientSessionEntity {
         this.clientId = clientId;
     }
 
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
     public UserSessionEntity getSession() {
         return session;
     }
@@ -53,14 +65,6 @@ public class ClientSessionEntity {
         this.redirectUri = redirectUri;
     }
 
-    public String getState() {
-        return state;
-    }
-
-    public void setState(String state) {
-        this.state = state;
-    }
-
     public int getTimestamp() {
         return timestamp;
     }
@@ -84,4 +88,16 @@ public class ClientSessionEntity {
     public void setRoles(Set<String> roles) {
         this.roles = roles;
     }
+
+    public Map<String, String> getNotes() {
+        return notes;
+    }
+
+    public String getAuthMethod() {
+        return authMethod;
+    }
+
+    public void setAuthMethod(String authMethod) {
+        this.authMethod = authMethod;
+    }
 }
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 56d4755..7ef7e77 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
@@ -42,20 +42,12 @@ public class MemUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
-    public ClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession, String redirectUri, String state, Set<String> roles) {
-        UserSessionEntity userSessionEntity = getUserSessionEntity(realm, userSession.getId());
-
+    public ClientSessionModel createClientSession(RealmModel realm, ClientModel client) {
         ClientSessionEntity entity = new ClientSessionEntity();
         entity.setId(KeycloakModelUtils.generateId());
         entity.setTimestamp(Time.currentTime());
         entity.setClientId(client.getId());
-        entity.setSession(userSessionEntity);
-        entity.setRedirectUri(redirectUri);
-        entity.setState(state);
-        entity.setRoles(roles);
-
-        userSessionEntity.addClientSession(entity);
-
+        entity.setRealmId(realm.getId());
         clientSessions.put(entity.getId(), entity);
         return new ClientSessionAdapter(session, this, realm, entity);
     }
@@ -67,6 +59,16 @@ public class MemUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
+    public ClientSessionModel getClientSession(String id) {
+        ClientSessionEntity entity = clientSessions.get(id);
+        if (entity != null) {
+            RealmModel realm = session.realms().getRealm(entity.getRealmId());
+            return  new ClientSessionAdapter(session, this, realm, entity);
+        }
+        return null;
+    }
+
+    @Override
     public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
         String id = KeycloakModelUtils.generateId();
 
@@ -118,7 +120,9 @@ public class MemUserSessionProvider implements UserSessionProvider {
     public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
         List<UserSessionEntity> userSessionEntities = new LinkedList<UserSessionEntity>();
         for (ClientSessionEntity s : clientSessions.values()) {
-            if (s.getSession().getRealm().equals(realm.getId()) && s.getClientId().equals(client.getId())) {
+            String realmId = realm.getId();
+            String clientId = client.getId();
+            if (s.getSession().getRealm().equals(realmId) && s.getClientId().equals(clientId)) {
                 if (!userSessionEntities.contains(s.getSession())) {
                     userSessionEntities.add(s.getSession());
                 }
@@ -188,6 +192,13 @@ public class MemUserSessionProvider implements UserSessionProvider {
                 }
             }
         }
+        Iterator<ClientSessionEntity> citr = clientSessions.values().iterator();
+        while (citr.hasNext()) {
+            ClientSessionEntity c = citr.next();
+            if (c.getSession() == null && c.getTimestamp() < Time.currentTime() - realm.getSsoSessionIdleTimeout()) {
+                citr.remove();
+            }
+        }
     }
 
     @Override
@@ -203,6 +214,13 @@ public class MemUserSessionProvider implements UserSessionProvider {
                 }
             }
         }
+        Iterator<ClientSessionEntity> citr = clientSessions.values().iterator();
+        while (citr.hasNext()) {
+            ClientSessionEntity c = citr.next();
+            if (c.getSession() == null && c.getRealmId().equals(realm.getId())) {
+                citr.remove();
+            }
+        }
     }
 
     @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 6a3a963..3b8e867 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
@@ -30,6 +30,10 @@ public class UserSessionAdapter implements UserSessionModel {
         this.entity = entity;
     }
 
+    public UserSessionEntity getEntity() {
+        return entity;
+    }
+
     public String getId() {
         return entity.getId();
     }
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
old mode 100644
new mode 100755
index c21ae09..4e0f53c
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
@@ -10,6 +10,8 @@ import org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity;
 import org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity;
 
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -21,15 +23,13 @@ public class ClientSessionAdapter implements ClientSessionModel {
     private MongoUserSessionProvider provider;
     private RealmModel realm;
     private MongoClientSessionEntity entity;
-    private MongoUserSessionEntity userSessionEntity;
     private MongoStoreInvocationContext invContext;
 
-    public ClientSessionAdapter(KeycloakSession session, MongoUserSessionProvider provider, RealmModel realm, MongoClientSessionEntity entity, MongoUserSessionEntity userSessionEntity, MongoStoreInvocationContext invContext) {
+    public ClientSessionAdapter(KeycloakSession session, MongoUserSessionProvider provider, RealmModel realm, MongoClientSessionEntity entity, MongoStoreInvocationContext invContext) {
         this.session = session;
         this.provider = provider;
         this.realm = realm;
         this.entity = entity;
-        this.userSessionEntity = userSessionEntity;
         this.invContext = invContext;
     }
 
@@ -39,18 +39,39 @@ public class ClientSessionAdapter implements ClientSessionModel {
     }
 
     @Override
+    public RealmModel getRealm() {
+        return session.realms().getRealm(entity.getRealmId());
+    }
+
+    @Override
     public ClientModel getClient() {
         return realm.findClientById(entity.getClientId());
     }
 
     @Override
-    public String getState() {
-        return entity.getState();
+    public UserSessionModel getUserSession() {
+        if (entity.getSessionId() == null) return null;
+        return provider.getUserSession(realm, entity.getSessionId());
     }
 
     @Override
-    public UserSessionModel getUserSession() {
-        return new UserSessionAdapter(session, provider, userSessionEntity, realm, invContext);
+    public void setUserSession(UserSessionModel userSession) {
+        MongoUserSessionEntity userSessionEntity = provider.getUserSessionEntity(realm, userSession.getId());
+        entity.setSessionId(userSessionEntity.getId());
+        provider.getMongoStore().pushItemToList(userSessionEntity, "clientSessions", entity.getId(), true, invContext);
+    }
+
+    @Override
+    public void setRedirectUri(String uri) {
+        entity.setRedirectUri(uri);
+
+    }
+
+    @Override
+    public void setRoles(Set<String> roles) {
+        List<String> list = new LinkedList<String>();
+        list.addAll(roles);
+        entity.setRoles(list);
     }
 
     @Override
@@ -66,7 +87,6 @@ public class ClientSessionAdapter implements ClientSessionModel {
     @Override
     public void setTimestamp(int timestamp) {
         entity.setTimestamp(timestamp);
-        invContext.getMongoStore().updateEntity(userSessionEntity, invContext);
     }
 
     @Override
@@ -77,7 +97,6 @@ public class ClientSessionAdapter implements ClientSessionModel {
     @Override
     public void setAction(Action action) {
         entity.setAction(action);
-        invContext.getMongoStore().updateEntity(userSessionEntity, invContext);
     }
 
     @Override
@@ -85,4 +104,30 @@ public class ClientSessionAdapter implements ClientSessionModel {
         return entity.getRoles() != null ? new HashSet<String>(entity.getRoles()) : null;
     }
 
+    @Override
+    public String getNote(String name) {
+        return entity.getNotes().get(name);
+    }
+
+    @Override
+    public void setNote(String name, String value) {
+        entity.getNotes().put(name, value);
+
+    }
+
+    @Override
+    public void removeNote(String name) {
+        entity.getNotes().remove(name);
+
+    }
+
+    @Override
+    public String getAuthMethod() {
+        return entity.getAuthMethod();
+    }
+
+    @Override
+    public void setAuthMethod(String method) {
+        entity.setAuthMethod(method);
+    }
 }
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java
index d454f27..6dd1c09 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java
@@ -1,23 +1,31 @@
 package org.keycloak.models.sessions.mongo.entities;
 
+import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.entities.AbstractIdentifiableEntity;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public class MongoClientSessionEntity {
+public class MongoClientSessionEntity extends AbstractIdentifiableEntity implements MongoIdentifiableEntity {
 
     private String id;
     private String clientId;
+    private String realmId;
+    private String sessionId;
 
     private String redirectUri;
-    private String state;
+    private String authMethod;
 
     private int timestamp;
     private ClientSessionModel.Action action;
     private List<String> roles;
+    private Map<String, String> notes = new HashMap<String, String>();
 
     public String getId() {
         return id;
@@ -35,6 +43,14 @@ public class MongoClientSessionEntity {
         this.clientId = clientId;
     }
 
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
     public String getRedirectUri() {
         return redirectUri;
     }
@@ -43,12 +59,12 @@ public class MongoClientSessionEntity {
         this.redirectUri = redirectUri;
     }
 
-    public String getState() {
-        return state;
+    public String getAuthMethod() {
+        return authMethod;
     }
 
-    public void setState(String state) {
-        this.state = state;
+    public void setAuthMethod(String authMethod) {
+        this.authMethod = authMethod;
     }
 
     public int getTimestamp() {
@@ -75,4 +91,24 @@ public class MongoClientSessionEntity {
         this.roles = roles;
     }
 
+    public Map<String, String> getNotes() {
+        return notes;
+    }
+
+    public void setNotes(Map<String, String> notes) {
+        this.notes = notes;
+    }
+
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+    }
+
+    @Override
+    public void afterRemove(MongoStoreInvocationContext context) {
+    }
+
 }
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 5f2bd28..141297b 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
@@ -1,10 +1,13 @@
 package org.keycloak.models.sessions.mongo.entities;
 
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.MongoCollection;
 import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.entities.AbstractIdentifiableEntity;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -29,7 +32,7 @@ public class MongoUserSessionEntity extends AbstractIdentifiableEntity implement
 
     private int lastSessionRefresh;
 
-    private List<MongoClientSessionEntity> clientSessions;
+    private List<String> clientSessions = new ArrayList<String>();
 
     public String getRealmId() {
         return realmId;
@@ -95,16 +98,20 @@ public class MongoUserSessionEntity extends AbstractIdentifiableEntity implement
         this.lastSessionRefresh = lastSessionRefresh;
     }
 
-    public List<MongoClientSessionEntity> getClientSessions() {
+    public List<String> getClientSessions() {
         return clientSessions;
     }
 
-    public void setClientSessions(List<MongoClientSessionEntity> clientSessions) {
+    public void setClientSessions(List<String> clientSessions) {
         this.clientSessions = clientSessions;
     }
 
     @Override
     public void afterRemove(MongoStoreInvocationContext context) {
+        DBObject query = new QueryBuilder()
+                .and("sessionId").is(getId())
+                .get();
+        context.getMongoStore().removeEntities(MongoClientSessionEntity.class, query, context);
     }
 
 }
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 3fdfdba..a508c34 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
@@ -38,50 +38,38 @@ public class MongoUserSessionProvider implements UserSessionProvider {
         this.invocationContext = invocationContext;
     }
 
-    @Override
-    public ClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession, String redirectUri, String state, Set<String> roles) {
-        MongoUserSessionEntity userSessionEntity = getUserSessionEntity(realm, userSession.getId());
+    public MongoStore getMongoStore() {
+        return mongoStore;
+    }
 
+    @Override
+    public ClientSessionModel createClientSession(RealmModel realm, ClientModel client) {
         MongoClientSessionEntity entity = new MongoClientSessionEntity();
         entity.setId(KeycloakModelUtils.generateId());
         entity.setTimestamp(Time.currentTime());
         entity.setClientId(client.getId());
-        entity.setRedirectUri(redirectUri);
-        entity.setState(state);
-        if (roles != null) {
-            entity.setRoles(new LinkedList<String>(roles));
-        }
-
-        mongoStore.pushItemToList(userSessionEntity, "clientSessions", entity, false, invocationContext);
-
-        return new ClientSessionAdapter(session, this, realm, entity, userSessionEntity, invocationContext);
+        entity.setRealmId(realm.getId());
+        return new ClientSessionAdapter(session, this, realm, entity, invocationContext);
     }
 
     @Override
     public ClientSessionModel getClientSession(RealmModel realm, String id) {
-        DBObject query = new QueryBuilder()
-                .and("realmId").is(realm.getId())
-                .and("clientSessions.id").is(id).get();
-
-        List<MongoUserSessionEntity> entities = mongoStore.loadEntities(MongoUserSessionEntity.class, query, invocationContext);
-        if (entities.isEmpty()) {
-            return null;
-        }
+        MongoClientSessionEntity entity = getClientSessionEntity(id);
+        if (entity == null) return null;
+        return new ClientSessionAdapter(session, this, realm, entity, invocationContext);
+    }
 
-        MongoUserSessionEntity userSessionEntity = entities.get(0);
-        List<MongoClientSessionEntity> sessions = userSessionEntity.getClientSessions();
-        if (sessions == null) {
-            return null;
-        }
-        for (MongoClientSessionEntity s : sessions) {
-            if (s.getId().equals(id)) {
-                return new ClientSessionAdapter(session, this, realm, s, userSessionEntity, invocationContext);
-            }
+    @Override
+    public ClientSessionModel getClientSession(String id) {
+        MongoClientSessionEntity entity = getClientSessionEntity(id);
+        if (entity != null) {
+            RealmModel realm = session.realms().getRealm(entity.getRealmId());
+            return  new ClientSessionAdapter(session, this, realm, entity, invocationContext);
         }
-
         return null;
     }
 
+
     @Override
     public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe) {
         MongoUserSessionEntity entity = new MongoUserSessionEntity();
@@ -91,6 +79,7 @@ public class MongoUserSessionProvider implements UserSessionProvider {
         entity.setIpAddress(ipAddress);
         entity.setAuthMethod(authMethod);
         entity.setRememberMe(rememberMe);
+        entity.setRealmId(realm.getId());
 
         int currentTime = Time.currentTime();
 
@@ -115,6 +104,10 @@ public class MongoUserSessionProvider implements UserSessionProvider {
         return mongoStore.loadEntity(MongoUserSessionEntity.class, id, invocationContext);
     }
 
+    MongoClientSessionEntity getClientSessionEntity(String id) {
+        return mongoStore.loadEntity(MongoClientSessionEntity.class, id, invocationContext);
+    }
+
     @Override
     public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
         DBObject query = new BasicDBObject("user", user.getId());
@@ -167,21 +160,35 @@ public class MongoUserSessionProvider implements UserSessionProvider {
     public void removeUserSessions(RealmModel realm) {
         DBObject query = new BasicDBObject("realmId", realm.getId());
         mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
+        query = new QueryBuilder()
+                .and("realmId").is(realm.getId())
+                .get();
+
+        mongoStore.removeEntities(MongoClientSessionEntity.class, query, invocationContext);
     }
 
     @Override
     public void removeExpiredUserSessions(RealmModel realm) {
         int currentTime = Time.currentTime();
         DBObject query = new QueryBuilder()
+                .and("realmId").is(realm.getId())
                 .and("started").lessThan(currentTime - realm.getSsoSessionMaxLifespan())
                 .get();
 
         mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
         query = new QueryBuilder()
+                .and("realmId").is(realm.getId())
                 .and("lastSessionRefresh").lessThan(currentTime - realm.getSsoSessionIdleTimeout())
                 .get();
 
         mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
+        query = new QueryBuilder()
+                .and("sessionId").is(null)
+                .and("realmId").is(realm.getId())
+                .and("timestamp").lessThan(currentTime - realm.getSsoSessionIdleTimeout())
+                .get();
+
+        mongoStore.removeEntities(MongoClientSessionEntity.class, query, invocationContext);
     }
 
     @Override
@@ -239,24 +246,9 @@ public class MongoUserSessionProvider implements UserSessionProvider {
     // TODO Not very efficient, should use Mongo $pull to remove directly
     public void onClientRemoved(RealmModel realm, ClientModel client) {
         DBObject query = new QueryBuilder()
-                .and("clientSessions.clientId").is(client.getId())
+                .and("clientId").is(client.getId())
                 .get();
-        List<MongoUserSessionEntity> userSessionEntities = mongoStore.loadEntities(MongoUserSessionEntity.class, query, invocationContext);
-        for (MongoUserSessionEntity e : userSessionEntities) {
-            if (e.getClientSessions() == null) {
-                continue;
-            }
-
-            List<MongoClientSessionEntity> remove = new LinkedList<MongoClientSessionEntity>();
-            for (MongoClientSessionEntity c : e.getClientSessions()) {
-                if (c.getClientId().equals(client.getId())) {
-                    remove.add(c);
-                }
-            }
-            for (MongoClientSessionEntity c : remove) {
-                mongoStore.pullItemFromList(e, "clientSessions", c, invocationContext);
-            }
-        }
+        mongoStore.removeEntities(MongoUserSessionEntity.class, query, invocationContext);
     }
 
     @Override
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 6a66585..e47833d 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
@@ -135,8 +135,10 @@ public class UserSessionAdapter extends AbstractMongoAdapter<MongoUserSessionEnt
             return sessions;
         }
 
-        for (MongoClientSessionEntity e : entity.getClientSessions()) {
-            sessions.add(new ClientSessionAdapter(keycloakSession, provider, realm, e, entity, invocationContext));
+        for (String id : entity.getClientSessions()) {
+            ClientSessionModel clientSession = provider.getClientSession(realm, id);
+            if (clientSession == null) continue;
+            sessions.add(clientSession);
         }
         return sessions;
     }

services/pom.xml 5(+5 -0)

diff --git a/services/pom.xml b/services/pom.xml
index e2390b7..0e5e597 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -15,6 +15,11 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-federation</artifactId>
+            <version>2.7.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
             <groupId>org.bouncycastle</groupId>
             <artifactId>bcprov-jdk16</artifactId>
             <scope>provided</scope>
diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCode.java b/services/src/main/java/org/keycloak/services/managers/AccessCode.java
index edea255..8b9d3f4 100755
--- a/services/src/main/java/org/keycloak/services/managers/AccessCode.java
+++ b/services/src/main/java/org/keycloak/services/managers/AccessCode.java
@@ -103,7 +103,7 @@ public class AccessCode {
     }
 
     public String getState() {
-        return clientSession.getState();
+        throw new RuntimeException("REFACTORING, TODO REMOVE ACCESS CODE");
     }
 
     public String getRedirectUri() {
diff --git a/services/src/main/java/org/keycloak/services/managers/Auth.java b/services/src/main/java/org/keycloak/services/managers/Auth.java
index 7568c1b..1527ea5 100755
--- a/services/src/main/java/org/keycloak/services/managers/Auth.java
+++ b/services/src/main/java/org/keycloak/services/managers/Auth.java
@@ -2,6 +2,7 @@ package org.keycloak.services.managers;
 
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
@@ -18,6 +19,7 @@ public class Auth {
     private final UserModel user;
     private final ClientModel client;
     private final UserSessionModel session;
+    private ClientSessionModel clientSession;
 
     public Auth(RealmModel realm, AccessToken token, UserModel user, ClientModel client, UserSessionModel session, boolean cookie) {
         this.cookie = cookie;
@@ -53,6 +55,14 @@ public class Auth {
         return session;
     }
 
+    public ClientSessionModel getClientSession() {
+        return clientSession;
+    }
+
+    public void setClientSession(ClientSessionModel clientSession) {
+        this.clientSession = clientSession;
+    }
+
     public boolean hasRealmRole(String role) {
         if (cookie) {
             return user.hasRole(realm.getRole(role));
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
new file mode 100755
index 0000000..217675a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
@@ -0,0 +1,173 @@
+package org.keycloak.services.managers;
+
+import org.keycloak.OAuthErrorException;
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.util.Base64Url;
+import org.keycloak.util.Time;
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.Signature;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClientSessionCode {
+
+    private final RealmModel realm;
+    private final ClientSessionModel clientSession;
+
+    public ClientSessionCode(RealmModel realm, ClientSessionModel clientSession) {
+        this.realm = realm;
+        this.clientSession = clientSession;
+    }
+
+    public static ClientSessionCode parse(String code, KeycloakSession session) {
+        try {
+            String[] parts = code.split("\\.");
+            String id = new String(Base64Url.decode(parts[1]));
+
+            ClientSessionModel clientSession = session.sessions().getClientSession(id);
+            if (clientSession == null) {
+                return null;
+            }
+
+            String hash = createSignatureHash(clientSession.getRealm(), clientSession);
+            if (!hash.equals(parts[0])) {
+                return null;
+            }
+
+            return new ClientSessionCode(clientSession.getRealm(), clientSession);
+        } catch (RuntimeException e) {
+            return null;
+        }
+    }
+
+
+    public static ClientSessionCode parse(String code, KeycloakSession session, RealmModel realm) {
+        try {
+            String[] parts = code.split("\\.");
+            String id = new String(Base64Url.decode(parts[1]));
+
+            ClientSessionModel clientSession = session.sessions().getClientSession(realm, id);
+            if (clientSession == null) {
+                return null;
+            }
+
+            String hash = createSignatureHash(realm, clientSession);
+            if (!hash.equals(parts[0])) {
+                return null;
+            }
+
+            return new ClientSessionCode(realm, clientSession);
+        } catch (RuntimeException e) {
+            return null;
+        }
+    }
+
+    public ClientSessionModel getClientSession() {
+        return clientSession;
+    }
+
+    public boolean isValid(RequiredAction requiredAction) {
+        return isValid(convertToAction(requiredAction));
+    }
+
+    public boolean isValid(ClientSessionModel.Action requestedAction) {
+        ClientSessionModel.Action action = clientSession.getAction();
+        if (action == null) {
+            return false;
+        }
+
+        int timestamp = clientSession.getTimestamp();
+
+        if (!action.equals(requestedAction)) {
+            return false;
+        }
+
+        int lifespan = action.equals(ClientSessionModel.Action.CODE_TO_TOKEN) ? realm.getAccessCodeLifespan() : realm.getAccessCodeLifespanUserAction();
+        return timestamp + lifespan > Time.currentTime();
+    }
+
+    public Set<RoleModel> getRequestedRoles() {
+        Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
+        for (String roleId : clientSession.getRoles()) {
+            RoleModel role = realm.getRoleById(roleId);
+            if (role == null) {
+                new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid role " + roleId);
+            }
+            requestedRoles.add(realm.getRoleById(roleId));
+        }
+        return requestedRoles;
+    }
+
+    public void setAction(ClientSessionModel.Action action) {
+        clientSession.setAction(action);
+        clientSession.setTimestamp(Time.currentTime());
+    }
+
+    public void setRequiredAction(RequiredAction requiredAction) {
+        setAction(convertToAction(requiredAction));
+    }
+
+    private ClientSessionModel.Action convertToAction(RequiredAction requiredAction) {
+        switch (requiredAction) {
+            case CONFIGURE_TOTP:
+                return ClientSessionModel.Action.CONFIGURE_TOTP;
+            case UPDATE_PASSWORD:
+                return ClientSessionModel.Action.UPDATE_PASSWORD;
+            case UPDATE_PROFILE:
+                return ClientSessionModel.Action.UPDATE_PROFILE;
+            case VERIFY_EMAIL:
+                return ClientSessionModel.Action.VERIFY_EMAIL;
+            default:
+                throw new IllegalArgumentException("Unknown required action " + requiredAction);
+        }
+    }
+
+    public String getCode() {
+        return generateCode(realm, clientSession);
+    }
+
+    private static String generateCode(RealmModel realm, ClientSessionModel clientSession) {
+        String hash = createSignatureHash(realm, clientSession);
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(hash);
+        sb.append(".");
+        sb.append(Base64Url.encode(clientSession.getId().getBytes()));
+
+        return sb.toString();
+    }
+
+    private static String createSignatureHash(RealmModel realm, ClientSessionModel clientSession) {
+        try {
+            Signature signature = Signature.getInstance(RSAProvider.getJavaAlgorithm(Algorithm.RS256));
+            signature.initSign(realm.getPrivateKey());
+            signature.update(clientSession.getId().getBytes());
+            signature.update(ByteBuffer.allocate(4).putInt(clientSession.getTimestamp()));
+            if (clientSession.getAction() != null) {
+                signature.update(clientSession.getAction().toString().getBytes());
+            }
+            byte[] sign = signature.sign();
+
+            MessageDigest digest = MessageDigest.getInstance("sha-1");
+            digest.update(sign);
+            return Base64Url.encode(digest.digest());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 112f6e6..e7de717 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -53,16 +53,6 @@ public class TokenManager {
         }
     }
 
-    public AccessCode createAccessCode(String scopeParam, String state, String redirect, KeycloakSession session, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession) {
-        Set<String> requestedRoles = new HashSet<String>();
-        for (RoleModel r : getAccess(scopeParam, client, user)) {
-            requestedRoles.add(r.getId());
-        }
-
-        ClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession, redirect, state, requestedRoles);
-        return new AccessCode(realm, clientSession);
-    }
-
     public AccessToken refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel client, String encodedRefreshToken, EventBuilder event) throws OAuthErrorException {
         RefreshToken refreshToken = verifyRefreshToken(realm, encodedRefreshToken);
 
@@ -132,7 +122,21 @@ public class TokenManager {
         return token;
     }
 
-    public Set<RoleModel> getAccess(String scopeParam, ClientModel client, UserModel user) {
+    public static void attachClientSession(UserSessionModel session, ClientSessionModel clientSession) {
+        UserModel user = session.getUser();
+        clientSession.setUserSession(session);
+        Set<String> requestedRoles = new HashSet<String>();
+        // todo scope param protocol independent
+        for (RoleModel r : TokenManager.getAccess(null, clientSession.getClient(), user)) {
+            requestedRoles.add(r.getId());
+        }
+        clientSession.setRoles(requestedRoles);
+
+
+    }
+
+
+    public static Set<RoleModel> getAccess(String scopeParam, ClientModel client, UserModel user) {
         // todo scopeParam is ignored until we figure out a scheme that fits with openid connect
         Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
 
diff --git a/services/src/main/java/org/keycloak/services/protocol/OpenIdConnectProtocol.java b/services/src/main/java/org/keycloak/services/protocol/OpenIdConnectProtocol.java
new file mode 100755
index 0000000..79cf76c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/protocol/OpenIdConnectProtocol.java
@@ -0,0 +1,17 @@
+package org.keycloak.services.protocol;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OpenIdConnectProtocol {
+    public static final String LOGIN_PAGE_PROTOCOL = "oidc_login_page";
+
+    public static final String STATE_PARAM = "state";
+    public static final String SCOPE_PARAM = "scope";
+    public static final String RESPONSE_TYPE_PARAM = "response_type";
+    public static final String REDIRECT_URI_PARAM = "redirect_uri";
+    public static final String CLIENT_ID_PARAM = "client_id";
+    public static final String PROMPT_PARAM = "prompt";
+    public static final String LOGIN_HINT_PARAM = "login_hint";
+}
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 4ceb9f3..d343af6 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -54,7 +54,9 @@ import org.keycloak.services.ForbiddenException;
 import org.keycloak.services.managers.AppAuthManager;
 import org.keycloak.services.managers.Auth;
 import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
+import org.keycloak.services.protocol.OpenIdConnectProtocol;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.services.resources.flows.OAuthRedirect;
 import org.keycloak.services.resources.flows.Urls;
@@ -191,12 +193,15 @@ public class AccountService {
                 boolean associated = false;
                 for (ClientSessionModel c : userSession.getClientSessions()) {
                     if (c.getClient().equals(application)) {
+                        auth.setClientSession(c);
                         associated = true;
                         break;
                     }
                 }
                 if (!associated) {
-                    session.sessions().createClientSession(realm, application, userSession, null, null, null);
+                    ClientSessionModel clientSession = session.sessions().createClientSession(realm, application);
+                    clientSession.setUserSession(userSession);
+                    auth.setClientSession(clientSession);
                 }
             }
 
@@ -650,12 +655,13 @@ public class AccountService {
                 String redirectUri = UriBuilder.fromUri(Urls.accountSocialPage(uriInfo.getBaseUri(), realm.getName())).build().toString();
 
                 try {
+                    ClientSessionModel clientSession = auth.getClientSession();
+                    clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
+                    clientSession.setRedirectUri(redirectUri);
+                    clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, UUID.randomUUID().toString());
+                    ClientSessionCode clientSessionCode = new ClientSessionCode(realm, clientSession);
                     return Flows.social(realm, uriInfo, clientConnection, provider)
-                            .user(user)
-                            .putClientAttribute(OAuth2Constants.CLIENT_ID, Constants.ACCOUNT_MANAGEMENT_APP)
-                            .putClientAttribute(OAuth2Constants.STATE, UUID.randomUUID().toString())
-                            .putClientAttribute(OAuth2Constants.REDIRECT_URI, redirectUri)
-                            .redirectToSocialProvider();
+                            .redirectToSocialProvider(clientSessionCode);
                 } catch (SocialProviderException spe) {
                     setReferrerOnPage();
                     return account.setError(Messages.SOCIAL_REDIRECT_ERROR).createResponse(AccountPages.SOCIAL);
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 fd38a4c..4c2c6e3 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
@@ -9,6 +9,7 @@ import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
@@ -31,10 +32,12 @@ import org.keycloak.representations.idm.SocialLinkRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.UserSessionRepresentation;
 import org.keycloak.services.managers.AccessCode;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.managers.UserManager;
+import org.keycloak.services.protocol.OpenIdConnectProtocol;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.services.resources.flows.Urls;
 
@@ -58,6 +61,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -884,8 +888,6 @@ public class UsersResource {
 
         String redirect = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()).toString();
         String clientId = Constants.ACCOUNT_MANAGEMENT_APP;
-        String state = null;
-        String scope = null;
 
         ClientModel client = realm.findClient(clientId);
         if (client == null || !client.isEnabled()) {
@@ -894,13 +896,20 @@ public class UsersResource {
 
         UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false);
         //audit.session(userSession);
+        ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
+        clientSession.setAuthMethod(OpenIdConnectProtocol.LOGIN_PAGE_PROTOCOL);
+        clientSession.setRedirectUri(redirect);
+        clientSession.setUserSession(userSession);
+        ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
 
-        AccessCode accessCode = tokenManager.createAccessCode(null, state, redirect, session, realm, client, user, userSession);
         accessCode.setRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
 
         try {
             UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
-            builder.queryParam("key", accessCode.getCode());
+            builder.queryParam("code", accessCode.getCode());
+            String key = UUID.randomUUID().toString();
+            clientSession.setNote("key", key);
+            builder.queryParam("key", key);
 
             String link = builder.build(realm.getName()).toString();
             long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/ErrorFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/ErrorFlows.java
old mode 100644
new mode 100755
index 7d78352..7d575d5
--- a/services/src/main/java/org/keycloak/services/resources/flows/ErrorFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/ErrorFlows.java
@@ -22,4 +22,6 @@ public class ErrorFlows {
         return Response.status(status).entity(error).type(MediaType.APPLICATION_JSON).build();
     }
 
+
+
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Flows.java b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
index 9765c56..2adf26c 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
@@ -31,6 +31,7 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.social.SocialProvider;
 
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 /**
@@ -58,4 +59,9 @@ public class Flows {
         return new ErrorFlows();
     }
 
+    public static Response forwardToSecurityFailurePage(KeycloakSession session, RealmModel realm, UriInfo uriInfo, String message) {
+        return Flows.forms(session, realm, null, uriInfo).setError(message).createErrorPage();
+    }
+
+
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index a20f6fc..6482ff8 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -29,6 +29,7 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.Details;
 import org.keycloak.events.EventType;
+import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
@@ -42,16 +43,20 @@ import org.keycloak.models.UserSessionModel;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.AccessCode;
 import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.TokenManager;
+import org.keycloak.services.protocol.OpenIdConnectProtocol;
 
 import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -85,7 +90,7 @@ public class OAuthFlows {
         this.tokenManager = tokenManager;
     }
 
-    public Response redirectAccessCode(AccessCode accessCode, UserSessionModel userSession, String state, String redirect) {
+    public Response redirectAccessCode(ClientSessionCode accessCode, UserSessionModel userSession, String state, String redirect) {
         String code = accessCode.getCode();
         UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
         log.debugv("redirectAccessCode: state: {0}", state);
@@ -110,8 +115,8 @@ public class OAuthFlows {
         }
 
         // refresh the cookies!
-        authManager.createLoginCookie(realm, accessCode.getUser(), userSession, uriInfo, clientConnection);
-        if (userSession.isRememberMe()) authManager.createRememberMeCookie(realm, accessCode.getUser().getUsername(), uriInfo, clientConnection);
+        authManager.createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
+        if (userSession.isRememberMe()) authManager.createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection);
         return location.build();
     }
 
@@ -123,6 +128,72 @@ public class OAuthFlows {
         return Response.status(302).location(redirectUri.build()).build();
     }
 
+
+    public Response processAccessCode(ClientSessionModel clientSession, UserSessionModel session, EventBuilder event) {
+        UserModel user = session.getUser();
+        isTotpConfigurationRequired(user);
+        isEmailVerificationRequired(user);
+        ClientModel client = clientSession.getClient();
+
+        boolean isResource = client instanceof ApplicationModel;
+        ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
+
+
+        log.debugv("processAccessCode: isResource: {0}", isResource);
+        log.debugv("processAccessCode: go to oauth page?: {0}",
+                !isResource);
+
+        event.detail(Details.CODE_ID, clientSession.getId());
+
+        Set<RequiredAction> requiredActions = user.getRequiredActions();
+        if (!requiredActions.isEmpty()) {
+            RequiredAction action = user.getRequiredActions().iterator().next();
+            accessCode.setRequiredAction(action);
+
+            LoginFormsProvider loginFormsProvider = Flows.forms(this.session, realm, client, uriInfo).setAccessCode(accessCode.getCode()).setUser(user);
+            if (action.equals(RequiredAction.VERIFY_EMAIL)) {
+                String key = UUID.randomUUID().toString();
+                clientSession.setNote("key", key);
+                loginFormsProvider.setVerifyCode(key);
+                event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
+            }
+
+            return loginFormsProvider
+                    .createResponse(action);
+        }
+
+        if (!isResource) {
+            accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT);
+
+            List<RoleModel> realmRoles = new LinkedList<RoleModel>();
+            MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<String, RoleModel>();
+            for (RoleModel r : accessCode.getRequestedRoles()) {
+                if (r.getContainer() instanceof RealmModel) {
+                    realmRoles.add(r);
+                } else {
+                    resourceRoles.add(((ApplicationModel) r.getContainer()).getName(), r);
+                }
+            }
+
+            return Flows.forms(this.session, realm, client, uriInfo)
+                    .setAccessCode(accessCode.getCode())
+                    .setAccessRequest(realmRoles, resourceRoles)
+                    .setClient(client)
+                    .createOAuthGrant();
+        }
+
+        String redirect = clientSession.getRedirectUri();
+
+        if (redirect != null) {
+            event.success();
+            String state = clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM);
+            accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
+            return redirectAccessCode(accessCode, session, state, redirect);
+        } else {
+            return null;
+        }
+    }
+    /*
     public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, EventBuilder event) {
         isTotpConfigurationRequired(user);
         isEmailVerificationRequired(user);
@@ -178,10 +249,13 @@ public class OAuthFlows {
             return null;
         }
     }
+    */
 
+    /*
     public Response forwardToSecurityFailure(String message) {
         return Flows.forms(session, realm, null, uriInfo).setError(message).createErrorPage();
     }
+    */
 
     private void isTotpConfigurationRequired(UserModel user) {
         for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java
index 93eb48a..8aa642a 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/SocialRedirectFlows.java
@@ -2,8 +2,10 @@ package org.keycloak.services.resources.flows;
 
 import org.keycloak.ClientConnection;
 import org.keycloak.jose.jws.JWSBuilder;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.resources.SocialResource;
 import org.keycloak.services.util.CookieHelper;
 import org.keycloak.social.AuthRequest;
@@ -23,30 +25,17 @@ public class SocialRedirectFlows {
     private final UriInfo uriInfo;
     private ClientConnection clientConnection;
     private final SocialProvider socialProvider;
-    private final SocialResource.State state;
 
     SocialRedirectFlows(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, SocialProvider provider) {
         this.realm = realm;
         this.uriInfo = uriInfo;
         this.clientConnection = clientConnection;
         this.socialProvider = provider;
-
-        state = new SocialResource.State();
-        state.setRealm(realm.getName());
-        state.setProvider(provider.getId());
-    }
-
-    public SocialRedirectFlows putClientAttribute(String key, String value) {
-        state.set(key, value);
-        return this;
-    }
-
-    public SocialRedirectFlows user(UserModel user) {
-        state.setUser(user.getId());
-        return this;
     }
 
-    public Response redirectToSocialProvider() throws SocialProviderException {
+    public Response redirectToSocialProvider(ClientSessionCode code) throws SocialProviderException {
+        code.setAction(ClientSessionModel.Action.SOCIAL_CALLBACK);
+        code.getClientSession().setNote("social_provider", socialProvider.getId());
         String socialProviderId = socialProvider.getId();
 
         String key = realm.getSocialConfig().get(socialProviderId + ".key");
@@ -54,20 +43,8 @@ public class SocialRedirectFlows {
         String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString();
         SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);
 
-        String encodedState = new JWSBuilder().jsonContent(state).rsa256(realm.getPrivateKey());
-
-        AuthRequest authRequest = socialProvider.getAuthUrl(config, encodedState);
-
-        if (authRequest.getAttributes() != null) {
-            String cookiePath = Urls.socialBase(uriInfo.getBaseUri()).build().getRawPath().toString();
-
-            String encoded = new JWSBuilder()
-                    .jsonContent(authRequest.getAttributes())
-                    .rsa256(realm.getPrivateKey());
-
-            CookieHelper.addCookie("KEYCLOAK_SOCIAL", encoded, cookiePath, null, null, -1, realm.getSslRequired().isRequired(clientConnection), true);
-        }
 
+        AuthRequest authRequest = socialProvider.getAuthUrl(code.getClientSession(), config, code.getCode());
         return Response.status(302).location(authRequest.getAuthUri()).build();
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index fcd3c5d..ca5f72f 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -25,6 +25,7 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.ClientConnection;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.events.Event;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
@@ -44,8 +45,10 @@ import org.keycloak.models.utils.TimeBasedOTP;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.AccessCode;
 import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.messages.Messages;
+import org.keycloak.services.protocol.OpenIdConnectProtocol;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.services.resources.flows.Urls;
 import org.keycloak.services.validation.Validation;
@@ -54,6 +57,7 @@ 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.HttpHeaders;
 import javax.ws.rs.core.MediaType;
@@ -63,6 +67,7 @@ import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.Providers;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -102,22 +107,66 @@ public class RequiredActionsService {
         this.event = event;
     }
 
+    private boolean checkSsl() {
+        if (uriInfo.getBaseUri().getScheme().equals("https")) {
+            return true;
+        } else {
+            return !realm.getSslRequired().isRequired(clientConnection);
+        }
+    }
+
+
+    private class Checks {
+        ClientSessionCode clientCode;
+        Response response;
+
+        boolean check(String code, ClientSessionModel.Action requiredAction) {
+            if (!checkSsl()) {
+                event.error(Errors.SSL_REQUIRED);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
+                return false;
+            }
+            if (!realm.isEnabled()) {
+                event.error(Errors.REALM_DISABLED);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
+                return false;
+            }
+            clientCode = ClientSessionCode.parse(code, session, realm);
+            if (clientCode == null) {
+                event.error(Errors.INVALID_CODE);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
+                return false;
+            }
+            if (!clientCode.isValid(requiredAction)) {
+                event.error(Errors.INVALID_CODE);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
+            }
+            return true;
+        }
+    }
+
     @Path("profile")
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response updateProfile(final MultivaluedMap<String, String> formData) {
-        AccessCode accessCode = getAccessCodeEntry(RequiredAction.UPDATE_PROFILE);
-        if (accessCode == null) {
-            return unauthorized();
+    public Response updateProfile(@QueryParam("code") String code,
+                                  final MultivaluedMap<String, String> formData) {
+        event.event(EventType.UPDATE_PROFILE);
+        Checks checks = new Checks();
+        if (!checks.check(code, ClientSessionModel.Action.UPDATE_PROFILE)) {
+            return checks.response;
         }
+        ClientSessionCode accessCode = checks.clientCode;
+        ClientSessionModel clientSession = accessCode.getClientSession();
+        UserSessionModel userSession = clientSession.getUserSession();
+        UserModel user = userSession.getUser();
 
-        UserModel user = getUser(accessCode);
-
-        initEvent(accessCode);
+        initEvent(clientSession);
 
         String error = Validation.validateUpdateProfileForm(formData);
         if (error != null) {
-            return Flows.forms(session, realm, null, uriInfo).setUser(user).setError(error).createResponse(RequiredAction.UPDATE_PROFILE);
+            return Flows.forms(session, realm, null, uriInfo).setUser(user).setError(error)
+                    .setAccessCode(accessCode.getCode())
+                    .createResponse(RequiredAction.UPDATE_PROFILE);
         }
 
         user.setFirstName(formData.getFirst("firstName"));
@@ -137,30 +186,38 @@ public class RequiredActionsService {
             event.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
         }
 
-        return redirectOauth(user, accessCode);
+        return redirectOauth(user, accessCode, clientSession, userSession);
     }
 
     @Path("totp")
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response updateTotp(final MultivaluedMap<String, String> formData) {
-        AccessCode accessCode = getAccessCodeEntry(RequiredAction.CONFIGURE_TOTP);
-        if (accessCode == null) {
-            return unauthorized();
+    public Response updateTotp(@QueryParam("code") String code,
+                               final MultivaluedMap<String, String> formData) {
+        event.event(EventType.UPDATE_TOTP);
+        Checks checks = new Checks();
+        if (!checks.check(code, ClientSessionModel.Action.CONFIGURE_TOTP)) {
+            return checks.response;
         }
+        ClientSessionCode accessCode = checks.clientCode;
+        ClientSessionModel clientSession = accessCode.getClientSession();
+        UserSessionModel userSession = clientSession.getUserSession();
+        UserModel user = userSession.getUser();
 
-        UserModel user = getUser(accessCode);
-
-        initEvent(accessCode);
+        initEvent(clientSession);
 
         String totp = formData.getFirst("totp");
         String totpSecret = formData.getFirst("totpSecret");
 
         LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo).setUser(user);
         if (Validation.isEmpty(totp)) {
-            return loginForms.setError(Messages.MISSING_TOTP).createResponse(RequiredAction.CONFIGURE_TOTP);
+            return loginForms.setError(Messages.MISSING_TOTP)
+                    .setAccessCode(accessCode.getCode())
+                    .createResponse(RequiredAction.CONFIGURE_TOTP);
         } else if (!new TimeBasedOTP().validate(totp, totpSecret.getBytes())) {
-            return loginForms.setError(Messages.INVALID_TOTP).createResponse(RequiredAction.CONFIGURE_TOTP);
+            return loginForms.setError(Messages.INVALID_TOTP)
+                    .setAccessCode(accessCode.getCode())
+                    .createResponse(RequiredAction.CONFIGURE_TOTP);
         }
 
         UserCredentialModel credentials = new UserCredentialModel();
@@ -174,124 +231,174 @@ public class RequiredActionsService {
 
         event.clone().event(EventType.UPDATE_TOTP).success();
 
-        return redirectOauth(user, accessCode);
+        return redirectOauth(user, accessCode, clientSession, userSession);
     }
 
     @Path("password")
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response updatePassword(final MultivaluedMap<String, String> formData) {
-        AccessCode accessCode = getAccessCodeEntry(RequiredAction.UPDATE_PASSWORD);
-        if (accessCode == null) {
-            return unauthorized();
+    public Response updatePassword(@QueryParam("code") String code,
+                                   final MultivaluedMap<String, String> formData) {
+        event.event(EventType.UPDATE_PASSWORD);
+        Checks checks = new Checks();
+        if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD)) {
+            return checks.response;
         }
+        ClientSessionCode accessCode = checks.clientCode;
+        ClientSessionModel clientSession = accessCode.getClientSession();
+        UserSessionModel userSession = clientSession.getUserSession();
+        UserModel user = userSession.getUser();
 
-        UserModel user = getUser(accessCode);
-
-        initEvent(accessCode);
+        initEvent(clientSession);
 
         String passwordNew = formData.getFirst("password-new");
         String passwordConfirm = formData.getFirst("password-confirm");
 
         LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo).setUser(user);
         if (Validation.isEmpty(passwordNew)) {
-            return loginForms.setError(Messages.MISSING_PASSWORD).createResponse(RequiredAction.UPDATE_PASSWORD);
+            return loginForms.setError(Messages.MISSING_PASSWORD)
+                    .setAccessCode(accessCode.getCode())
+                    .createResponse(RequiredAction.UPDATE_PASSWORD);
         } else if (!passwordNew.equals(passwordConfirm)) {
-            return loginForms.setError(Messages.NOTMATCH_PASSWORD).createResponse(RequiredAction.UPDATE_PASSWORD);
+            return loginForms.setError(Messages.NOTMATCH_PASSWORD)
+                    .setAccessCode(accessCode.getCode())
+                    .createResponse(RequiredAction.UPDATE_PASSWORD);
         }
 
         try {
             session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
         } catch (Exception ape) {
-            return loginForms.setError(ape.getMessage()).createResponse(RequiredAction.UPDATE_PASSWORD);
+            return loginForms.setError(ape.getMessage())
+                    .setAccessCode(accessCode.getCode())
+                    .createResponse(RequiredAction.UPDATE_PASSWORD);
         }
 
         user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
 
         event.clone().event(EventType.UPDATE_PASSWORD).success();
+        return redirectOauth(user, accessCode, clientSession, userSession);
 
         // Redirect to account management to login if password reset was initiated by admin
+        /* here while refactoring,  ok to remove when you want
         if (accessCode.getSessionState() == null) {
             return Response.seeOther(Urls.accountPage(uriInfo.getBaseUri(), realm.getId())).build();
         } else {
             return redirectOauth(user, accessCode);
         }
+        */
     }
 
 
     @Path("email-verification")
     @GET
-    public Response emailVerification() {
+    public Response emailVerification(@QueryParam("code") String code) {
+        event.event(EventType.VERIFY_EMAIL);
         if (uriInfo.getQueryParameters().containsKey("key")) {
-            AccessCode accessCode = AccessCode.parse(uriInfo.getQueryParameters().getFirst("key"), session, realm);
-            if (accessCode == null || !accessCode.isValid(RequiredAction.VERIFY_EMAIL)) {
-                return unauthorized();
+            Checks checks = new Checks();
+            if (!checks.check(code, ClientSessionModel.Action.VERIFY_EMAIL)) {
+                return checks.response;
             }
-
-            UserModel user = getUser(accessCode);
-
-            initEvent(accessCode);
-
+            ClientSessionCode accessCode = checks.clientCode;
+            ClientSessionModel clientSession = accessCode.getClientSession();
+            UserSessionModel userSession = clientSession.getUserSession();
+            UserModel user = userSession.getUser();
+            String key = uriInfo.getQueryParameters().getFirst("key");
+            String keyNote = clientSession.getNote("key");
+            if (key == null || !key.equals(keyNote)) {
+                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Somebody is trying to illegally change your email.");
+            }
+            initEvent(clientSession);
             user.setEmailVerified(true);
 
             user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
 
-            event.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
+            event.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
 
-            return redirectOauth(user, accessCode);
+            return redirectOauth(user, accessCode, clientSession, userSession);
         } else {
-            AccessCode accessCode = getAccessCodeEntry(RequiredAction.VERIFY_EMAIL);
-            if (accessCode == null) {
-                return unauthorized();
+            Checks checks = new Checks();
+            if (!checks.check(code, ClientSessionModel.Action.VERIFY_EMAIL)) {
+                return checks.response;
             }
-
-            initEvent(accessCode);
-
-            return Flows.forms(session, realm, null, uriInfo).setAccessCode(accessCode.getCode()).setUser(accessCode.getUser())
+            ClientSessionCode accessCode = checks.clientCode;
+            ClientSessionModel clientSession = accessCode.getClientSession();
+            String verifyCode = UUID.randomUUID().toString();
+            clientSession.setNote("key", verifyCode);
+            UserSessionModel userSession = clientSession.getUserSession();
+            UserModel user = userSession.getUser();
+
+            initEvent(clientSession);
+
+            return Flows.forms(session, realm, null, uriInfo)
+                    .setAccessCode(accessCode.getCode())
+                    .setVerifyCode(verifyCode)
+                    .setUser(userSession.getUser())
                     .createResponse(RequiredAction.VERIFY_EMAIL);
         }
     }
 
     @Path("password-reset")
     @GET
-    public Response passwordReset() {
+    public Response passwordReset(@QueryParam("code") String code) {
+        event.event(EventType.SEND_RESET_PASSWORD);
         if (uriInfo.getQueryParameters().containsKey("key")) {
-            AccessCode accessCode = AccessCode.parse(uriInfo.getQueryParameters().getFirst("key"), session, realm);
-            if (accessCode == null || !accessCode.isValid(RequiredAction.UPDATE_PASSWORD)) {
-                return unauthorized();
+            Checks checks = new Checks();
+            if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD)) {
+                return checks.response;
             }
-
-            return Flows.forms(session, realm, null, uriInfo).setAccessCode(accessCode.getCode()).createResponse(RequiredAction.UPDATE_PASSWORD);
+            ClientSessionCode accessCode = checks.clientCode;
+            ClientSessionModel clientSession = accessCode.getClientSession();
+            UserSessionModel userSession = clientSession.getUserSession();
+            UserModel user = userSession.getUser();
+            String key = uriInfo.getQueryParameters().getFirst("key");
+            String keyNote = clientSession.getNote("key");
+            if (key == null || !key.equals(keyNote)) {
+                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Somebody is trying to illegally change your password.");
+            }
+            return Flows.forms(session, realm, null, uriInfo)
+                    .setAccessCode(accessCode.getCode())
+                    .createResponse(RequiredAction.UPDATE_PASSWORD);
         } else {
-            return Flows.forms(session, realm, null, uriInfo).createPasswordReset();
+            return Flows.forms(session, realm, null, uriInfo)
+                    .setAccessCode(code)
+                    .createPasswordReset();
         }
     }
 
     @Path("password-reset")
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response sendPasswordReset(final MultivaluedMap<String, String> formData) {
-        String username = formData.getFirst("username");
-
-        String scopeParam = uriInfo.getQueryParameters().getFirst(OAuth2Constants.SCOPE);
-        String state = uriInfo.getQueryParameters().getFirst(OAuth2Constants.STATE);
-        String redirect = uriInfo.getQueryParameters().getFirst(OAuth2Constants.REDIRECT_URI);
-        String clientId = uriInfo.getQueryParameters().getFirst(OAuth2Constants.CLIENT_ID);
+    public Response sendPasswordReset(@QueryParam("code") String code,
+                                      final MultivaluedMap<String, String> formData) {
+        event.event(EventType.SEND_RESET_PASSWORD);
+        if (!checkSsl()) {
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
+        }
+        if (!realm.isEnabled()) {
+            event.error(Errors.REALM_DISABLED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
+        }
+        ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
+        if (accessCode == null) {
+            event.error(Errors.INVALID_CODE);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
+        }
+        ClientSessionModel clientSession = accessCode.getClientSession();
 
-        AuthenticationManager authManager = new AuthenticationManager();
+        String username = formData.getFirst("username");
 
-        ClientModel client = realm.findClient(clientId);
+        ClientModel client = clientSession.getClient();
         if (client == null) {
-            return Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager).forwardToSecurityFailure(
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,
                     "Unknown login requester.");
         }
         if (!client.isEnabled()) {
-            return Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager).forwardToSecurityFailure(
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,
                     "Login requester not enabled.");
         }
 
-        event.event(EventType.SEND_RESET_PASSWORD).client(clientId)
-                .detail(Details.REDIRECT_URI, redirect)
+        event.client(client.getClientId())
+                .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
                 .detail(Details.RESPONSE_TYPE, "code")
                 .detail(Details.AUTH_METHOD, "form")
                 .detail(Details.USERNAME, username);
@@ -306,55 +413,35 @@ public class RequiredActionsService {
         } else {
             UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", false);
             event.session(userSession);
+            TokenManager.attachClientSession(userSession, clientSession);
 
-            AccessCode accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, session, realm, client, user, userSession);
             accessCode.setRequiredAction(RequiredAction.UPDATE_PASSWORD);
 
             try {
                 UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
-                builder.queryParam("key", accessCode.getCode());
+                builder.queryParam("code", accessCode.getCode());
+                String verifyCode = UUID.randomUUID().toString();
+                clientSession.setNote("key", verifyCode);
+                builder.queryParam("key", verifyCode);
 
                 String link = builder.build(realm.getName()).toString();
                 long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
 
                 this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendPasswordReset(link, expiration);
 
-                event.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
+                event.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, clientSession.getId()).success();
             } catch (EmailException e) {
                 logger.error("Failed to send password reset email", e);
-                return Flows.forms(this.session, realm, client, uriInfo).setError("emailSendError").createErrorPage();
+                return Flows.forms(this.session, realm, client, uriInfo).setError("emailSendError")
+                        .setAccessCode(accessCode.getCode())
+                        .createErrorPage();
             }
         }
 
         return Flows.forms(session, realm, client,  uriInfo).setSuccess("emailSent").createPasswordReset();
     }
 
-    private AccessCode getAccessCodeEntry(RequiredAction requiredAction) {
-        String code = uriInfo.getQueryParameters().getFirst(OAuth2Constants.CODE);
-        if (code == null) {
-            logger.debug("Code query param not found");
-            return null;
-        }
-
-        AccessCode accessCode = AccessCode.parse(code, session, realm);
-        if (accessCode == null) {
-            logger.debug("Access code not found");
-            return null;
-        }
-
-        if (!accessCode.isValid(requiredAction)) {
-            logger.debugv("Invalid access code");
-            return null;
-        }
-
-        return accessCode;
-    }
-
-    private UserModel getUser(AccessCode accessCode) {
-        return session.users().getUserByUsername(accessCode.getUser().getUsername(), realm);
-    }
-
-    private Response redirectOauth(UserModel user, AccessCode accessCode) {
+    private Response redirectOauth(UserModel user, ClientSessionCode accessCode, ClientSessionModel clientSession, UserSessionModel userSession) {
         if (accessCode == null) {
             return null;
         }
@@ -362,37 +449,40 @@ public class RequiredActionsService {
         Set<RequiredAction> requiredActions = user.getRequiredActions();
         if (!requiredActions.isEmpty()) {
             accessCode.setRequiredAction(requiredActions.iterator().next());
-            return Flows.forms(session, realm, null, uriInfo).setAccessCode(accessCode.getCode()).setUser(user)
+            return Flows.forms(session, realm, null, uriInfo)
+                    .setAccessCode(accessCode.getCode())
+                    .setUser(user)
                     .createResponse(requiredActions.iterator().next());
         } else {
-            logger.debugv("Redirecting to: {0}", accessCode.getRedirectUri());
+            logger.debugv("Redirecting to: {0}", clientSession.getRedirectUri());
             accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
 
             AuthenticationManager authManager = new AuthenticationManager();
 
-            UserSessionModel userSession = session.sessions().getUserSession(realm, accessCode.getSessionState());
             if (!AuthenticationManager.isSessionValid(realm, userSession)) {
                 AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection);
-                return Flows.oauth(this.session, realm, request, uriInfo, clientConnection, authManager, tokenManager).redirectError(accessCode.getClient(), "access_denied", accessCode.getState(), accessCode.getRedirectUri());
+                return Flows.oauth(this.session, realm, request, uriInfo, clientConnection, authManager, tokenManager)
+                        .redirectError(clientSession.getClient(), "access_denied", clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), clientSession.getRedirectUri());
             }
             event.session(userSession);
 
             event.success();
 
-            return Flows.oauth(this.session, realm, request, uriInfo, clientConnection, authManager, tokenManager).redirectAccessCode(accessCode,
-                    userSession, accessCode.getState(), accessCode.getRedirectUri());
+            return Flows.oauth(this.session, realm, request, uriInfo, clientConnection, authManager, tokenManager)
+                    .redirectAccessCode(accessCode,
+                            userSession, clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), clientSession.getRedirectUri());
         }
     }
 
-    private void initEvent(AccessCode accessCode) {
-        event.event(EventType.LOGIN).client(accessCode.getClient())
-                .user(accessCode.getUser())
-                .session(accessCode.getSessionState())
-                .detail(Details.CODE_ID, accessCode.getCodeId())
-                .detail(Details.REDIRECT_URI, accessCode.getRedirectUri())
+    private void initEvent(ClientSessionModel clientSession) {
+        event.event(EventType.LOGIN).client(clientSession.getClient())
+                .user(clientSession.getUserSession().getUser())
+                .session(clientSession.getUserSession().getId())
+                .detail(Details.CODE_ID, clientSession.getId())
+                .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
                 .detail(Details.RESPONSE_TYPE, "code");
 
-        UserSessionModel userSession = accessCode.getSessionState() != null ? session.sessions().getUserSession(realm, accessCode.getSessionState()) : null;
+        UserSessionModel userSession = clientSession.getUserSession();
 
         if (userSession != null) {
             event.detail(Details.AUTH_METHOD, userSession.getAuthMethod());
@@ -402,9 +492,4 @@ public class RequiredActionsService {
             }
         }
     }
-
-    private Response unauthorized() {
-        return Flows.forms(session, realm, null, uriInfo).setError("Unauthorized request").createErrorPage();
-    }
-
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index 572b74d..7a8733a 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -33,6 +33,7 @@ import org.keycloak.events.EventType;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.models.AccountRoles;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
@@ -41,6 +42,7 @@ import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.EventsManager;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.RealmManager;
@@ -106,51 +108,37 @@ public class SocialResource {
     @GET
     @Path("callback")
     public Response callback(@QueryParam("state") String encodedState) throws URISyntaxException, IOException {
-        State initialRequest;
+        ClientSessionCode clientCode = null;
+        ClientSessionModel clientSession = null;
         try {
-            initialRequest = new JWSInput(encodedState).readJsonContent(State.class);
+            clientCode = ClientSessionCode.parse(encodedState, session);
+            if (clientCode == null) {
+                return Flows.forms(session, null, null, uriInfo).setError("Unexpected callback").createErrorPage();
+            }
+            clientSession = clientCode.getClientSession();
+            if (!clientCode.isValid(ClientSessionModel.Action.SOCIAL_CALLBACK)) {
+                return Flows.forwardToSecurityFailurePage(session, clientSession.getRealm(), uriInfo, "Invalid code, please login again through your application.");
+            }
         } catch (Throwable t) {
             logger.error("Invalid social callback", t);
             return Flows.forms(session, null, null, uriInfo).setError("Unexpected callback").createErrorPage();
         }
+        String providerId = clientSession.getNote("social_provider");
+        SocialProvider provider = SocialLoader.load(providerId);
 
-        SocialProvider provider = SocialLoader.load(initialRequest.getProvider());
-
-        String realmName = initialRequest.getRealm();
         String authMethod = "social@" + provider.getId();
 
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = realmManager.getRealmByName(realmName);
+        RealmModel realm = clientSession.getRealm();
 
         EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder()
-                .event(EventType.LOGIN)
-                .detail(Details.RESPONSE_TYPE, initialRequest.get(OAuth2Constants.RESPONSE_TYPE))
+                .event(EventType.SOCIAL_LOGIN)
+                .client(clientSession.getClient())
+                .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
                 .detail(Details.AUTH_METHOD, authMethod);
 
-        AuthenticationManager authManager = new AuthenticationManager();
-        OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
-
         if (!realm.isEnabled()) {
             event.error(Errors.REALM_DISABLED);
-            return oauth.forwardToSecurityFailure("Realm not enabled.");
-        }
-
-        String clientId = initialRequest.get(OAuth2Constants.CLIENT_ID);
-        String redirectUri = initialRequest.get(OAuth2Constants.REDIRECT_URI);
-        String scope = initialRequest.get(OAuth2Constants.SCOPE);
-        String state = initialRequest.get(OAuth2Constants.STATE);
-        String responseType = initialRequest.get(OAuth2Constants.RESPONSE_TYPE);
-
-        event.client(clientId).detail(Details.REDIRECT_URI, redirectUri);
-
-        ClientModel client = realm.findClient(clientId);
-        if (client == null) {
-            event.error(Errors.CLIENT_NOT_FOUND);
-            return oauth.forwardToSecurityFailure("Unknown login requester.");
-        }
-        if (!client.isEnabled()) {
-            event.error(Errors.CLIENT_DISABLED);
-            return oauth.forwardToSecurityFailure("Login requester not enabled.");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
         }
 
         String key = realm.getSocialConfig().get(provider.getId() + ".key");
@@ -159,26 +147,19 @@ public class SocialResource {
         SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);
 
         Map<String, String[]> queryParams = getQueryParams();
-        Map<String, String> attributes = getAttributes();
 
-        AuthCallback callback = new AuthCallback(queryParams, attributes);
+        AuthCallback callback = new AuthCallback(queryParams);
 
         SocialUser socialUser;
         try {
-            socialUser = provider.processCallback(config, callback);
+            socialUser = provider.processCallback(clientSession, config, callback);
         } catch (SocialAccessDeniedException e) {
-            MultivaluedMap<String, String> queryParms = new MultivaluedMapImpl<String, String>();
-            queryParms.putSingle(OAuth2Constants.CLIENT_ID, clientId);
-            queryParms.putSingle(OAuth2Constants.STATE, state);
-            queryParms.putSingle(OAuth2Constants.SCOPE, scope);
-            queryParms.putSingle(OAuth2Constants.REDIRECT_URI, redirectUri);
-            queryParms.putSingle(OAuth2Constants.RESPONSE_TYPE, responseType);
-
             event.error(Errors.REJECTED_BY_USER);
-            return  Flows.forms(session, realm, client, uriInfo).setQueryParams(queryParms).setWarning("Access denied").createLogin();
+            clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
+            return  Flows.forms(session, realm, clientSession.getClient(), uriInfo).setAccessCode(clientCode.getCode()).setWarning("Access denied").createLogin();
         } catch (SocialProviderException e) {
             logger.error("Failed to process social callback", e);
-            return oauth.forwardToSecurityFailure("Failed to process social callback");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process social callback");
         }
 
         event.detail(Details.USERNAME, socialUser.getId() + "@" + provider.getId());
@@ -188,37 +169,32 @@ public class SocialResource {
             UserModel user = session.users().getUserBySocialLink(socialLink, realm);
 
             // Check if user is already authenticated (this means linking social into existing user account)
-            String userId = initialRequest.getUser();
-            if (userId != null) {
-                UserModel authenticatedUser = session.users().getUserById(userId, realm);
+            if (clientSession.getUserSession() != null) {
 
-                event.event(EventType.SOCIAL_LINK).user(userId);
+                UserModel authenticatedUser = clientSession.getUserSession().getUser();
+
+                event.event(EventType.SOCIAL_LINK).user(authenticatedUser.getId());
 
                 if (user != null) {
                     event.error(Errors.SOCIAL_ID_IN_USE);
-                    return oauth.forwardToSecurityFailure("This social account is already linked to other user");
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "This social account is already linked to other user");
                 }
 
                 if (!authenticatedUser.isEnabled()) {
                     event.error(Errors.USER_DISABLED);
-                    return oauth.forwardToSecurityFailure("User is disabled");
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "User is disabled");
                 }
 
                 if (!authenticatedUser.hasRole(realm.getApplicationByName(Constants.ACCOUNT_MANAGEMENT_APP).getRole(AccountRoles.MANAGE_ACCOUNT))) {
                     event.error(Errors.NOT_ALLOWED);
-                    return oauth.forwardToSecurityFailure("Insufficient permissions to link social account");
-                }
-
-                if (redirectUri == null) {
-                    event.error(Errors.INVALID_REDIRECT_URI);
-                    return oauth.forwardToSecurityFailure("Unknown redirectUri");
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Insufficient permissions to link social account");
                 }
 
                 session.users().addSocialLink(realm, authenticatedUser, socialLink);
                 logger.debugv("Social provider {0} linked with user {1}", provider.getId(), authenticatedUser.getUsername());
 
                 event.success();
-                return Response.status(302).location(UriBuilder.fromUri(redirectUri).build()).build();
+                return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
             }
 
             if (user == null) {
@@ -245,38 +221,79 @@ public class SocialResource {
 
             if (!user.isEnabled()) {
                 event.error(Errors.USER_DISABLED);
-                return oauth.forwardToSecurityFailure("Your account is not enabled.");
+                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Your account is not enabled.");
             }
 
             String username = socialLink.getSocialUserId() + "@" + socialLink.getSocialProvider();
 
             UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), authMethod, false);
             event.session(userSession);
+            TokenManager.attachClientSession(userSession, clientSession);
 
-            Response response = oauth.processAccessCode(scope, state, redirectUri, client, user, userSession, event);
+            OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, new AuthenticationManager(), tokenManager);
+            Response response = oauth.processAccessCode(clientSession, userSession, event);
             if (session.getTransaction().isActive()) {
                 session.getTransaction().commit();
             }
             return response;
         } catch (ModelDuplicateException e) {
             // Assume email is the duplicate as there's nothing else atm
-            return returnToLogin(realm, client, initialRequest.getAttributes(), "socialEmailExists");
+            return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
+                    .setAccessCode(clientCode.getCode())
+                    .setError("socialEmailExists")
+                    .createLogin();
         }
     }
 
+    private class Checks {
+        ClientSessionCode clientCode;
+        Response response;
+
+        private boolean checkSsl(RealmModel realm) {
+            if (uriInfo.getBaseUri().getScheme().equals("https")) {
+                return true;
+            } else {
+                return !realm.getSslRequired().isRequired(clientConnection);
+            }
+        }
+
+
+        boolean check(EventBuilder event, RealmModel realm, String code, ClientSessionModel.Action requiredAction) {
+            if (!checkSsl(realm)) {
+                event.error(Errors.SSL_REQUIRED);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
+                return false;
+            }
+            if (!realm.isEnabled()) {
+                event.error(Errors.REALM_DISABLED);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
+                return false;
+            }
+            clientCode = ClientSessionCode.parse(code, session, realm);
+            if (clientCode == null) {
+                event.error(Errors.INVALID_CODE);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
+                return false;
+            }
+            if (!clientCode.isValid(requiredAction)) {
+                event.error(Errors.INVALID_CODE);
+                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
+            }
+            return true;
+        }
+    }
+
+
     @GET
     @Path("{realm}/login")
     public Response redirectToProviderAuth(@PathParam("realm") final String realmName,
-                                           @QueryParam("provider_id") final String providerId, @QueryParam(OAuth2Constants.CLIENT_ID) final String clientId,
-                                           @QueryParam("scope") final String scope, @QueryParam("state") final String state,
-                                           @QueryParam("redirect_uri") String redirectUri, @QueryParam("response_type") String responseType) {
+                                           @QueryParam("provider_id") final String providerId,
+                                           @QueryParam("code") String code) {
         RealmManager realmManager = new RealmManager(session);
         RealmModel realm = realmManager.getRealmByName(realmName);
 
         EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder()
-                .event(EventType.LOGIN).client(clientId)
-                .detail(Details.REDIRECT_URI, redirectUri)
-                .detail(Details.RESPONSE_TYPE, "code")
+                .event(EventType.LOGIN)
                 .detail(Details.AUTH_METHOD, "social@" + providerId);
 
         SocialProvider provider = SocialLoader.load(providerId);
@@ -285,30 +302,16 @@ public class SocialResource {
             return Flows.forms(session, realm, null, uriInfo).setError("Social provider not found").createErrorPage();
         }
 
-        ClientModel client = realm.findClient(clientId);
-        if (client == null) {
-            event.error(Errors.CLIENT_NOT_FOUND);
-            return Flows.forms(session, realm, null, uriInfo).setError("Unknown login requester.").createErrorPage();
+        Checks checks = new Checks();
+        if (!checks.check(event, realm, code, ClientSessionModel.Action.AUTHENTICATE)) {
+            return checks.response;
         }
 
-        if (!client.isEnabled()) {
-            event.error(Errors.CLIENT_DISABLED);
-            return Flows.forms(session, realm, null, uriInfo).setError("Login requester not enabled.").createErrorPage();
-        }
-        redirectUri = TokenService.verifyRedirectUri(uriInfo, redirectUri, realm, client);
-        if (redirectUri == null) {
-            event.error(Errors.INVALID_REDIRECT_URI);
-            return Flows.forms(session, realm, null, uriInfo).setError("Invalid redirect_uri.").createErrorPage();
-        }
+        ClientSessionCode clientSessionCode = checks.clientCode;
 
         try {
             return Flows.social(realm, uriInfo, clientConnection, provider)
-                    .putClientAttribute(OAuth2Constants.CLIENT_ID, clientId)
-                    .putClientAttribute(OAuth2Constants.SCOPE, scope)
-                    .putClientAttribute(OAuth2Constants.STATE, state)
-                    .putClientAttribute(OAuth2Constants.REDIRECT_URI, redirectUri)
-                    .putClientAttribute(OAuth2Constants.RESPONSE_TYPE, responseType)
-                    .redirectToSocialProvider();
+                    .redirectToSocialProvider(clientSessionCode);
         } catch (Throwable t) {
             logger.error("Failed to redirect to social auth", t);
             return Flows.forms(session, realm, null, uriInfo).setError("Failed to redirect to social auth").createErrorPage();
@@ -334,52 +337,4 @@ public class SocialResource {
         return queryParams;
     }
 
-    private Map<String, String> getAttributes() throws IOException {
-        Cookie cookie = headers.getCookies().get("KEYCLOAK_SOCIAL");
-        return cookie != null ? new JWSInput(cookie.getValue()).readJsonContent(HashMap.class) : null;
-    }
-
-    public static class State {
-        private String realm;
-        private String provider;
-        private String user;
-        private Map<String, String> attributes  = new HashMap<String, String>();
-
-        public String getRealm() {
-            return realm;
-        }
-
-        public void setRealm(String realm) {
-            this.realm = realm;
-        }
-
-        public String getProvider() {
-            return provider;
-        }
-
-        public void setProvider(String provider) {
-            this.provider = provider;
-        }
-
-        public String getUser() {
-            return user;
-        }
-
-        public void setUser(String user) {
-            this.user = user;
-        }
-
-        public Map<String, String> getAttributes() {
-            return attributes;
-        }
-
-        public String get(String key) {
-            return attributes.get(key);
-        }
-
-        public void set(String key, String value) {
-            attributes.put(key, value);
-        }
-    }
-
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index d964906..55a320c 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -36,13 +36,14 @@ import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.RefreshToken;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.ForbiddenException;
-import org.keycloak.services.managers.AccessCode;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
 import org.keycloak.representations.PasswordToken;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.messages.Messages;
+import org.keycloak.services.protocol.OpenIdConnectProtocol;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.services.resources.flows.OAuthFlows;
 import org.keycloak.services.resources.flows.Urls;
@@ -462,26 +463,46 @@ public class TokenService {
     /**
      * URL called after login page.  YOU SHOULD NEVER INVOKE THIS DIRECTLY!
      *
-     * @param clientId
-     * @param scopeParam
-     * @param state
-     * @param redirect
+     * @param code
      * @param formData
      * @return
      */
     @Path("auth/request/login")
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response processLogin(@QueryParam("client_id") final String clientId, @QueryParam("scope") final String scopeParam,
-                                 @QueryParam("state") final String state, @QueryParam("redirect_uri") String redirect,
+    public Response processLogin(@QueryParam("code") String code,
                                  final MultivaluedMap<String, String> formData) {
+        event.event(EventType.LOGIN);
+        if (!checkSsl()) {
+            event.error(Errors.SSL_REQUIRED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
+        }
+
+        if (!realm.isEnabled()) {
+            event.error(Errors.REALM_DISABLED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
+        }
+        ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
+        if (clientCode == null) {
+            event.error(Errors.INVALID_CODE);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
+        }
+        ClientSessionModel clientSession = clientCode.getClientSession();
+        if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) {
+            clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
+            event.client(clientSession.getClient()).error(Errors.INVALID_USER_CREDENTIALS);
+            return Flows.forms(this.session, realm, clientSession.getClient(), uriInfo).setError(Messages.INVALID_USER)
+                    .setAccessCode(clientCode.getCode())
+                    .createLogin();
+        }
+
         String username = formData.getFirst(AuthenticationManager.FORM_USERNAME);
 
         String rememberMe = formData.getFirst("rememberMe");
         boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
 
-        event.event(EventType.LOGIN).client(clientId)
-                .detail(Details.REDIRECT_URI, redirect)
+        event.client(clientSession.getClient().getClientId())
+                .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
                 .detail(Details.RESPONSE_TYPE, "code")
                 .detail(Details.AUTH_METHOD, "form")
                 .detail(Details.USERNAME, username);
@@ -492,33 +513,19 @@ public class TokenService {
 
         OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
 
-        if (!checkSsl()) {
-            return oauth.forwardToSecurityFailure("HTTPS required");
-        }
-
-        if (!realm.isEnabled()) {
-            event.error(Errors.REALM_DISABLED);
-            return oauth.forwardToSecurityFailure("Realm not enabled.");
-        }
-        ClientModel client = realm.findClient(clientId);
+        ClientModel client = clientSession.getClient();
         if (client == null) {
             event.error(Errors.CLIENT_NOT_FOUND);
-            return oauth.forwardToSecurityFailure("Unknown login requester.");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
         }
         if (!client.isEnabled()) {
             event.error(Errors.CLIENT_NOT_FOUND);
-            return oauth.forwardToSecurityFailure("Login requester not enabled.");
-        }
-
-        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
-        if (redirect == null) {
-            event.error(Errors.INVALID_REDIRECT_URI);
-            return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
         }
 
         if (formData.containsKey("cancel")) {
             event.error(Errors.REJECTED_BY_USER);
-            return oauth.redirectError(client, "access_denied", state, redirect);
+            return oauth.redirectError(client, "access_denied", clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), clientSession.getRedirectUri());
         }
 
         AuthenticationStatus status = authManager.authenticateForm(session, clientConnection, realm, formData);
@@ -538,28 +545,45 @@ public class TokenService {
             case SUCCESS:
             case ACTIONS_REQUIRED:
                 UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "form", remember);
+                TokenManager.attachClientSession(userSession, clientSession);
 		        event.session(userSession);
 
-                return oauth.processAccessCode(scopeParam, state, redirect, client, user, userSession, event);
+                return oauth.processAccessCode(clientSession, userSession, event);
             case ACCOUNT_TEMPORARILY_DISABLED:
                 event.error(Errors.USER_TEMPORARILY_DISABLED);
-                return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).setFormData(formData).createLogin();
+                return Flows.forms(this.session, realm, client, uriInfo)
+                        .setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
+                        .setFormData(formData)
+                        .setAccessCode(clientCode.getCode())
+                        .createLogin();
             case ACCOUNT_DISABLED:
                 event.error(Errors.USER_DISABLED);
-                return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.ACCOUNT_DISABLED).setFormData(formData).createLogin();
+                return Flows.forms(this.session, realm, client, uriInfo)
+                        .setError(Messages.ACCOUNT_DISABLED)
+                        .setAccessCode(clientCode.getCode())
+                        .setFormData(formData).createLogin();
             case MISSING_TOTP:
                 formData.remove(CredentialRepresentation.PASSWORD);
 
                 String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
                 formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
 
-                return Flows.forms(this.session, realm, client, uriInfo).setFormData(formData).createLoginTotp();
+                return Flows.forms(this.session, realm, client, uriInfo)
+                        .setFormData(formData)
+                        .setAccessCode(clientCode.getCode())
+                        .createLoginTotp();
             case INVALID_USER:
                 event.error(Errors.USER_NOT_FOUND);
-                return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER).setFormData(formData).createLogin();
+                return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER)
+                        .setFormData(formData)
+                        .setAccessCode(clientCode.getCode())
+                        .createLogin();
             default:
                 event.error(Errors.INVALID_USER_CREDENTIALS);
-                return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER).setFormData(formData).createLogin();
+                return Flows.forms(this.session, realm, client, uriInfo).setError(Messages.INVALID_USER)
+                        .setFormData(formData)
+                        .setAccessCode(clientCode.getCode())
+                        .createLogin();
         }
     }
 
@@ -575,25 +599,44 @@ public class TokenService {
     /**
      * Registration
      *
-     * @param clientId
-     * @param scopeParam
-     * @param state
-     * @param redirect
+     * @param code
      * @param formData
      * @return
      */
     @Path("registrations")
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response processRegister(@QueryParam("client_id") final String clientId,
-                                    @QueryParam("scope") final String scopeParam, @QueryParam("state") final String state,
-                                    @QueryParam("redirect_uri") String redirect, final MultivaluedMap<String, String> formData) {
+    public Response processRegister(@QueryParam("code") String code,
+                                    final MultivaluedMap<String, String> formData) {
+        event.event(EventType.REGISTER);
+        if (!checkSsl()) {
+            event.error(Errors.SSL_REQUIRED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
+        }
+
+        if (!realm.isEnabled()) {
+            event.error(Errors.REALM_DISABLED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
+        }
+        if (!realm.isRegistrationAllowed()) {
+            event.error(Errors.REGISTRATION_DISABLED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed");
+        }
+        ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
+        if (clientCode == null) {
+            event.error(Errors.INVALID_CODE);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
+        }
+        if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) {
+            event.error(Errors.INVALID_CODE);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
+        }
 
         String username = formData.getFirst("username");
         String email = formData.getFirst("email");
-
-        event.event(EventType.REGISTER).client(clientId)
-                .detail(Details.REDIRECT_URI, redirect)
+        ClientSessionModel clientSession = clientCode.getClientSession();
+        event.client(clientSession.getClient())
+                .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
                 .detail(Details.RESPONSE_TYPE, "code")
                 .detail(Details.USERNAME, username)
                 .detail(Details.EMAIL, email)
@@ -603,29 +646,19 @@ public class TokenService {
 
         if (!realm.isEnabled()) {
             event.error(Errors.REALM_DISABLED);
-            return oauth.forwardToSecurityFailure("Realm not enabled");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled");
         }
-        ClientModel client = realm.findClient(clientId);
+        ClientModel client = clientSession.getClient();
         if (client == null) {
             event.error(Errors.CLIENT_NOT_FOUND);
-            return oauth.forwardToSecurityFailure("Unknown login requester.");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
         }
 
         if (!client.isEnabled()) {
             event.error(Errors.CLIENT_DISABLED);
-            return oauth.forwardToSecurityFailure("Login requester not enabled.");
-        }
-
-        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
-        if (redirect == null) {
-            event.error(Errors.INVALID_REDIRECT_URI);
-            return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
         }
 
-        if (!realm.isRegistrationAllowed()) {
-            event.error(Errors.REGISTRATION_DISABLED);
-            return oauth.forwardToSecurityFailure("Registration not allowed");
-        }
 
         List<String> requiredCredentialTypes = new LinkedList<String>();
         for (RequiredCredentialModel m : realm.getRequiredCredentials()) {
@@ -640,19 +673,31 @@ public class TokenService {
 
         if (error != null) {
             event.error(Errors.INVALID_REGISTRATION);
-            return Flows.forms(session, realm, client, uriInfo).setError(error).setFormData(formData).createRegistration();
+            return Flows.forms(session, realm, client, uriInfo)
+                    .setError(error)
+                    .setFormData(formData)
+                    .setAccessCode(clientCode.getCode())
+                    .createRegistration();
         }
 
         // Validate that user with this username doesn't exist in realm or any federation provider
         if (session.users().getUserByUsername(username, realm) != null) {
             event.error(Errors.USERNAME_IN_USE);
-            return Flows.forms(session, realm, client, uriInfo).setError(Messages.USERNAME_EXISTS).setFormData(formData).createRegistration();
+            return Flows.forms(session, realm, client, uriInfo)
+                    .setError(Messages.USERNAME_EXISTS)
+                    .setFormData(formData)
+                    .setAccessCode(clientCode.getCode())
+                    .createRegistration();
         }
 
         // Validate that user with this email doesn't exist in realm or any federation provider
         if (session.users().getUserByEmail(email, realm) != null) {
             event.error(Errors.EMAIL_IN_USE);
-            return Flows.forms(session, realm, client, uriInfo).setError(Messages.EMAIL_EXISTS).setFormData(formData).createRegistration();
+            return Flows.forms(session, realm, client, uriInfo)
+                    .setError(Messages.EMAIL_EXISTS)
+                    .setFormData(formData)
+                    .setAccessCode(clientCode.getCode())
+                    .createRegistration();
         }
 
         UserModel user = session.users().addUser(realm, username);
@@ -680,14 +725,17 @@ public class TokenService {
             // User already registered, but force him to update password
             if (!passwordUpdateSuccessful) {
                 user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
-                return Flows.forms(session, realm, client, uriInfo).setError(passwordUpdateError).createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
+                return Flows.forms(session, realm, client, uriInfo)
+                        .setError(passwordUpdateError)
+                        .setAccessCode(clientCode.getCode())
+                        .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
             }
         }
 
         event.user(user).success();
         event.reset();
 
-        return processLogin(clientId, scopeParam, state, redirect, formData);
+        return processLogin(code, formData);
     }
 
     /**
@@ -737,8 +785,7 @@ public class TokenService {
             event.error(Errors.INVALID_CODE);
             throw new BadRequestException("Code not specified", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
         }
-
-        AccessCode accessCode = AccessCode.parse(code, session, realm);
+        ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
         if (accessCode == null) {
             String[] parts = code.split("\\.");
             if (parts.length == 2) {
@@ -754,7 +801,8 @@ public class TokenService {
             return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
                     .build();
         }
-        event.detail(Details.CODE_ID, accessCode.getCodeId());
+        ClientSessionModel clientSession = accessCode.getClientSession();
+        event.detail(Details.CODE_ID, clientSession.getId());
         if (!accessCode.isValid(ClientSessionModel.Action.CODE_TO_TOKEN)) {
             Map<String, String> res = new HashMap<String, String>();
             res.put(OAuth2Constants.ERROR, "invalid_grant");
@@ -765,13 +813,13 @@ public class TokenService {
         }
 
         accessCode.setAction(null);
-
-        event.user(accessCode.getUser());
-        event.session(accessCode.getSessionState());
+        UserSessionModel userSession = clientSession.getUserSession();
+        event.user(userSession.getUser());
+        event.session(userSession.getId());
 
         ClientModel client = authorizeClient(authorizationHeader, formData, event);
 
-        if (!client.getClientId().equals(accessCode.getClient().getClientId())) {
+        if (!client.getClientId().equals(clientSession.getClient().getClientId())) {
             Map<String, String> res = new HashMap<String, String>();
             res.put(OAuth2Constants.ERROR, "invalid_grant");
             res.put(OAuth2Constants.ERROR_DESCRIPTION, "Auth error");
@@ -780,7 +828,7 @@ public class TokenService {
                     .build();
         }
 
-        UserModel user = session.users().getUserById(accessCode.getUser().getId(), realm);
+        UserModel user = session.users().getUserById(userSession.getUser().getId(), realm);
         if (user == null) {
             Map<String, String> res = new HashMap<String, String>();
             res.put(OAuth2Constants.ERROR, "invalid_grant");
@@ -799,7 +847,6 @@ public class TokenService {
                     .build();
         }
 
-        UserSessionModel userSession = session.sessions().getUserSession(realm, accessCode.getSessionState());
         if (!AuthenticationManager.isSessionValid(realm, userSession)) {
             AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection);
             Map<String, String> res = new HashMap<String, String>();
@@ -893,11 +940,106 @@ public class TokenService {
     }
 
     /**
+     * checks input and initializes variables based on a front page action like the login page or registration page
+     *
+     */
+    private class FrontPageInitializer {
+        protected String code;
+        protected String clientId;
+        protected String redirect;
+        protected String state;
+        protected String scopeParam;
+        protected String responseType;
+        protected String loginHint;
+        protected String prompt;
+        protected ClientSessionModel clientSession;
+
+        public Response processInput() {
+            if (code != null) {
+                event.detail(Details.CODE_ID, code);
+            } else {
+                event.client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
+            }
+            if (!checkSsl()) {
+                event.error(Errors.SSL_REQUIRED);
+                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
+            }
+            if (!realm.isEnabled()) {
+                event.error(Errors.REALM_DISABLED);
+                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled");
+            }
+
+            clientSession = null;
+            if (code != null) {
+                ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
+                if (clientCode == null) {
+                    event.error(Errors.INVALID_CODE);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
+                }
+                if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) {
+                    event.error(Errors.INVALID_CODE);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.");
+                }
+                clientSession = clientCode.getClientSession();
+                if (!clientSession.getAuthMethod().equals(OpenIdConnectProtocol.LOGIN_PAGE_PROTOCOL)) {
+                    event.error(Errors.INVALID_CODE);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid protocol, please login again through your application.");
+                }
+                state = clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM);
+                scopeParam = clientSession.getNote(OpenIdConnectProtocol.SCOPE_PARAM);
+                responseType = clientSession.getNote(OpenIdConnectProtocol.RESPONSE_TYPE_PARAM);
+                loginHint = clientSession.getNote(OpenIdConnectProtocol.LOGIN_HINT_PARAM);
+                prompt = clientSession.getNote(OpenIdConnectProtocol.PROMPT_PARAM);
+            } else {
+                if (state == null) {
+                    event.error(Errors.STATE_PARAM_NOT_FOUND);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid state param.");
+
+                }
+                ClientModel client = realm.findClient(clientId);
+                if (client == null) {
+                    event.error(Errors.CLIENT_NOT_FOUND);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
+                }
+
+                if (!client.isEnabled()) {
+                    event.error(Errors.CLIENT_DISABLED);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
+                }
+                if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
+                    event.error(Errors.NOT_ALLOWED);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login");
+                }
+                if (client.isDirectGrantsOnly()) {
+                    event.error(Errors.NOT_ALLOWED);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login");
+                }
+                redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
+                if (redirect == null) {
+                    event.error(Errors.INVALID_REDIRECT_URI);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.");
+                }
+                clientSession = session.sessions().createClientSession(realm, client);
+                clientSession.setAuthMethod(OpenIdConnectProtocol.LOGIN_PAGE_PROTOCOL);
+                clientSession.setRedirectUri(redirect);
+                clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
+                clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, state);
+                if (scopeParam != null) clientSession.setNote(OpenIdConnectProtocol.SCOPE_PARAM, scopeParam);
+                if (responseType != null) clientSession.setNote(OpenIdConnectProtocol.RESPONSE_TYPE_PARAM, responseType);
+                if (loginHint != null) clientSession.setNote(OpenIdConnectProtocol.LOGIN_HINT_PARAM, loginHint);
+                if (prompt != null) clientSession.setNote(OpenIdConnectProtocol.PROMPT_PARAM, prompt);
+            }
+            return null;
+        }
+    }
+
+    /**
      * Login page.  Must be redirected to from the application or oauth client.
      *
      * @See <a href="http://tools.ietf.org/html/rfc6749#section-4.1">http://tools.ietf.org/html/rfc6749#section-4.1</a>
      *
      *
+     * @param code
      * @param responseType
      * @param redirect
      * @param clientId
@@ -908,60 +1050,54 @@ public class TokenService {
      */
     @Path("login")
     @GET
-    public Response loginPage(final @QueryParam("response_type") String responseType,
-                              @QueryParam("redirect_uri") String redirect, final @QueryParam("client_id") String clientId,
-                              final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state, final @QueryParam("prompt") String prompt,
-                              final @QueryParam("login_hint") String loginHint) {
-        event.event(EventType.LOGIN).client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
-
-        OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
+    public Response loginPage(@QueryParam("code") String code,
+                              @QueryParam(OpenIdConnectProtocol.RESPONSE_TYPE_PARAM) String responseType,
+                              @QueryParam(OpenIdConnectProtocol.REDIRECT_URI_PARAM) String redirect,
+                              @QueryParam(OpenIdConnectProtocol.CLIENT_ID_PARAM) String clientId,
+                              @QueryParam(OpenIdConnectProtocol.SCOPE_PARAM) String scopeParam,
+                              @QueryParam(OpenIdConnectProtocol.STATE_PARAM) String state,
+                              @QueryParam(OpenIdConnectProtocol.PROMPT_PARAM) String prompt,
+                              @QueryParam(OpenIdConnectProtocol.LOGIN_HINT_PARAM) String loginHint) {
+        event.event(EventType.LOGIN);
+        FrontPageInitializer pageInitializer = new FrontPageInitializer();
+        pageInitializer.code = code;
+        pageInitializer.responseType = responseType;
+        pageInitializer.redirect = redirect;
+        pageInitializer.clientId = clientId;
+        pageInitializer.scopeParam = scopeParam;
+        pageInitializer.state = state;
+        pageInitializer.prompt = prompt;
+        pageInitializer.loginHint = loginHint;
+        Response response = pageInitializer.processInput();
+        if (response != null) return response;
+        ClientSessionModel clientSession = pageInitializer.clientSession;
+        code = pageInitializer.code;
+        responseType = pageInitializer.responseType;
+        redirect = pageInitializer.redirect;
+        clientId = pageInitializer.clientId ;
+        scopeParam = pageInitializer.scopeParam;
+        state = pageInitializer.state;
+        prompt = pageInitializer.prompt;
+        loginHint = pageInitializer.loginHint;
 
-        if (!checkSsl()) {
-            return oauth.forwardToSecurityFailure("HTTPS required");
-        }
-
-        if (!realm.isEnabled()) {
-            event.error(Errors.REALM_DISABLED);
-            return oauth.forwardToSecurityFailure("Realm not enabled");
-        }
-        ClientModel client = realm.findClient(clientId);
-        if (client == null) {
-            event.error(Errors.CLIENT_NOT_FOUND);
-            return oauth.forwardToSecurityFailure("Unknown login requester.");
-        }
 
-        if (!client.isEnabled()) {
-            event.error(Errors.CLIENT_DISABLED);
-            return oauth.forwardToSecurityFailure("Login requester not enabled.");
-        }
-        if ( (client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
-            event.error(Errors.NOT_ALLOWED);
-            return oauth.forwardToSecurityFailure("Bearer-only applications are not allowed to initiate browser login");
-        }
-        if (client.isDirectGrantsOnly()) {
-            event.error(Errors.NOT_ALLOWED);
-            return oauth.forwardToSecurityFailure("direct-grants-only clients are not allowed to initiate browser login");
-        }
-        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
-        if (redirect == null) {
-            event.error(Errors.INVALID_REDIRECT_URI);
-            return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
-        }
+        OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
 
         AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers);
         if (authResult != null) {
             UserModel user = authResult.getUser();
             UserSessionModel session = authResult.getSession();
-
+            TokenManager.attachClientSession(session, clientSession);
             event.user(user).session(session).detail(Details.AUTH_METHOD, "sso");
-            return oauth.processAccessCode(scopeParam, state, redirect, client, user, session, event);
+            return oauth.processAccessCode(clientSession, session, event);
         }
 
         if (prompt != null && prompt.equals("none")) {
-            return oauth.redirectError(client, "access_denied", state, redirect);
+            return oauth.redirectError(clientSession.getClient(), "access_denied", state, redirect);
         }
 
-        LoginFormsProvider forms = Flows.forms(session, realm, client, uriInfo);
+        LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
+                .setAccessCode(new ClientSessionCode(realm, clientSession).getCode());
 
         String rememberMeUsername = null;
         if (realm.isRememberMe()) {
@@ -999,46 +1135,35 @@ public class TokenService {
      */
     @Path("registrations")
     @GET
-    public Response registerPage(final @QueryParam("response_type") String responseType,
-                                 @QueryParam("redirect_uri") String redirect, final @QueryParam("client_id") String clientId,
-                                 final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) {
-        event.event(EventType.REGISTER).client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
-
-        OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
-
-        if (!checkSsl()) {
-            return oauth.forwardToSecurityFailure("HTTPS required");
-        }
-
-        if (!realm.isEnabled()) {
-            event.error(Errors.REALM_DISABLED);
-            return oauth.forwardToSecurityFailure("Realm not enabled");
-        }
-        ClientModel client = realm.findClient(clientId);
-        if (client == null) {
-            event.error(Errors.CLIENT_NOT_FOUND);
-            return oauth.forwardToSecurityFailure("Unknown login requester.");
-        }
-
-        if (!client.isEnabled()) {
-            event.error(Errors.CLIENT_DISABLED);
-            return oauth.forwardToSecurityFailure("Login requester not enabled.");
-        }
-
-        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
-        if (redirect == null) {
-            event.error(Errors.INVALID_REDIRECT_URI);
-            return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
-        }
-
+    public Response registerPage(@QueryParam("code") String code,
+                                 @QueryParam("response_type") String responseType,
+                                 @QueryParam(OpenIdConnectProtocol.REDIRECT_URI_PARAM) String redirect,
+                                 @QueryParam(OpenIdConnectProtocol.CLIENT_ID_PARAM) String clientId,
+                                 @QueryParam("scope") String scopeParam,
+                                 @QueryParam("state") String state) {
+        event.event(EventType.REGISTER);
         if (!realm.isRegistrationAllowed()) {
             event.error(Errors.REGISTRATION_DISABLED);
-            return oauth.forwardToSecurityFailure("Registration not allowed");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed");
         }
 
+        FrontPageInitializer pageInitializer = new FrontPageInitializer();
+        pageInitializer.code = code;
+        pageInitializer.responseType = responseType;
+        pageInitializer.redirect = redirect;
+        pageInitializer.clientId = clientId;
+        pageInitializer.scopeParam = scopeParam;
+        pageInitializer.state = state;
+        Response response = pageInitializer.processInput();
+        if (response != null) return response;
+        ClientSessionModel clientSession = pageInitializer.clientSession;
+
+
         authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
 
-        return Flows.forms(session, realm, client, uriInfo).createRegistration();
+        return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
+                .setAccessCode(new ClientSessionCode(realm, clientSession).getCode())
+                .createRegistration();
     }
 
     /**
@@ -1050,7 +1175,7 @@ public class TokenService {
     @Path("logout")
     @GET
     @NoCache
-    public Response logout(final @QueryParam("redirect_uri") String redirectUri) {
+    public Response logout(final @QueryParam(OpenIdConnectProtocol.REDIRECT_URI_PARAM) String redirectUri) {
         event.event(EventType.LOGOUT);
         if (redirectUri != null) {
             event.detail(Details.REDIRECT_URI, redirectUri);
@@ -1065,7 +1190,7 @@ public class TokenService {
             String validatedRedirect = verifyRealmRedirectUri(uriInfo, redirectUri, realm);
             if (validatedRedirect == null) {
                 OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
-                return oauth.forwardToSecurityFailure("Invalid redirect uri.");
+                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.");
             }
             return Response.status(302).location(UriBuilder.fromUri(validatedRedirect).build()).build();
         } else {
@@ -1145,27 +1270,27 @@ public class TokenService {
         OAuthFlows oauth = Flows.oauth(session, realm, request, uriInfo, clientConnection, authManager, tokenManager);
 
         if (!checkSsl()) {
-            return oauth.forwardToSecurityFailure("HTTPS required");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
         }
 
         String code = formData.getFirst(OAuth2Constants.CODE);
 
-        AccessCode accessCode = AccessCode.parse(code, session, realm);
+        ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
         if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT)) {
             event.error(Errors.INVALID_CODE);
-            return oauth.forwardToSecurityFailure("Invalid access code.");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid access code.");
         }
-        event.detail(Details.CODE_ID, accessCode.getCodeId());
+        ClientSessionModel clientSession = accessCode.getClientSession();
+        event.detail(Details.CODE_ID, clientSession.getId());
 
-        String redirect = accessCode.getRedirectUri();
-        String state = accessCode.getState();
+        String redirect = clientSession.getRedirectUri();
 
-        event.client(accessCode.getClient())
-                .user(accessCode.getUser())
+        event.client(clientSession.getClient())
+                .user(clientSession.getUserSession().getUser())
                 .detail(Details.RESPONSE_TYPE, "code")
                 .detail(Details.REDIRECT_URI, redirect);
 
-        UserSessionModel userSession = session.sessions().getUserSession(realm, accessCode.getSessionState());
+        UserSessionModel userSession = clientSession.getUserSession();
         if (userSession != null) {
             event.detail(Details.AUTH_METHOD, userSession.getAuthMethod());
             event.detail(Details.USERNAME, userSession.getLoginUsername());
@@ -1177,19 +1302,19 @@ public class TokenService {
         if (!AuthenticationManager.isSessionValid(realm, userSession)) {
             AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection);
             event.error(Errors.INVALID_CODE);
-            return oauth.forwardToSecurityFailure("Session not active");
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Session not active");
         }
         event.session(userSession);
 
         if (formData.containsKey("cancel")) {
             event.error(Errors.REJECTED_BY_USER);
-            return redirectAccessDenied(redirect, state);
+            return redirectAccessDenied(redirect, clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM));
         }
 
         event.success();
 
         accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
-        return oauth.redirectAccessCode(accessCode, userSession, state, redirect);
+        return oauth.redirectAccessCode(accessCode, userSession, clientSession.getNote(OpenIdConnectProtocol.STATE_PARAM), redirect);
     }
 
     @Path("oauth/oob")
diff --git a/social/core/pom.xml b/social/core/pom.xml
index 895237e..cf11cff 100755
--- a/social/core/pom.xml
+++ b/social/core/pom.xml
@@ -21,6 +21,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.codehaus.jackson</groupId>
             <artifactId>jackson-core-asl</artifactId>
             <scope>provided</scope>
diff --git a/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java b/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java
index 43eefc1..161d1b4 100755
--- a/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java
+++ b/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java
@@ -2,6 +2,7 @@ package org.keycloak.social;
 
 import org.codehaus.jackson.map.ObjectMapper;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.social.utils.SimpleHttp;
 
 import java.io.IOException;
@@ -43,7 +44,7 @@ public abstract class AbstractOAuth2Provider implements SocialProvider {
     protected abstract SocialUser getProfile(String accessToken) throws SocialProviderException;
 
     @Override
-    public AuthRequest getAuthUrl(SocialProviderConfig config, String state) throws SocialProviderException {
+    public AuthRequest getAuthUrl(ClientSessionModel clientSession, SocialProviderConfig config, String state) throws SocialProviderException {
         return AuthRequest.create(getAuthUrl())
                 .setQueryParam(CLIENT_ID, config.getKey())
                 .setQueryParam(RESPONSE_TYPE, CODE)
@@ -54,7 +55,7 @@ public abstract class AbstractOAuth2Provider implements SocialProvider {
     }
 
     @Override
-    public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
+    public SocialUser processCallback(ClientSessionModel clientSession, SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
         String error = callback.getQueryParam(OAuth2Constants.ERROR);
         if (error != null) {
             if (error.equals("access_denied")) {
diff --git a/social/core/src/main/java/org/keycloak/social/AuthCallback.java b/social/core/src/main/java/org/keycloak/social/AuthCallback.java
old mode 100644
new mode 100755
index ebb1660..58aee5b
--- a/social/core/src/main/java/org/keycloak/social/AuthCallback.java
+++ b/social/core/src/main/java/org/keycloak/social/AuthCallback.java
@@ -29,11 +29,9 @@ import java.util.Map;
 public class AuthCallback {
 
     private Map<String, String[]> queryParams;
-    private Map<String, String> attributes;
 
-    public AuthCallback(Map<String, String[]> queryParams, Map<String, String> attributes) {
+    public AuthCallback(Map<String, String[]> queryParams) {
         this.queryParams = queryParams;
-        this.attributes = attributes;
     }
 
     public String getQueryParam(String name) {
@@ -44,8 +42,5 @@ public class AuthCallback {
         return null;
     }
 
-    public String getAttribute(String name) {
-        return attributes != null ? attributes.get(name) : null;
-    }
 
 }
diff --git a/social/core/src/main/java/org/keycloak/social/AuthRequest.java b/social/core/src/main/java/org/keycloak/social/AuthRequest.java
old mode 100644
new mode 100755
index ee89fbe..1efce35
--- a/social/core/src/main/java/org/keycloak/social/AuthRequest.java
+++ b/social/core/src/main/java/org/keycloak/social/AuthRequest.java
@@ -35,8 +35,6 @@ public class AuthRequest {
 
     private URI authUri;
 
-    private Map<String, String> attributes;
-
     public static AuthRequestBuilder create(String url) {
         AuthRequestBuilder req = new AuthRequestBuilder();
 
@@ -46,27 +44,20 @@ public class AuthRequest {
         return req;
     }
 
-    private AuthRequest(URI authUri, Map<String, String> attributes) {
+    private AuthRequest(URI authUri) {
         this.authUri = authUri;
-        this.attributes = attributes;
     }
 
     public URI getAuthUri() {
         return authUri;
     }
 
-    public Map<String, String> getAttributes() {
-        return attributes;
-    }
-
     public static class AuthRequestBuilder {
 
         private StringBuilder b;
 
         private char sep;
 
-        private Map<String, String> attributes;
-
         private String id;
 
         private AuthRequestBuilder() {
@@ -90,17 +81,9 @@ public class AuthRequest {
             }
         }
 
-        public AuthRequestBuilder setAttribute(String name, String value) {
-            if (attributes == null) {
-                attributes = new HashMap<String, String>();
-            }
-            attributes.put(name, value);
-            return this;
-        }
-
         public AuthRequest build() {
             try {
-                return new AuthRequest(new URI(b.toString()), attributes);
+                return new AuthRequest(new URI(b.toString()));
             } catch (URISyntaxException e) {
                 throw new IllegalArgumentException(e);
             }
diff --git a/social/core/src/main/java/org/keycloak/social/SocialProvider.java b/social/core/src/main/java/org/keycloak/social/SocialProvider.java
old mode 100644
new mode 100755
index b098077..1f90622
--- a/social/core/src/main/java/org/keycloak/social/SocialProvider.java
+++ b/social/core/src/main/java/org/keycloak/social/SocialProvider.java
@@ -22,6 +22,8 @@
 package org.keycloak.social;
 
 
+import org.keycloak.models.ClientSessionModel;
+
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
@@ -29,10 +31,10 @@ public interface SocialProvider {
 
     String getId();
 
-    AuthRequest getAuthUrl(SocialProviderConfig config, String state) throws SocialProviderException;
+    AuthRequest getAuthUrl(ClientSessionModel clientSession, SocialProviderConfig config, String state) throws SocialProviderException;
 
     String getName();
 
-    SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException;
+    SocialUser processCallback(ClientSessionModel clientSession, SocialProviderConfig config, AuthCallback callback) throws SocialProviderException;
 
 }
diff --git a/social/facebook/pom.xml b/social/facebook/pom.xml
index bbf264c..69c48c0 100755
--- a/social/facebook/pom.xml
+++ b/social/facebook/pom.xml
@@ -16,6 +16,12 @@
     <dependencies>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-social-core</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
diff --git a/social/github/pom.xml b/social/github/pom.xml
index 8746269..351f4b1 100755
--- a/social/github/pom.xml
+++ b/social/github/pom.xml
@@ -16,6 +16,12 @@
     <dependencies>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-social-core</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
diff --git a/social/google/pom.xml b/social/google/pom.xml
index 4f2bfb5..a1ca189 100755
--- a/social/google/pom.xml
+++ b/social/google/pom.xml
@@ -16,6 +16,12 @@
     <dependencies>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-social-core</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
diff --git a/social/twitter/pom.xml b/social/twitter/pom.xml
index 6017024..dd3f14e 100755
--- a/social/twitter/pom.xml
+++ b/social/twitter/pom.xml
@@ -16,6 +16,12 @@
     <dependencies>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-social-core</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java
index 7a6c88b..0fd4ee3 100755
--- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java
@@ -21,6 +21,7 @@
  */
 package org.keycloak.social.twitter;
 
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.social.AuthCallback;
 import org.keycloak.social.AuthRequest;
 import org.keycloak.social.SocialAccessDeniedException;
@@ -45,7 +46,7 @@ public class TwitterProvider implements SocialProvider {
     }
 
     @Override
-    public AuthRequest getAuthUrl(SocialProviderConfig config, String state) throws SocialProviderException {
+    public AuthRequest getAuthUrl(ClientSessionModel clientSession, SocialProviderConfig config, String state) throws SocialProviderException {
         try {
             Twitter twitter = new TwitterFactory().getInstance();
             twitter.setOAuthConsumer(config.getKey(), config.getSecret());
@@ -53,8 +54,9 @@ public class TwitterProvider implements SocialProvider {
             URI uri = new URI(config.getCallbackUrl() + "?state=" + state);
 
             RequestToken requestToken = twitter.getOAuthRequestToken(uri.toString());
+            clientSession.setNote("twitter_token", requestToken.getToken());
+            clientSession.setNote("twitter_tokenSecret", requestToken.getTokenSecret());
             return AuthRequest.create(requestToken.getAuthenticationURL())
-                    .setAttribute("token", requestToken.getToken()).setAttribute("tokenSecret", requestToken.getTokenSecret())
                     .build();
         } catch (Exception e) {
             throw new SocialProviderException(e);
@@ -67,7 +69,7 @@ public class TwitterProvider implements SocialProvider {
     }
 
     @Override
-    public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
+    public SocialUser processCallback(ClientSessionModel clientSession, SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
         if (callback.getQueryParam("denied") != null) {
             throw new SocialAccessDeniedException();
         }
@@ -78,8 +80,10 @@ public class TwitterProvider implements SocialProvider {
 
             String token = callback.getQueryParam("oauth_token");
             String verifier = callback.getQueryParam("oauth_verifier");
+            String twitterToken = clientSession.getNote("twitter_token");
+            String twitterSecret = clientSession.getNote("twitter_tokenSecret");
 
-            RequestToken requestToken = new RequestToken((String)callback.getAttribute("token"), (String)callback.getAttribute("tokenSecret"));
+            RequestToken requestToken = new RequestToken(twitterToken, twitterSecret);
 
             twitter.getOAuthAccessToken(requestToken, verifier);
             twitter4j.User twitterUser = twitter.verifyCredentials();
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 e22a630..3eb3dae 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
@@ -140,6 +140,7 @@ public class AccountTest {
 
     @Before
     public void before() {
+        oauth.state("mystate"); // keycloak enforces that a state param has been sent by client
         userId = keycloakRule.getUser("test", "test-user@localhost").getId();
     }
 
@@ -160,6 +161,16 @@ public class AccountTest {
     }
 
     @Test
+    @Ignore
+    public void runit() throws Exception {
+        Thread.sleep(10000000);
+
+    }
+
+
+
+
+    @Test
     public void returnToAppFromQueryParam() {
         driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app");
         loginPage.login("test-user@localhost", "password");
@@ -446,6 +457,7 @@ public class AccountTest {
         // Create second session
         WebDriver driver2 = WebRule.createWebDriver();
         OAuthClient oauth2 = new OAuthClient(driver2);
+        oauth2.state("mystate");
         oauth2.doLogin("view-sessions", "password");
 
         Event login2Event = events.expectLogin().user(userId).detail(Details.USERNAME, "view-sessions").assertEvent();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
index 8fe4d72..f2b8b26 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
@@ -88,6 +88,7 @@ public class RequiredActionEmailVerificationTest {
 
     @Before
     public void before() {
+        oauth.state("mystate"); // have to set this as keycloak validates that state is sent
         keycloakRule.configure(new KeycloakSetup() {
 
             @Override
@@ -115,7 +116,8 @@ public class RequiredActionEmailVerificationTest {
         String body = (String) message.getContent();
         String verificationUrl = MailUtil.getLink(body);
 
-        Event sendEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).detail("email", "test-user@localhost").assertEvent();
+        AssertEvents.ExpectedEvent emailEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).detail("email", "test-user@localhost");
+        Event sendEvent = emailEvent.assertEvent();
         String sessionId = sendEvent.getSessionId();
 
         String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java
index a512795..26979ee 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java
@@ -22,6 +22,7 @@
 package org.keycloak.testsuite.actions;
 
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
@@ -86,6 +87,12 @@ public class RequiredActionResetPasswordTest {
     @WebResource
     protected LoginPasswordUpdatePage changePasswordPage;
 
+    @Before
+    public void before() {
+        oauth.state("mystate"); // have to set this as keycloak validates that state is sent
+    }
+
+
     @Test
     public void tempPassword() throws Exception {
         loginPage.open();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
index 0dd893b..fc9b9fe 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -124,6 +124,15 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
                 .session(isUUID());
     }
 
+    public ExpectedEvent expectSocialLogin() {
+        return expect(EventType.SOCIAL_LOGIN)
+                .detail(Details.CODE_ID, isCodeId())
+                .detail(Details.USERNAME, DEFAULT_USERNAME)
+                .detail(Details.AUTH_METHOD, "form")
+                .detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI)
+                .session(isUUID());
+    }
+
     public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
         return expect(EventType.CODE_TO_TOKEN)
                 .detail(Details.CODE_ID, codeId)
@@ -277,6 +286,11 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
             return this;
         }
 
+        public ExpectedEvent clearDetails() {
+            if (details != null) details.clear();
+            return this;
+        }
+
         public ExpectedEvent error(String error) {
             expected.setError(error);
             return this;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java
index f473cb9..154bd19 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java
@@ -1,6 +1,7 @@
 package org.keycloak.testsuite;
 
 import org.keycloak.OAuth2Constants;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.social.AuthCallback;
 import org.keycloak.social.AuthRequest;
 import org.keycloak.social.SocialAccessDeniedException;
@@ -19,7 +20,7 @@ public class DummySocial implements SocialProvider {
     }
 
     @Override
-    public AuthRequest getAuthUrl(SocialProviderConfig config, String state) throws SocialProviderException {
+    public AuthRequest getAuthUrl(ClientSessionModel clientSession, SocialProviderConfig config, String state) throws SocialProviderException {
         return AuthRequest.create(AUTH_PATH)
                 .setQueryParam(OAuth2Constants.RESPONSE_TYPE, "token")
                 .setQueryParam(OAuth2Constants.REDIRECT_URI, config.getCallbackUrl())
@@ -32,7 +33,7 @@ public class DummySocial implements SocialProvider {
     }
 
     @Override
-    public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
+    public SocialUser processCallback(ClientSessionModel clientSession, SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
         String error = callback.getQueryParam(OAuth2Constants.ERROR);
         if (error != null) {
             throw new SocialAccessDeniedException();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
index 78b5249..8a922af 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
@@ -145,6 +145,10 @@ public class LoginTotpTest {
     @Test
     public void loginWithTotpExpiredPasswordToken() throws Exception {
         try {
+
+            loginPage.open();
+            loginPage.login("test-user@localhost", "password");
+
             keycloakRule.configure(new KeycloakSetup() {
                 @Override
                 public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
@@ -153,9 +157,6 @@ public class LoginTotpTest {
                 }
             });
 
-            loginPage.open();
-            loginPage.login("test-user@localhost", "password");
-
             loginTotpPage.assertCurrent();
 
             Thread.sleep(2000);
@@ -165,7 +166,11 @@ public class LoginTotpTest {
             loginPage.assertCurrent();
             Assert.assertEquals("Invalid username or password.", loginPage.getError());
 
-            events.expectLogin().error("invalid_user_credentials").removeDetail(Details.CODE_ID).session((String) null).assertEvent();
+            AssertEvents.ExpectedEvent expectedEvent = events.expectLogin().error("invalid_user_credentials")
+                    .user((String)null)
+                    .clearDetails()
+                    .session((String) null);
+            expectedEvent.assertEvent();
         } finally {
             keycloakRule.configure(new KeycloakSetup() {
                 @Override
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 f2c168c..9cda36b 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
@@ -10,6 +10,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.services.protocol.OpenIdConnectProtocol;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.util.Time;
 
@@ -73,7 +74,7 @@ public class UserSessionProviderTest {
         assertEquals(realm.findClient("test-app").getClientId(), session.getClient().getClientId());
         assertEquals(sessions[0].getId(), session.getUserSession().getId());
         assertEquals("http://redirect", session.getRedirectUri());
-        assertEquals("state", session.getState());
+        assertEquals("state", session.getNote(OpenIdConnectProtocol.STATE_PARAM));
         assertEquals(2, session.getRoles().size());
         assertTrue(session.getRoles().contains("one"));
         assertTrue(session.getRoles().contains("two"));
@@ -245,7 +246,11 @@ public class UserSessionProviderTest {
         for (int i = 0; i < 25; i++) {
             UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false);
             userSession.setStarted(Time.currentTime() + i);
-            session.sessions().createClientSession(realm, realm.findClient("test-app"), userSession, "http://redirect", "state", new HashSet<String>());
+            ClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.findClient("test-app"));
+            clientSession.setUserSession(userSession);
+            clientSession.setRedirectUri("http://redirect");
+            clientSession.setRoles(new HashSet<String>());
+            clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, "state");
         }
 
         resetSession();
@@ -280,6 +285,15 @@ public class UserSessionProviderTest {
         assertEquals(1, session.sessions().getActiveUserSessions(realm, realm.findClient("third-party")));
     }
 
+    private ClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles) {
+        ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
+        if (userSession != null) clientSession.setUserSession(userSession);
+        clientSession.setRedirectUri(redirect);
+        if (state != null) clientSession.setNote(OpenIdConnectProtocol.STATE_PARAM, state);
+        if (roles != null) clientSession.setRoles(roles);
+        return clientSession;
+    }
+
     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);
@@ -288,14 +302,14 @@ public class UserSessionProviderTest {
         roles.add("one");
         roles.add("two");
 
-        session.sessions().createClientSession(realm, realm.findClient("test-app"), sessions[0], "http://redirect", "state", roles);
-        session.sessions().createClientSession(realm, realm.findClient("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>());
+        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);
-        session.sessions().createClientSession(realm, realm.findClient("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>());
+        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);
-        session.sessions().createClientSession(realm, realm.findClient("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>());
+        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 98dd5ac..410b012 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
@@ -135,7 +135,8 @@ public class AccessTokenTest {
         AccessTokenResponse response = oauth.doAccessTokenRequest(code, "invalid");
         Assert.assertEquals(400, response.getStatusCode());
 
-        events.expectCodeToToken(codeId, loginEvent.getSessionId()).error("invalid_client_credentials").removeDetail(Details.TOKEN_ID).removeDetail(Details.REFRESH_TOKEN_ID).assertEvent();
+        AssertEvents.ExpectedEvent expectedEvent = events.expectCodeToToken(codeId, loginEvent.getSessionId()).error("invalid_client_credentials").removeDetail(Details.TOKEN_ID).removeDetail(Details.REFRESH_TOKEN_ID);
+        expectedEvent.assertEvent();
     }
 
     @Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
index 2ca2021..a70cc85 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
@@ -22,18 +22,23 @@
 package org.keycloak.testsuite.oauth;
 
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventType;
 import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
 import org.keycloak.services.managers.AccessCode;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.OAuthClient.AuthorizationCodeResponse;
+import org.keycloak.testsuite.pages.ErrorPage;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
@@ -65,6 +70,9 @@ public class AuthorizationCodeTest {
     @WebResource
     protected LoginPage loginPage;
 
+    @WebResource
+    protected ErrorPage errorPage;
+
     @Rule
     public AssertEvents events = new AssertEvents(keycloakRule);
 
@@ -103,7 +111,7 @@ public class AuthorizationCodeTest {
         String code = driver.findElement(By.id(OAuth2Constants.CODE)).getText();
         keycloakRule.verifyCode(code);
 
-        String codeId = events.expectLogin().detail(Details.REDIRECT_URI, Constants.INSTALLED_APP_URN).assertEvent().getDetails().get(Details.CODE_ID);
+        String codeId = events.expectLogin().detail(Details.REDIRECT_URI, "http://localhost:8081/auth/realms/test/tokens/oauth/oob").assertEvent().getDetails().get(Details.CODE_ID);
         assertCode(codeId, code);
 
         keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@@ -133,7 +141,10 @@ public class AuthorizationCodeTest {
         String error = driver.findElement(By.id(OAuth2Constants.ERROR)).getText();
         assertEquals("access_denied", error);
 
-        events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).detail(Details.REDIRECT_URI, Constants.INSTALLED_APP_URN).assertEvent().getDetails().get(Details.CODE_ID);
+        events.expectLogin().error("rejected_by_user").user((String) null).session((String) null)
+                .removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID)
+                .detail(Details.REDIRECT_URI, "http://localhost:8081/auth/realms/test/tokens/oauth/oob")
+                .assertEvent().getDetails().get(Details.CODE_ID);
 
         keycloakRule.update(new KeycloakRule.KeycloakSetup() {
             @Override
@@ -167,22 +178,22 @@ public class AuthorizationCodeTest {
 
     @Test
     public void authorizationRequestNoState() throws IOException {
-        AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
-
-        Assert.assertTrue(response.isRedirected());
-        Assert.assertNotNull(response.getCode());
-        Assert.assertNull(response.getState());
-        Assert.assertNull(response.getError());
-
-        keycloakRule.verifyCode(response.getCode());
-
-        String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
-        assertCode(codeId, response.getCode());
+        oauth.state(null);
+        oauth.openLoginForm();
+        Assert.assertTrue(errorPage.isCurrent());
+
+        events.expect(EventType.LOGIN_ERROR)
+                .error(Errors.STATE_PARAM_NOT_FOUND)
+                .detail(Details.RESPONSE_TYPE, "code")
+                .detail(Details.REDIRECT_URI, oauth.getRedirectUri())
+                .user((String)null)
+                .assertEvent();
+        //assertCode(codeId, response.getCode());
     }
 
     private void assertCode(String expectedCodeId, String actualCode) {
-        AccessCode code = keycloakRule.verifyCode(actualCode);
-        assertEquals(expectedCodeId, code.getCodeId());
+        ClientSessionCode code = keycloakRule.verifyCode(actualCode);
+        assertEquals(expectedCodeId, code.getClientSession().getId());
     }
 
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index a674407..33d02c9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -75,7 +75,7 @@ public class OAuthClient {
 
     private String redirectUri = "http://localhost:8081/app/auth";
 
-    private String state;
+    private String state = "mystate";
 
     private PublicKey realmPublicKey;
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java
index 686b785..887762f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/perf/AccessTokenPerfTest.java
@@ -55,6 +55,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -163,21 +165,24 @@ public class AccessTokenPerfTest {
             return b.build(realm).toString();
         }
 
+        static Pattern actionParser = Pattern.compile("action=\"([^\"]+)\"");
 
-        @Override
         public void run() {
             //this.client = new ResteasyClientBuilder().build();
             String state = "42";
-            Response response = client.target(getLoginFormUrl(state)).request().get();
-            URI uri = null;
-            if (200 == response.getStatus()) {
-                response.close();
-                Form form = new Form();
-                form.param("username", "test-user@localhost");
-                form.param("password", "password");
-                response = client.target(getProcessLoginUrl(state)).request().post(Entity.form(form));
-
+            String loginFormUrl = getLoginFormUrl(state);
+            String html = client.target(loginFormUrl).request().get(String.class);
+            Matcher matcher = actionParser.matcher(html);
+            matcher.find();
+            String actionUrl = matcher.group(1);
+            if (!actionUrl.startsWith("http")) {
+                actionUrl = UriBuilder.fromUri(actionUrl).scheme("http").host("localhost").port(8081).build().toString();
             }
+            Form form = new Form();
+            form.param("username", "test-user@localhost");
+            form.param("password", "password");
+            Response response = client.target(actionUrl).request().post(Entity.form(form));
+            URI uri = null;
             Assert.assertEquals(302, response.getStatus());
             uri = response.getLocation();
             for (String header : response.getHeaders().keySet()) {
@@ -191,7 +196,7 @@ public class AccessTokenPerfTest {
             String code = getCode(uri);
             Assert.assertNotNull(code);
 
-            Form form = new Form();
+            form = new Form();
             form.param(OAuth2Constants.GRANT_TYPE, grantType)
                     .param(OAuth2Constants.CODE, code)
                     .param(OAuth2Constants.REDIRECT_URI, redirectUri);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
index f108a63..33dc6eb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
@@ -27,6 +27,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.services.managers.AccessCode;
+import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.ApplicationServlet;
 
@@ -109,12 +110,12 @@ public class KeycloakRule extends AbstractKeycloakRule {
         stopSession(session, true);
     }
 
-    public AccessCode verifyCode(String code) {
+    public ClientSessionCode verifyCode(String code) {
         KeycloakSession session = startSession();
         try {
             RealmModel realm = session.realms().getRealm("test");
             try {
-                AccessCode accessCode = AccessCode.parse(code, session, realm);
+                ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
                 if (accessCode == null) {
                     Assert.fail("Invalid code");
                 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
index f81b160..658512e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
@@ -114,14 +114,17 @@ public class SocialLoginTest {
         String userId = events.expect(EventType.REGISTER)
                 .user(AssertEvents.isUUID())
                 .detail(Details.EMAIL, "bob@builder.com")
-                .detail(Details.RESPONSE_TYPE, "code")
                 .detail(Details.REGISTER_METHOD, "social@dummy")
                 .detail(Details.REDIRECT_URI, AssertEvents.DEFAULT_REDIRECT_URI)
                 .detail(Details.USERNAME, "1@dummy")
                 .session((String) null)
                 .assertEvent().getUserId();
 
-        Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "1@dummy").detail(Details.AUTH_METHOD, "social@dummy").assertEvent();
+        Event loginEvent = events.expectSocialLogin()
+                .user(userId)
+                .detail(Details.USERNAME, "1@dummy")
+                .detail(Details.AUTH_METHOD, "social@dummy")
+                .assertEvent();
 
         String sessionId = loginEvent.getSessionId();
         String codeId = loginEvent.getDetails().get(Details.CODE_ID);
@@ -153,7 +156,7 @@ public class SocialLoginTest {
         driver.findElement(By.id("username")).sendKeys("dummy-user1");
         driver.findElement(By.id("login")).click();
 
-        events.expectLogin().user(userId).detail(Details.USERNAME, "1@dummy").detail(Details.AUTH_METHOD, "social@dummy").assertEvent();
+        events.expectSocialLogin().user(userId).detail(Details.USERNAME, "1@dummy").detail(Details.AUTH_METHOD, "social@dummy").assertEvent();
     }
 
     @Test
@@ -199,8 +202,9 @@ public class SocialLoginTest {
         Assert.assertTrue(loginPage.isCurrent());
         Assert.assertEquals("Access denied", loginPage.getWarning());
 
-        events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).detail(Details.AUTH_METHOD, "social@dummy").removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).assertEvent();
+        events.expectSocialLogin().error("rejected_by_user").user((String) null).session((String) null).detail(Details.AUTH_METHOD, "social@dummy").removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).assertEvent();
 
+        String src = driver.getPageSource();
         loginPage.login("test-user@localhost", "password");
 
         Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
@@ -238,7 +242,6 @@ public class SocialLoginTest {
             String userId = events.expect(EventType.REGISTER)
                     .user(AssertEvents.isUUID())
                     .detail(Details.EMAIL, "bob@builder.com")
-                    .detail(Details.RESPONSE_TYPE, "code")
                     .detail(Details.REGISTER_METHOD, "social@dummy")
                     .detail(Details.REDIRECT_URI, AssertEvents.DEFAULT_REDIRECT_URI)
                     .detail(Details.USERNAME, "2@dummy")