keycloak-aplcache
Changes
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java 18(+14 -4)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java 7(+3 -4)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java 43(+36 -7)
model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java 8(+4 -4)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java 13(+9 -4)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java 7(+3 -4)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java 12(+8 -4)
model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java 7(+3 -4)
services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java 28(+18 -10)
services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java 17(+10 -7)
services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java 31(+5 -26)
Details
diff --git a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
index 01f524a..cd724ef 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
@@ -32,9 +32,9 @@ public interface ClientSessionModel {
public Set<String> getProtocolMappers();
public void setProtocolMappers(Set<String> protocolMappers);
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators();
- public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status);
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status);
+ public Map<String, ExecutionStatus> getExecutionStatus();
+ public void setExecutionStatus(String authenticator, ExecutionStatus status);
+ public void clearExecutionStatus();
public UserModel getAuthenticatedUser();
public void setAuthenticatedUser(UserModel user);
@@ -67,6 +67,8 @@ public interface ClientSessionModel {
*/
public Map<String, String> getUserSessionNotes();
+ public void clearUserSessionNotes();
+
public static enum Action {
OAUTH_GRANT,
CODE_TO_TOKEN,
@@ -80,4 +82,12 @@ public interface ClientSessionModel {
LOGGED_OUT
}
+ public enum ExecutionStatus {
+ FAILED,
+ SUCCESS,
+ SETUP_REQUIRED,
+ ATTEMPTED,
+ SKIPPED,
+ CHALLENGED
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
index 6af29cf..516ba26 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
@@ -37,15 +37,6 @@ public interface UserSessionModel {
List<ClientSessionModel> getClientSessions();
- public static enum AuthenticatorStatus {
- FAILED,
- SUCCESS,
- SETUP_REQUIRED,
- ATTEMPTED,
- SKIPPED,
- CHALLENGED
- }
-
public String getNote(String name);
public void setNote(String name, String value);
public void removeNote(String name);
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
index 78be9ee..a00b539 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
@@ -185,23 +185,32 @@ public class ClientSessionAdapter implements ClientSessionModel {
return copy;
}
+ @Override
+ public void clearUserSessionNotes() {
+ entity.setUserSessionNotes(new HashMap<String, String>());
+ update();
+
+ }
+
void update() {
provider.getTx().replace(cache, entity.getId(), entity);
}
@Override
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
+ public Map<String, ExecutionStatus> getExecutionStatus() {
return entity.getAuthenticatorStatus();
}
@Override
- public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+ public void setExecutionStatus(String authenticator, ExecutionStatus status) {
entity.getAuthenticatorStatus().put(authenticator, status);
+ update();
}
@Override
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status) {
- entity.setAuthenticatorStatus(status);
+ public void clearExecutionStatus() {
+ entity.getAuthenticatorStatus().clear();
+ update();
}
@Override
@@ -211,6 +220,7 @@ public class ClientSessionAdapter implements ClientSessionModel {
@Override
public void setAuthenticatedUser(UserModel user) {
entity.setAuthUserId(user.getId());
+ update();
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java
index 340bf92..3cb6614 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java
@@ -1,7 +1,6 @@
package org.keycloak.models.sessions.infinispan.entities;
import org.keycloak.models.ClientSessionModel;
-import org.keycloak.models.UserSessionModel;
import java.util.HashMap;
import java.util.Map;
@@ -30,7 +29,7 @@ public class ClientSessionEntity extends SessionEntity {
private Set<String> protocolMappers;
private Map<String, String> notes;
private Map<String, String> userSessionNotes;
- private Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus = new HashMap<>();
+ private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
private String authUserId;
public String getClient() {
@@ -113,11 +112,11 @@ public class ClientSessionEntity extends SessionEntity {
this.notes = notes;
}
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticatorStatus() {
+ public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
return authenticatorStatus;
}
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus) {
+ public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
this.authenticatorStatus = authenticatorStatus;
}
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
index 2ce7034..e8498e9 100755
--- 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,6 +6,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.sessions.jpa.entities.ClientSessionAuthStatusEntity;
import org.keycloak.models.sessions.jpa.entities.ClientSessionEntity;
import org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity;
import org.keycloak.models.sessions.jpa.entities.ClientSessionProtocolMapperEntity;
@@ -107,6 +108,17 @@ public class ClientSessionAdapter implements ClientSessionModel {
}
@Override
+ public void clearUserSessionNotes() {
+ Iterator<ClientUserSessionNoteEntity> it = entity.getUserSessionNotes().iterator();
+ while (it.hasNext()) {
+ ClientUserSessionNoteEntity attr = it.next();
+ it.remove();
+ em.remove(attr);
+ }
+
+ }
+
+ @Override
public String getId() {
return entity.getId();
}
@@ -242,27 +254,44 @@ public class ClientSessionAdapter implements ClientSessionModel {
}
@Override
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
- return null;
+ public Map<String, ExecutionStatus> getExecutionStatus() {
+ Map<String, ExecutionStatus> result = new HashMap<>();
+ for (ClientSessionAuthStatusEntity status : entity.getAuthanticatorStatus()) {
+ result.put(status.getAuthenticator(), status.getStatus());
+ }
+ return result;
}
@Override
- public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+ public void setExecutionStatus(String authenticator, ExecutionStatus status) {
+ ClientSessionAuthStatusEntity authStatus = new ClientSessionAuthStatusEntity();
+ authStatus.setAuthenticator(authenticator);
+ authStatus.setClientSession(entity);
+ authStatus.setStatus(status);
+ em.persist(authStatus);
+ entity.getAuthanticatorStatus().add(authStatus);
+ em.flush();
+
}
@Override
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status) {
-
+ public void clearExecutionStatus() {
+ Iterator<ClientSessionAuthStatusEntity> iterator = entity.getAuthanticatorStatus().iterator();
+ while (iterator.hasNext()) {
+ ClientSessionAuthStatusEntity authStatus = iterator.next();
+ iterator.remove();
+ em.remove(authStatus);
+ }
}
@Override
public UserModel getAuthenticatedUser() {
- return null;
+ return session.users().getUserById(entity.getUserId(), realm);
}
@Override
public void setAuthenticatedUser(UserModel user) {
-
+ entity.setUserId(user.getId());
}
}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java
index 49afbab..e8dea7a 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java
@@ -1,6 +1,6 @@
package org.keycloak.models.sessions.jpa.entities;
-import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.ClientSessionModel;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -39,7 +39,7 @@ public class ClientSessionAuthStatusEntity {
@Column(name = "AUTHENTICATOR")
protected String authenticator;
@Column(name = "STATUS")
- protected UserSessionModel.AuthenticatorStatus status;
+ protected ClientSessionModel.ExecutionStatus status;
public String getAuthenticator() {
return authenticator;
@@ -49,11 +49,11 @@ public class ClientSessionAuthStatusEntity {
this.authenticator = authenticator;
}
- public UserSessionModel.AuthenticatorStatus getStatus() {
+ public ClientSessionModel.ExecutionStatus getStatus() {
return status;
}
- public void setStatus(UserSessionModel.AuthenticatorStatus status) {
+ public void setStatus(ClientSessionModel.ExecutionStatus status) {
this.status = status;
}
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
index 0e0647f..f9e52d0 100755
--- 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
@@ -155,19 +155,24 @@ public class ClientSessionAdapter implements ClientSessionModel {
}
@Override
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
+ public Map<String, ExecutionStatus> getExecutionStatus() {
return entity.getAuthenticatorStatus();
}
@Override
- public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+ public void setExecutionStatus(String authenticator, ExecutionStatus status) {
entity.getAuthenticatorStatus().put(authenticator, status);
}
@Override
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status) {
- entity.setAuthenticatorStatus(status);
+ public void clearExecutionStatus() {
+ entity.getAuthenticatorStatus().clear();
+ }
+
+ @Override
+ public void clearUserSessionNotes() {
+ entity.getUserSessionNotes().clear();
}
@Override
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
index e76f624..da5e050 100755
--- 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
@@ -1,7 +1,6 @@
package org.keycloak.models.sessions.mem.entities;
import org.keycloak.models.ClientSessionModel;
-import org.keycloak.models.UserSessionModel;
import java.util.HashMap;
import java.util.Map;
@@ -15,7 +14,7 @@ public class ClientSessionEntity {
private String id;
private String clientId;
private String realmId;
- private Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus = new HashMap<>();
+ private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
private String authUserId;
private UserSessionEntity session;
@@ -122,11 +121,11 @@ public class ClientSessionEntity {
this.authUserId = authUserId;
}
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticatorStatus() {
+ public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
return authenticatorStatus;
}
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus) {
+ public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
this.authenticatorStatus = authenticatorStatus;
}
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
index ad1d0d7..4ad1d51 100755
--- 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
@@ -171,22 +171,26 @@ public class ClientSessionAdapter extends AbstractMongoAdapter<MongoClientSessio
}
@Override
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
+ public Map<String, ExecutionStatus> getExecutionStatus() {
return entity.getAuthenticatorStatus();
}
@Override
- public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+ public void setExecutionStatus(String authenticator, ExecutionStatus status) {
entity.getAuthenticatorStatus().put(authenticator, status);
updateMongoEntity();
}
@Override
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status) {
- entity.setAuthenticatorStatus(status);
+ public void clearExecutionStatus() {
+ entity.getAuthenticatorStatus().clear();
updateMongoEntity();
+ }
+ @Override
+ public void clearUserSessionNotes() {
+ entity.getUserSessionNotes().clear();
}
@Override
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 21831b6..de7bed3 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
@@ -4,7 +4,6 @@ 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.ClientSessionModel;
-import org.keycloak.models.UserSessionModel;
import org.keycloak.models.entities.AbstractIdentifiableEntity;
import java.util.HashMap;
@@ -31,7 +30,7 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
private List<String> protocolMappers;
private Map<String, String> notes = new HashMap<String, String>();
private Map<String, String> userSessionNotes = new HashMap<String, String>();
- private Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus = new HashMap<>();
+ private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
private String authUserId;
public String getId() {
@@ -130,11 +129,11 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
this.sessionId = sessionId;
}
- public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticatorStatus() {
+ public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
return authenticatorStatus;
}
- public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus) {
+ public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
this.authenticatorStatus = authenticatorStatus;
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index bbafc76..fce95a2 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -3,6 +3,7 @@ package org.keycloak.authentication;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
+import org.keycloak.authentication.authenticators.AbstractFormAuthenticator;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
@@ -17,23 +18,23 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.services.ErrorPage;
-import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
+import org.keycloak.util.Time;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationProcessor {
+ public static final String CURRENT_AUTHENTICATION_EXECUTION = "current.authentication.execution";
protected static Logger logger = Logger.getLogger(AuthenticationProcessor.class);
protected RealmModel realm;
protected UserSessionModel userSession;
@@ -325,7 +326,7 @@ public class AuthenticationProcessor {
@Override
public String generateAccessCode() {
ClientSessionCode accessCode = new ClientSessionCode(getRealm(), getClientSession());
- accessCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
+ clientSession.setTimestamp(Time.currentTime());
return accessCode.getCode();
}
}
@@ -362,23 +363,32 @@ public class AuthenticationProcessor {
}
}
- public void logUserFailure() {
+ public void logFailure() {
+ if (realm.isBruteForceProtected()) {
+ String username = clientSession.getNote(AbstractFormAuthenticator.ATTEMPTED_USERNAME);
+ // todo need to handle non form failures
+ if (username == null) {
+
+ } else {
+ protector.failedLogin(realm, username, connection);
+ }
+ }
}
protected boolean isProcessed(AuthenticationExecutionModel model) {
if (model.isDisabled()) return true;
- UserSessionModel.AuthenticatorStatus status = clientSession.getAuthenticators().get(model.getId());
+ ClientSessionModel.ExecutionStatus status = clientSession.getExecutionStatus().get(model.getId());
if (status == null) return false;
- return status == UserSessionModel.AuthenticatorStatus.SUCCESS || status == UserSessionModel.AuthenticatorStatus.SKIPPED
- || status == UserSessionModel.AuthenticatorStatus.ATTEMPTED
- || status == UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED;
+ return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
+ || status == ClientSessionModel.ExecutionStatus.ATTEMPTED
+ || status == ClientSessionModel.ExecutionStatus.SETUP_REQUIRED;
}
public boolean isSuccessful(AuthenticationExecutionModel model) {
- UserSessionModel.AuthenticatorStatus status = clientSession.getAuthenticators().get(model.getId());
+ ClientSessionModel.ExecutionStatus status = clientSession.getExecutionStatus().get(model.getId());
if (status == null) return false;
- return status == UserSessionModel.AuthenticatorStatus.SUCCESS;
+ return status == ClientSessionModel.ExecutionStatus.SUCCESS;
}
public Response handleBrowserException(Exception failure) {
@@ -449,6 +459,51 @@ public class AuthenticationProcessor {
return authenticationComplete();
}
+ protected void resetFlow() {
+ clientSession.clearExecutionStatus();
+ clientSession.clearUserSessionNotes();
+ clientSession.removeNote(CURRENT_AUTHENTICATION_EXECUTION);
+ }
+
+ public Response authenticationAction(String execution) {
+ checkClientSession();
+ String current = clientSession.getNote(CURRENT_AUTHENTICATION_EXECUTION);
+ if (!execution.equals(current)) {
+ logger.debug("Current execution does not equal executed execution. Might be a page refresh");
+ logFailure();
+ resetFlow();
+ return authenticate();
+ }
+ AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
+ if (model == null) {
+ logger.debug("Cannot find execution, reseting flow");
+ logFailure();
+ resetFlow();
+ return authenticate();
+ }
+ event.event(EventType.LOGIN);
+ event.client(clientSession.getClient().getClientId())
+ .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
+ .detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
+ String authType = clientSession.getNote(Details.AUTH_TYPE);
+ if (authType != null) {
+ event.detail(Details.AUTH_TYPE, authType);
+ }
+ AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator());
+ AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId());
+ Authenticator authenticator = factory.create(authenticatorModel);
+ Result context = new Result(model, authenticatorModel, authenticator);
+ authenticator.action(context);
+
+ FlowExecution flowExecution = createFlowExecution(this.flowId);
+ Response challenge = flowExecution.action(execution, context);
+ if (challenge != null) return challenge;
+ if (clientSession.getAuthenticatedUser() == null) {
+ throw new AuthException(Error.UNKNOWN_USER);
+ }
+ return authenticationComplete();
+ }
+
public void checkClientSession() {
ClientSessionCode code = new ClientSessionCode(realm, clientSession);
if (!code.isValidAction(ClientSessionModel.Action.AUTHENTICATE.name())) {
@@ -457,6 +512,7 @@ public class AuthenticationProcessor {
if (!code.isActionActive(ClientSessionModel.Action.AUTHENTICATE.name())) {
throw new AuthException(Error.EXPIRED_CODE);
}
+ clientSession.setTimestamp(Time.currentTime());
}
public Response authenticateOnly() throws AuthException {
@@ -566,14 +622,14 @@ public class AuthenticationProcessor {
continue;
}
if (model.isAlternative() && alternativeSuccessful) {
- clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED);
+ clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
}
if (model.isAutheticatorFlow()) {
FlowExecution flowExecution = createFlowExecution(model.getAuthenticator());
Response flowResponse = flowExecution.processFlow();
if (flowResponse == null) {
- clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
+ clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
if (model.isAlternative()) alternativeSuccessful = true;
continue;
} else {
@@ -590,7 +646,7 @@ public class AuthenticationProcessor {
if (authenticator.requiresUser() && authUser == null){
if (alternativeChallenge != null) {
- clientSession.setAuthenticatorStatus(challengedAlternativeExecution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
+ clientSession.setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return alternativeChallenge;
}
throw new AuthException("authenticator: " + authenticatorModel.getProviderId(), Error.UNKNOWN_USER);
@@ -602,14 +658,14 @@ public class AuthenticationProcessor {
if (model.isRequired()) {
if (model.isUserSetupAllowed()) {
logger.debugv("authenticator SETUP_REQUIRED: {0}", authenticatorModel.getProviderId());
- clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
+ clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
authenticator.setRequiredActions(session, realm, clientSession.getAuthenticatedUser());
continue;
} else {
throw new AuthException(Error.CREDENTIAL_SETUP_REQUIRED);
}
} else if (model.isOptional()) {
- clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED);
+ clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
}
}
@@ -629,47 +685,49 @@ public class AuthenticationProcessor {
Status status = result.getStatus();
if (status == Status.SUCCESS){
logger.debugv("authenticator SUCCESS: {0}", authenticatorModel.getProviderId());
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
if (execution.isAlternative()) alternativeSuccessful = true;
return null;
} else if (status == Status.FAILED) {
logger.debugv("authenticator FAILED: {0}", authenticatorModel.getProviderId());
- logUserFailure();
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.FAILED);
- if (result.challenge != null) return result.challenge;
+ logFailure();
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
+ if (result.challenge != null) {
+ return sendChallenge(result, execution);
+ }
throw new AuthException(result.error);
} else if (status == Status.FORCE_CHALLENGE) {
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
- return result.challenge;
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+ return sendChallenge(result, execution);
} else if (status == Status.CHALLENGE) {
logger.debugv("authenticator CHALLENGE: {0}", authenticatorModel.getProviderId());
if (execution.isRequired()) {
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
- return result.challenge;
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+ return sendChallenge(result, execution);
}
UserModel authenticatedUser = clientSession.getAuthenticatedUser();
if (execution.isOptional() && authenticatedUser != null && result.getAuthenticator().configuredFor(session, realm, authenticatedUser)) {
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
- return result.challenge;
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+ return sendChallenge(result, execution);
}
if (execution.isAlternative()) {
alternativeChallenge = result.challenge;
challengedAlternativeExecution = execution;
} else {
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED);
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
}
return null;
} else if (status == Status.FAILURE_CHALLENGE) {
logger.debugv("authenticator FAILURE_CHALLENGE: {0}", authenticatorModel.getProviderId());
- logUserFailure();
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
- return result.challenge;
+ logFailure();
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+ return sendChallenge(result, execution);
} else if (status == Status.ATTEMPTED) {
logger.debugv("authenticator ATTEMPTED: {0}", authenticatorModel.getProviderId());
if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
throw new AuthException(Error.INVALID_CREDENTIALS);
}
- clientSession.setAuthenticatorStatus(execution.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
+ clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
return null;
} else {
logger.debugv("authenticator INTERNAL_ERROR: {0}", authenticatorModel.getProviderId());
@@ -679,8 +737,13 @@ public class AuthenticationProcessor {
}
+ public Response sendChallenge(Result result, AuthenticationExecutionModel execution) {
+ clientSession.setNote(CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
+ return result.challenge;
+ }
- }
+
+ }
diff --git a/services/src/main/java/org/keycloak/authentication/Authenticator.java b/services/src/main/java/org/keycloak/authentication/Authenticator.java
index 8245f82..36e6d52 100755
--- a/services/src/main/java/org/keycloak/authentication/Authenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/Authenticator.java
@@ -20,5 +20,7 @@ public interface Authenticator extends Provider {
*/
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
+ void action(AuthenticatorContext context);
+
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
index 6c611b1..957c540 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
@@ -2,6 +2,7 @@ package org.keycloak.authentication.authenticators;
import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
@@ -24,20 +25,25 @@ import java.util.List;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
-public class AbstractFormAuthenticator {
+public abstract class AbstractFormAuthenticator implements Authenticator {
- public static final String LOGIN_FORM_ACTION = "login_form";
public static final String REGISTRATION_FORM_ACTION = "registration_form";
- public static final String ACTION = "action";
- public static final String FORM_USERNAME = "FORM_USERNAME";
+ public static final String EXECUTION = "execution";
+ public static final String ATTEMPTED_USERNAME = "ATTEMPTED_USERNAME";
+
+ @Override
+ public void action(AuthenticatorContext context) {
+
+ }
+
+ @Override
+ public void close() {
- protected boolean isAction(AuthenticatorContext context, String action) {
- return action.equals(context.getAction());
}
protected LoginFormsProvider loginForm(AuthenticatorContext context) {
String accessCode = context.generateAccessCode();
- URI action = getActionUrl(context, accessCode, LOGIN_FORM_ACTION);
+ URI action = getActionUrl(context, accessCode);
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
.setUser(context.getUser())
.setActionUri(action)
@@ -48,10 +54,10 @@ public class AbstractFormAuthenticator {
return provider;
}
- public static URI getActionUrl(AuthenticatorContext context, String code, String action) {
+ public URI getActionUrl(AuthenticatorContext context, String code) {
return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
.queryParam(OAuth2Constants.CODE, code)
- .queryParam(ACTION, action)
+ .queryParam(EXECUTION, context.getExecution().getId())
.build(context.getRealm().getName());
}
@@ -111,7 +117,7 @@ public class AbstractFormAuthenticator {
return false;
}
context.getEvent().detail(Details.USERNAME, username);
- context.getClientSession().setNote(AbstractFormAuthenticator.FORM_USERNAME, username);
+ context.getClientSession().setNote(AbstractFormAuthenticator.ATTEMPTED_USERNAME, username);
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
if (invalidUser(context, user)) return false;
String rememberMe = inputData.getFirst("rememberMe");
@@ -119,6 +125,8 @@ public class AbstractFormAuthenticator {
if (remember) {
context.getClientSession().setNote(Details.REMEMBER_ME, "true");
context.getEvent().detail(Details.REMEMBER_ME, "true");
+ } else {
+ context.getClientSession().removeNote(Details.REMEMBER_ME);
}
context.setUser(user);
return true;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
index a4d6430..7e68a02 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
@@ -33,6 +33,11 @@ public class CookieAuthenticator implements Authenticator {
}
@Override
+ public void action(AuthenticatorContext context) {
+
+ }
+
+ @Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
index bce060b..fd2aa08 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
@@ -32,15 +32,16 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
}
@Override
- public void authenticate(AuthenticatorContext context) {
- if (!isAction(context, TOTP_FORM_ACTION)) {
- Response challengeResponse = challenge(context, null);
- context.challenge(challengeResponse);
- return;
- }
+ public void action(AuthenticatorContext context) {
validateOTP(context);
}
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ Response challengeResponse = challenge(context, null);
+ context.challenge(challengeResponse);
+ }
+
public void validateOTP(AuthenticatorContext context) {
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
List<UserCredentialModel> credentials = new LinkedList<>();
@@ -69,7 +70,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
protected Response challenge(AuthenticatorContext context, String error) {
String accessCode = context.generateAccessCode();
- URI action = AbstractFormAuthenticator.getActionUrl(context, accessCode, TOTP_FORM_ACTION);
+ URI action = getActionUrl(context, accessCode);
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
.setActionUri(action)
.setClientSessionCode(accessCode);
@@ -91,6 +92,8 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
}
+
+
@Override
public void close() {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java
index eb6c1ee..0696e06 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java
@@ -39,26 +39,17 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
return false;
}
- protected boolean isAlreadyChallenged(AuthenticatorContext context) {
- UserSessionModel.AuthenticatorStatus status = context.getClientSession().getAuthenticators().get(context.getExecution().getId());
- if (status == null) return false;
- return status == UserSessionModel.AuthenticatorStatus.CHALLENGED;
+ @Override
+ public void action(AuthenticatorContext context) {
+ context.attempted();
+ return;
}
@Override
public void authenticate(AuthenticatorContext context) {
HttpRequest request = context.getHttpRequest();
String authHeader = request.getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
- if (isAction(context, KERBEROS_DISABLED)) {
- context.attempted();
- return;
- }
- // Case when we don't yet have any Negotiate header
if (authHeader == null) {
- if (isAlreadyChallenged(context)) {
- context.attempted();
- return;
- }
Response challenge = challengeNegotiation(context, null);
context.forceChallenge(challenge);
return;
@@ -131,7 +122,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
*/
protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) {
String accessCode = context.generateAccessCode();
- URI action = getActionUrl(context, accessCode, KERBEROS_DISABLED);
+ URI action = getActionUrl(context, accessCode);
StringBuilder builder = new StringBuilder();
@@ -159,18 +150,6 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
.entity(builder.toString()).build();
}
- protected Response formChallenge(AuthenticatorContext context, String negotiateHeader) {
- String accessCode = context.generateAccessCode();
- URI action = getActionUrl(context, accessCode, KERBEROS_DISABLED);
- return context.getSession().getProvider(LoginFormsProvider.class)
- .setClientSessionCode(accessCode)
- .setActionUri(action)
- .setStatus(Response.Status.UNAUTHORIZED)
- .setResponseHeader(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader)
- .setUser(context.getUser())
- .createForm("bypass_kerberos.ftl", new HashMap<String, Object>());
- }
-
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordForm.java
index 3dd81d7..2c25bac 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordForm.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordForm.java
@@ -4,24 +4,18 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
-import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
-import java.util.LinkedList;
-import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -34,30 +28,8 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
this.model = model;
}
- @Override
- public void authenticate(AuthenticatorContext context) {
- if (isAction(context, REGISTRATION_FORM_ACTION) && context.getUser() != null) {
- context.success();
- return;
- }
- if (!isAction(context, LOGIN_FORM_ACTION)) {
- MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
- String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
-
- String rememberMeUsername = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders());
-
- if (loginHint != null || rememberMeUsername != null) {
- if (loginHint != null) {
- formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
- } else {
- formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
- formData.add("rememberMe", "on");
- }
- }
- Response challengeResponse = challenge(context, formData);
- context.challenge(challengeResponse);
- return;
- }
+ @Override
+ public void action(AuthenticatorContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
if (formData.containsKey("cancel")) {
context.getEvent().error(Errors.REJECTED_BY_USER);
@@ -66,10 +38,9 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
.setHttpHeaders(context.getHttpRequest().getHttpHeaders())
.setUriInfo(context.getUriInfo());
Response response = protocol.cancelLogin(context.getClientSession());
- context.challenge(response);
+ context.forceChallenge(response);
return;
}
-
if (!validateUser(context, formData)) {
return;
}
@@ -77,7 +48,30 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
return;
}
context.success();
+ }
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ if (REGISTRATION_FORM_ACTION.equals(context.getAction()) && context.getUser() != null) {
+ context.success();
+ return;
+ }
+ MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
+ String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
+
+ String rememberMeUsername = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders());
+
+ if (loginHint != null || rememberMeUsername != null) {
+ if (loginHint != null) {
+ formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
+ } else {
+ formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
+ formData.add("rememberMe", "on");
+ }
+ }
+ Response challengeResponse = challenge(context, formData);
+ context.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, context.getExecution().getId());
+ context.challenge(challengeResponse);
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
index d203218..b19295b 100755
--- a/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
@@ -142,25 +142,6 @@ public class ClientSessionCode {
clientSession.setTimestamp(Time.currentTime());
}
- public void setRequiredAction(RequiredAction requiredAction) {
- setAction(convertToAction(requiredAction));
- }
-
- private String convertToAction(RequiredAction requiredAction) {
- switch (requiredAction) {
- case CONFIGURE_TOTP:
- return ClientSessionModel.Action.CONFIGURE_TOTP.name();
- case UPDATE_PASSWORD:
- return ClientSessionModel.Action.UPDATE_PASSWORD.name();
- case UPDATE_PROFILE:
- return ClientSessionModel.Action.UPDATE_PROFILE.name();
- case VERIFY_EMAIL:
- return ClientSessionModel.Action.VERIFY_EMAIL.name();
- default:
- throw new IllegalArgumentException("Unknown required action " + requiredAction);
- }
- }
-
public String getCode() {
return generateCode(realm, clientSession);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 3bec193..cca6bb0 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -23,6 +23,7 @@ package org.keycloak.services.resources;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.InternalServerErrorException;
import org.keycloak.ClientConnection;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.AuthenticatorUtil;
@@ -65,6 +66,7 @@ import org.keycloak.services.ErrorPage;
import org.keycloak.services.Urls;
import org.keycloak.services.util.CookieHelper;
import org.keycloak.services.validation.Validation;
+import org.keycloak.util.Time;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -234,7 +236,7 @@ public class LoginActionsService {
@Path("authenticate")
@GET
public Response authenticate(@QueryParam("code") String code,
- @QueryParam("action") String action) {
+ @QueryParam("execution") String execution) {
event.event(EventType.LOGIN);
Checks checks = new Checks();
if (!checks.check(code)) {
@@ -249,28 +251,58 @@ public class LoginActionsService {
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
}
- AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
- String flowId = flow.getId();
+ return processAuthentication(execution, clientSession);
+ }
+
+ protected Response processAuthentication(String execution, ClientSessionModel clientSession) {
+ String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
+ AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
- .setFlowId(flowId)
+ .setFlowId(flow.getId())
.setConnection(clientConnection)
.setEventBuilder(event)
.setProtector(authManager.getProtector())
- .setAction(action)
.setRealm(realm)
.setSession(session)
.setUriInfo(uriInfo)
.setRequest(request);
try {
- return processor.authenticate();
+ if (execution != null) {
+ return processor.authenticationAction(execution);
+ } else {
+ return processor.authenticate();
+ }
} catch (Exception e) {
return processor.handleBrowserException(e);
}
}
/**
+ * URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
+ *
+ * @param code
+ * @return
+ */
+ @Path("authenticate")
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response authenticateForm(@QueryParam("code") String code,
+ @QueryParam("execution") String execution) {
+ event.event(EventType.LOGIN);
+ Checks checks = new Checks();
+ if (!checks.check(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
+ return checks.response;
+ }
+ final ClientSessionCode clientCode = checks.clientCode;
+ final ClientSessionModel clientSession = clientCode.getClientSession();
+
+ return processAuthentication(execution, clientSession);
+ }
+
+
+ /**
* protocol independent registration page entry point
*
* @param code
@@ -302,46 +334,6 @@ public class LoginActionsService {
.createRegistration();
}
- /**
- * URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
- *
- * @param code
- * @return
- */
- @Path("authenticate")
- @POST
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- public Response authenticateForm(@QueryParam("code") String code,
- @QueryParam("action") String action) {
- event.event(EventType.LOGIN);
- Checks checks = new Checks();
- if (!checks.check(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
- return checks.response;
- }
- final ClientSessionCode clientCode = checks.clientCode;
- final ClientSessionModel clientSession = clientCode.getClientSession();
-
- String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
- AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
- AuthenticationProcessor processor = new AuthenticationProcessor();
- processor.setClientSession(clientSession)
- .setFlowId(flow.getId())
- .setConnection(clientConnection)
- .setEventBuilder(event)
- .setProtector(authManager.getProtector())
- .setRealm(realm)
- .setSession(session)
- .setUriInfo(uriInfo)
- .setAction(action)
- .setRequest(request);
-
- try {
- return processor.authenticate();
- } catch (Exception e) {
- return processor.handleBrowserException(e);
- }
-
- }
/**
* Registration
@@ -946,7 +938,7 @@ public class LoginActionsService {
}
@Path("required-actions/{action}")
- public Object requiredAction(@QueryParam("code") String code,
+ public Object requiredAction(@QueryParam("code") final String code,
@PathParam("action") String action) {
event.event(EventType.LOGIN);
if (action == null) {
@@ -1024,6 +1016,11 @@ public class LoginActionsService {
@Override
public String generateAccessCode(String action) {
+ String clientSessionAction = clientSession.getAction();
+ if (action.equals(clientSessionAction)) {
+ clientSession.setTimestamp(Time.currentTime());
+ return code;
+ }
ClientSessionCode code = new ClientSessionCode(getRealm(), getClientSession());
code.setAction(action);
return code.getCode();