keycloak-aplcache
Changes
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java 13(+11 -2)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java 87(+85 -2)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java 48(+35 -13)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionNoteEntity.java 109(+109 -0)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionRoleEntity.java 7(+4 -3)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/JpaUserSessionProvider.java 56(+35 -21)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java 56(+52 -4)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java 34(+25 -9)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java 40(+29 -11)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java 63(+54 -9)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java 48(+42 -6)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoUserSessionEntity.java 13(+10 -3)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/MongoUserSessionProvider.java 86(+39 -47)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/UserSessionAdapter.java 6(+4 -2)
services/pom.xml 5(+5 -0)
social/core/pom.xml 6(+6 -0)
social/facebook/pom.xml 6(+6 -0)
social/github/pom.xml 6(+6 -0)
social/google/pom.xml 6(+6 -0)
social/twitter/pom.xml 6(+6 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java 4(+3 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionResetPasswordTest.java 7(+7 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java 26(+20 -6)
testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java 41(+26 -15)
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")
social/core/pom.xml 6(+6 -0)
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;
}
social/facebook/pom.xml 6(+6 -0)
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>
social/github/pom.xml 6(+6 -0)
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>
social/google/pom.xml 6(+6 -0)
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>
social/twitter/pom.xml 6(+6 -0)
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")