Merge pull request #1404 from patriot1burke/master
handle page refresh better
This commit is contained in:
commit
8d885d14ed
19 changed files with 450 additions and 319 deletions
|
@ -32,9 +32,9 @@ public interface ClientSessionModel {
|
||||||
public Set<String> getProtocolMappers();
|
public Set<String> getProtocolMappers();
|
||||||
public void setProtocolMappers(Set<String> protocolMappers);
|
public void setProtocolMappers(Set<String> protocolMappers);
|
||||||
|
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators();
|
public Map<String, ExecutionStatus> getExecutionStatus();
|
||||||
public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status);
|
public void setExecutionStatus(String authenticator, ExecutionStatus status);
|
||||||
public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status);
|
public void clearExecutionStatus();
|
||||||
public UserModel getAuthenticatedUser();
|
public UserModel getAuthenticatedUser();
|
||||||
public void setAuthenticatedUser(UserModel user);
|
public void setAuthenticatedUser(UserModel user);
|
||||||
|
|
||||||
|
@ -67,6 +67,8 @@ public interface ClientSessionModel {
|
||||||
*/
|
*/
|
||||||
public Map<String, String> getUserSessionNotes();
|
public Map<String, String> getUserSessionNotes();
|
||||||
|
|
||||||
|
public void clearUserSessionNotes();
|
||||||
|
|
||||||
public static enum Action {
|
public static enum Action {
|
||||||
OAUTH_GRANT,
|
OAUTH_GRANT,
|
||||||
CODE_TO_TOKEN,
|
CODE_TO_TOKEN,
|
||||||
|
@ -80,4 +82,12 @@ public interface ClientSessionModel {
|
||||||
LOGGED_OUT
|
LOGGED_OUT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ExecutionStatus {
|
||||||
|
FAILED,
|
||||||
|
SUCCESS,
|
||||||
|
SETUP_REQUIRED,
|
||||||
|
ATTEMPTED,
|
||||||
|
SKIPPED,
|
||||||
|
CHALLENGED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,15 +37,6 @@ public interface UserSessionModel {
|
||||||
|
|
||||||
List<ClientSessionModel> getClientSessions();
|
List<ClientSessionModel> getClientSessions();
|
||||||
|
|
||||||
public static enum AuthenticatorStatus {
|
|
||||||
FAILED,
|
|
||||||
SUCCESS,
|
|
||||||
SETUP_REQUIRED,
|
|
||||||
ATTEMPTED,
|
|
||||||
SKIPPED,
|
|
||||||
CHALLENGED
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNote(String name);
|
public String getNote(String name);
|
||||||
public void setNote(String name, String value);
|
public void setNote(String name, String value);
|
||||||
public void removeNote(String name);
|
public void removeNote(String name);
|
||||||
|
|
|
@ -185,23 +185,32 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearUserSessionNotes() {
|
||||||
|
entity.setUserSessionNotes(new HashMap<String, String>());
|
||||||
|
update();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
provider.getTx().replace(cache, entity.getId(), entity);
|
provider.getTx().replace(cache, entity.getId(), entity);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
|
public Map<String, ExecutionStatus> getExecutionStatus() {
|
||||||
return entity.getAuthenticatorStatus();
|
return entity.getAuthenticatorStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
|
public void setExecutionStatus(String authenticator, ExecutionStatus status) {
|
||||||
entity.getAuthenticatorStatus().put(authenticator, status);
|
entity.getAuthenticatorStatus().put(authenticator, status);
|
||||||
|
update();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status) {
|
public void clearExecutionStatus() {
|
||||||
entity.setAuthenticatorStatus(status);
|
entity.getAuthenticatorStatus().clear();
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -211,6 +220,7 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatedUser(UserModel user) {
|
public void setAuthenticatedUser(UserModel user) {
|
||||||
entity.setAuthUserId(user.getId());
|
entity.setAuthUserId(user.getId());
|
||||||
|
update();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.keycloak.models.sessions.infinispan.entities;
|
package org.keycloak.models.sessions.infinispan.entities;
|
||||||
|
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -30,7 +29,7 @@ public class ClientSessionEntity extends SessionEntity {
|
||||||
private Set<String> protocolMappers;
|
private Set<String> protocolMappers;
|
||||||
private Map<String, String> notes;
|
private Map<String, String> notes;
|
||||||
private Map<String, String> userSessionNotes;
|
private Map<String, String> userSessionNotes;
|
||||||
private Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus = new HashMap<>();
|
private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
|
||||||
private String authUserId;
|
private String authUserId;
|
||||||
|
|
||||||
public String getClient() {
|
public String getClient() {
|
||||||
|
@ -113,11 +112,11 @@ public class ClientSessionEntity extends SessionEntity {
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticatorStatus() {
|
public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
|
||||||
return authenticatorStatus;
|
return authenticatorStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus) {
|
public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
|
||||||
this.authenticatorStatus = authenticatorStatus;
|
this.authenticatorStatus = authenticatorStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
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.ClientSessionEntity;
|
||||||
import org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity;
|
import org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity;
|
||||||
import org.keycloak.models.sessions.jpa.entities.ClientSessionProtocolMapperEntity;
|
import org.keycloak.models.sessions.jpa.entities.ClientSessionProtocolMapperEntity;
|
||||||
|
@ -106,6 +107,17 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearUserSessionNotes() {
|
||||||
|
Iterator<ClientUserSessionNoteEntity> it = entity.getUserSessionNotes().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
ClientUserSessionNoteEntity attr = it.next();
|
||||||
|
it.remove();
|
||||||
|
em.remove(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return entity.getId();
|
return entity.getId();
|
||||||
|
@ -242,27 +254,44 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
|
public Map<String, ExecutionStatus> getExecutionStatus() {
|
||||||
return null;
|
Map<String, ExecutionStatus> result = new HashMap<>();
|
||||||
|
for (ClientSessionAuthStatusEntity status : entity.getAuthanticatorStatus()) {
|
||||||
|
result.put(status.getAuthenticator(), status.getStatus());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@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
|
@Override
|
||||||
public UserModel getAuthenticatedUser() {
|
public UserModel getAuthenticatedUser() {
|
||||||
return null;
|
return session.users().getUserById(entity.getUserId(), realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatedUser(UserModel user) {
|
public void setAuthenticatedUser(UserModel user) {
|
||||||
|
entity.setUserId(user.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.keycloak.models.sessions.jpa.entities;
|
package org.keycloak.models.sessions.jpa.entities;
|
||||||
|
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
@ -39,7 +39,7 @@ public class ClientSessionAuthStatusEntity {
|
||||||
@Column(name = "AUTHENTICATOR")
|
@Column(name = "AUTHENTICATOR")
|
||||||
protected String authenticator;
|
protected String authenticator;
|
||||||
@Column(name = "STATUS")
|
@Column(name = "STATUS")
|
||||||
protected UserSessionModel.AuthenticatorStatus status;
|
protected ClientSessionModel.ExecutionStatus status;
|
||||||
|
|
||||||
public String getAuthenticator() {
|
public String getAuthenticator() {
|
||||||
return authenticator;
|
return authenticator;
|
||||||
|
@ -49,11 +49,11 @@ public class ClientSessionAuthStatusEntity {
|
||||||
this.authenticator = authenticator;
|
this.authenticator = authenticator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserSessionModel.AuthenticatorStatus getStatus() {
|
public ClientSessionModel.ExecutionStatus getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStatus(UserSessionModel.AuthenticatorStatus status) {
|
public void setStatus(ClientSessionModel.ExecutionStatus status) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,19 +155,24 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
|
public Map<String, ExecutionStatus> getExecutionStatus() {
|
||||||
return entity.getAuthenticatorStatus();
|
return entity.getAuthenticatorStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
|
public void setExecutionStatus(String authenticator, ExecutionStatus status) {
|
||||||
entity.getAuthenticatorStatus().put(authenticator, status);
|
entity.getAuthenticatorStatus().put(authenticator, status);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status) {
|
public void clearExecutionStatus() {
|
||||||
entity.setAuthenticatorStatus(status);
|
entity.getAuthenticatorStatus().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearUserSessionNotes() {
|
||||||
|
entity.getUserSessionNotes().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.keycloak.models.sessions.mem.entities;
|
package org.keycloak.models.sessions.mem.entities;
|
||||||
|
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -15,7 +14,7 @@ public class ClientSessionEntity {
|
||||||
private String id;
|
private String id;
|
||||||
private String clientId;
|
private String clientId;
|
||||||
private String realmId;
|
private String realmId;
|
||||||
private Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus = new HashMap<>();
|
private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
|
||||||
private String authUserId;
|
private String authUserId;
|
||||||
|
|
||||||
private UserSessionEntity session;
|
private UserSessionEntity session;
|
||||||
|
@ -122,11 +121,11 @@ public class ClientSessionEntity {
|
||||||
this.authUserId = authUserId;
|
this.authUserId = authUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticatorStatus() {
|
public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
|
||||||
return authenticatorStatus;
|
return authenticatorStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus) {
|
public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
|
||||||
this.authenticatorStatus = authenticatorStatus;
|
this.authenticatorStatus = authenticatorStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,22 +171,26 @@ public class ClientSessionAdapter extends AbstractMongoAdapter<MongoClientSessio
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticators() {
|
public Map<String, ExecutionStatus> getExecutionStatus() {
|
||||||
return entity.getAuthenticatorStatus();
|
return entity.getAuthenticatorStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
|
public void setExecutionStatus(String authenticator, ExecutionStatus status) {
|
||||||
entity.getAuthenticatorStatus().put(authenticator, status);
|
entity.getAuthenticatorStatus().put(authenticator, status);
|
||||||
updateMongoEntity();
|
updateMongoEntity();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> status) {
|
public void clearExecutionStatus() {
|
||||||
entity.setAuthenticatorStatus(status);
|
entity.getAuthenticatorStatus().clear();
|
||||||
updateMongoEntity();
|
updateMongoEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearUserSessionNotes() {
|
||||||
|
entity.getUserSessionNotes().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,7 +4,6 @@ import org.keycloak.connections.mongo.api.MongoCollection;
|
||||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
|
||||||
import org.keycloak.models.entities.AbstractIdentifiableEntity;
|
import org.keycloak.models.entities.AbstractIdentifiableEntity;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -31,7 +30,7 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
|
||||||
private List<String> protocolMappers;
|
private List<String> protocolMappers;
|
||||||
private Map<String, String> notes = new HashMap<String, String>();
|
private Map<String, String> notes = new HashMap<String, String>();
|
||||||
private Map<String, String> userSessionNotes = 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;
|
private String authUserId;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -130,11 +129,11 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, UserSessionModel.AuthenticatorStatus> getAuthenticatorStatus() {
|
public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
|
||||||
return authenticatorStatus;
|
return authenticatorStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuthenticatorStatus(Map<String, UserSessionModel.AuthenticatorStatus> authenticatorStatus) {
|
public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
|
||||||
this.authenticatorStatus = authenticatorStatus;
|
this.authenticatorStatus = authenticatorStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.authentication;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.ClientConnection;
|
||||||
|
import org.keycloak.authentication.authenticators.AbstractFormAuthenticator;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
|
@ -21,17 +22,19 @@ import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
import org.keycloak.services.managers.ClientSessionCode;
|
import org.keycloak.services.managers.ClientSessionCode;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.util.Collections;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class AuthenticationProcessor {
|
public class AuthenticationProcessor {
|
||||||
|
public static final String CURRENT_AUTHENTICATION_EXECUTION = "current.authentication.execution";
|
||||||
protected static Logger logger = Logger.getLogger(AuthenticationProcessor.class);
|
protected static Logger logger = Logger.getLogger(AuthenticationProcessor.class);
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected UserSessionModel userSession;
|
protected UserSessionModel userSession;
|
||||||
|
@ -323,7 +326,7 @@ public class AuthenticationProcessor {
|
||||||
@Override
|
@Override
|
||||||
public String generateAccessCode() {
|
public String generateAccessCode() {
|
||||||
ClientSessionCode accessCode = new ClientSessionCode(getRealm(), getClientSession());
|
ClientSessionCode accessCode = new ClientSessionCode(getRealm(), getClientSession());
|
||||||
accessCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
clientSession.setTimestamp(Time.currentTime());
|
||||||
return accessCode.getCode();
|
return accessCode.getCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,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) {
|
protected boolean isProcessed(AuthenticationExecutionModel model) {
|
||||||
if (model.isDisabled()) return true;
|
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;
|
if (status == null) return false;
|
||||||
return status == UserSessionModel.AuthenticatorStatus.SUCCESS || status == UserSessionModel.AuthenticatorStatus.SKIPPED
|
return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
|
||||||
|| status == UserSessionModel.AuthenticatorStatus.ATTEMPTED
|
|| status == ClientSessionModel.ExecutionStatus.ATTEMPTED
|
||||||
|| status == UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED;
|
|| status == ClientSessionModel.ExecutionStatus.SETUP_REQUIRED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuccessful(AuthenticationExecutionModel model) {
|
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;
|
if (status == null) return false;
|
||||||
return status == UserSessionModel.AuthenticatorStatus.SUCCESS;
|
return status == ClientSessionModel.ExecutionStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response handleBrowserException(Exception failure) {
|
public Response handleBrowserException(Exception failure) {
|
||||||
|
@ -414,6 +426,16 @@ public class AuthenticationProcessor {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FlowExecution createFlowExecution(String flowId) {
|
||||||
|
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
|
||||||
|
if (flow == null) {
|
||||||
|
logger.error("Unknown flow to execute with");
|
||||||
|
throw new AuthException(Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
FlowExecution flowExecution = new FlowExecution();
|
||||||
|
flowExecution.executions = realm.getAuthenticationExecutions(flow.getId()).iterator();
|
||||||
|
return flowExecution;
|
||||||
|
}
|
||||||
|
|
||||||
public Response authenticate() throws AuthException {
|
public Response authenticate() throws AuthException {
|
||||||
checkClientSession();
|
checkClientSession();
|
||||||
|
@ -428,7 +450,53 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
UserModel authUser = clientSession.getAuthenticatedUser();
|
UserModel authUser = clientSession.getAuthenticatedUser();
|
||||||
validateUser(authUser);
|
validateUser(authUser);
|
||||||
Response challenge = processFlow(flowId);
|
FlowExecution flowExecution = createFlowExecution(this.flowId);
|
||||||
|
Response challenge = flowExecution.processFlow();
|
||||||
|
if (challenge != null) return challenge;
|
||||||
|
if (clientSession.getAuthenticatedUser() == null) {
|
||||||
|
throw new AuthException(Error.UNKNOWN_USER);
|
||||||
|
}
|
||||||
|
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 (challenge != null) return challenge;
|
||||||
if (clientSession.getAuthenticatedUser() == null) {
|
if (clientSession.getAuthenticatedUser() == null) {
|
||||||
throw new AuthException(Error.UNKNOWN_USER);
|
throw new AuthException(Error.UNKNOWN_USER);
|
||||||
|
@ -444,6 +512,7 @@ public class AuthenticationProcessor {
|
||||||
if (!code.isActionActive(ClientSessionModel.Action.AUTHENTICATE.name())) {
|
if (!code.isActionActive(ClientSessionModel.Action.AUTHENTICATE.name())) {
|
||||||
throw new AuthException(Error.EXPIRED_CODE);
|
throw new AuthException(Error.EXPIRED_CODE);
|
||||||
}
|
}
|
||||||
|
clientSession.setTimestamp(Time.currentTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response authenticateOnly() throws AuthException {
|
public Response authenticateOnly() throws AuthException {
|
||||||
|
@ -458,7 +527,8 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
UserModel authUser = clientSession.getAuthenticatedUser();
|
UserModel authUser = clientSession.getAuthenticatedUser();
|
||||||
validateUser(authUser);
|
validateUser(authUser);
|
||||||
Response challenge = processFlow(flowId);
|
FlowExecution flowExecution = createFlowExecution(this.flowId);
|
||||||
|
Response challenge = flowExecution.processFlow();
|
||||||
if (challenge != null) return challenge;
|
if (challenge != null) return challenge;
|
||||||
|
|
||||||
String username = clientSession.getAuthenticatedUser().getUsername();
|
String username = clientSession.getAuthenticatedUser().getUsername();
|
||||||
|
@ -482,125 +552,6 @@ public class AuthenticationProcessor {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response processFlow(String flowId) {
|
|
||||||
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
|
|
||||||
if (flow == null) {
|
|
||||||
logger.error("Unknown flow to execute with");
|
|
||||||
throw new AuthException(Error.INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(flowId);
|
|
||||||
if (executions == null) return null;
|
|
||||||
Response alternativeChallenge = null;
|
|
||||||
AuthenticationExecutionModel challengedAlternativeExecution = null;
|
|
||||||
boolean alternativeSuccessful = false;
|
|
||||||
for (AuthenticationExecutionModel model : executions) {
|
|
||||||
if (isProcessed(model)) {
|
|
||||||
logger.debug("execution is processed");
|
|
||||||
if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Result context = null;
|
|
||||||
if (model.isAlternative() && alternativeSuccessful) {
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (model.isAutheticatorFlow()) {
|
|
||||||
Response flowResponse = processFlow(model.getAuthenticator());
|
|
||||||
if (flowResponse == null) {
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
|
|
||||||
if (model.isAlternative()) alternativeSuccessful = true;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
return flowResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator());
|
|
||||||
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId());
|
|
||||||
Authenticator authenticator = factory.create(authenticatorModel);
|
|
||||||
logger.debugv("authenticator: {0}", authenticatorModel.getProviderId());
|
|
||||||
UserModel authUser = clientSession.getAuthenticatedUser();
|
|
||||||
|
|
||||||
if (authenticator.requiresUser() && authUser == null){
|
|
||||||
if (alternativeChallenge != null) {
|
|
||||||
clientSession.setAuthenticatorStatus(challengedAlternativeExecution.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
|
|
||||||
return alternativeChallenge;
|
|
||||||
}
|
|
||||||
throw new AuthException("authenticator: " + authenticatorModel.getProviderId(), Error.UNKNOWN_USER);
|
|
||||||
}
|
|
||||||
boolean configuredFor = false;
|
|
||||||
if (authenticator.requiresUser() && authUser != null) {
|
|
||||||
configuredFor = authenticator.configuredFor(session, realm, authUser);
|
|
||||||
if (!configuredFor) {
|
|
||||||
if (model.isRequired()) {
|
|
||||||
if (model.isUserSetupAllowed()) {
|
|
||||||
logger.debugv("authenticator SETUP_REQUIRED: {0}", authenticatorModel.getProviderId());
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.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);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context = new Result(model, authenticatorModel, authenticator);
|
|
||||||
authenticator.authenticate(context);
|
|
||||||
Status result = context.getStatus();
|
|
||||||
if (result == Status.SUCCESS){
|
|
||||||
logger.debugv("authenticator SUCCESS: {0}", authenticatorModel.getProviderId());
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
|
|
||||||
if (model.isAlternative()) alternativeSuccessful = true;
|
|
||||||
continue;
|
|
||||||
} else if (result == Status.FAILED) {
|
|
||||||
logger.debugv("authenticator FAILED: {0}", authenticatorModel.getProviderId());
|
|
||||||
logUserFailure();
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.FAILED);
|
|
||||||
if (context.challenge != null) return context.challenge;
|
|
||||||
throw new AuthException(context.error);
|
|
||||||
} else if (result == Status.FORCE_CHALLENGE) {
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
|
|
||||||
return context.challenge;
|
|
||||||
} else if (result == Status.CHALLENGE) {
|
|
||||||
logger.debugv("authenticator CHALLENGE: {0}", authenticatorModel.getProviderId());
|
|
||||||
if (model.isRequired() || (model.isOptional() && configuredFor)) {
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
|
|
||||||
return context.challenge;
|
|
||||||
}
|
|
||||||
else if (model.isAlternative()) {
|
|
||||||
alternativeChallenge = context.challenge;
|
|
||||||
challengedAlternativeExecution = model;
|
|
||||||
} else {
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else if (result == Status.FAILURE_CHALLENGE) {
|
|
||||||
logger.debugv("authenticator FAILURE_CHALLENGE: {0}", authenticatorModel.getProviderId());
|
|
||||||
logUserFailure();
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.CHALLENGED);
|
|
||||||
return context.challenge;
|
|
||||||
} else if (result == Status.ATTEMPTED) {
|
|
||||||
logger.debugv("authenticator ATTEMPTED: {0}", authenticatorModel.getProviderId());
|
|
||||||
if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
|
|
||||||
throw new AuthException(Error.INVALID_CREDENTIALS);
|
|
||||||
}
|
|
||||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
logger.debugv("authenticator INTERNAL_ERROR: {0}", authenticatorModel.getProviderId());
|
|
||||||
logger.error("Unknown result status");
|
|
||||||
throw new AuthException(Error.INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void validateUser(UserModel authenticatedUser) {
|
public void validateUser(UserModel authenticatedUser) {
|
||||||
if (authenticatedUser != null) {
|
if (authenticatedUser != null) {
|
||||||
if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
|
if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
|
||||||
|
@ -632,5 +583,170 @@ public class AuthenticationProcessor {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FlowExecution {
|
||||||
|
Response alternativeChallenge = null;
|
||||||
|
AuthenticationExecutionModel challengedAlternativeExecution = null;
|
||||||
|
boolean alternativeSuccessful = false;
|
||||||
|
Iterator<AuthenticationExecutionModel> executions;
|
||||||
|
|
||||||
|
public Response action(String actionExecution, Result actionResult) {
|
||||||
|
while (executions.hasNext()) {
|
||||||
|
AuthenticationExecutionModel model = executions.next();
|
||||||
|
if (isProcessed(model)) {
|
||||||
|
logger.debug("execution is processed");
|
||||||
|
if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!model.getId().equals(actionExecution)) {
|
||||||
|
if (model.isAutheticatorFlow()) {
|
||||||
|
FlowExecution flowExecution = createFlowExecution(model.getAuthenticator());
|
||||||
|
return flowExecution.action(actionExecution, actionResult);
|
||||||
|
} else {
|
||||||
|
throw new AuthException("action is not current execution", Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
} else { // we found the action
|
||||||
|
Response response = processResult(actionResult);
|
||||||
|
if (response == null) return processFlow();
|
||||||
|
else return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AuthException("action is not in current execution", Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response processFlow() {
|
||||||
|
while (executions.hasNext()) {
|
||||||
|
AuthenticationExecutionModel model = executions.next();
|
||||||
|
if (isProcessed(model)) {
|
||||||
|
logger.debug("execution is processed");
|
||||||
|
if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (model.isAlternative() && alternativeSuccessful) {
|
||||||
|
clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (model.isAutheticatorFlow()) {
|
||||||
|
FlowExecution flowExecution = createFlowExecution(model.getAuthenticator());
|
||||||
|
Response flowResponse = flowExecution.processFlow();
|
||||||
|
if (flowResponse == null) {
|
||||||
|
clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||||
|
if (model.isAlternative()) alternativeSuccessful = true;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return flowResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator());
|
||||||
|
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId());
|
||||||
|
Authenticator authenticator = factory.create(authenticatorModel);
|
||||||
|
logger.debugv("authenticator: {0}", authenticatorModel.getProviderId());
|
||||||
|
UserModel authUser = clientSession.getAuthenticatedUser();
|
||||||
|
|
||||||
|
if (authenticator.requiresUser() && authUser == null){
|
||||||
|
if (alternativeChallenge != null) {
|
||||||
|
clientSession.setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return alternativeChallenge;
|
||||||
|
}
|
||||||
|
throw new AuthException("authenticator: " + authenticatorModel.getProviderId(), Error.UNKNOWN_USER);
|
||||||
|
}
|
||||||
|
boolean configuredFor = false;
|
||||||
|
if (authenticator.requiresUser() && authUser != null) {
|
||||||
|
configuredFor = authenticator.configuredFor(session, realm, authUser);
|
||||||
|
if (!configuredFor) {
|
||||||
|
if (model.isRequired()) {
|
||||||
|
if (model.isUserSetupAllowed()) {
|
||||||
|
logger.debugv("authenticator SETUP_REQUIRED: {0}", authenticatorModel.getProviderId());
|
||||||
|
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.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Result context = new Result(model, authenticatorModel, authenticator);
|
||||||
|
authenticator.authenticate(context);
|
||||||
|
Response response = processResult(context);
|
||||||
|
if (response != null) return response;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Response processResult(Result result) {
|
||||||
|
AuthenticationExecutionModel execution = result.getExecution();
|
||||||
|
AuthenticatorModel authenticatorModel = result.getAuthenticatorModel();
|
||||||
|
Status status = result.getStatus();
|
||||||
|
if (status == Status.SUCCESS){
|
||||||
|
logger.debugv("authenticator SUCCESS: {0}", authenticatorModel.getProviderId());
|
||||||
|
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());
|
||||||
|
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.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.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.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
}
|
||||||
|
if (execution.isAlternative()) {
|
||||||
|
alternativeChallenge = result.challenge;
|
||||||
|
challengedAlternativeExecution = execution;
|
||||||
|
} else {
|
||||||
|
clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} else if (status == Status.FAILURE_CHALLENGE) {
|
||||||
|
logger.debugv("authenticator FAILURE_CHALLENGE: {0}", authenticatorModel.getProviderId());
|
||||||
|
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.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
logger.debugv("authenticator INTERNAL_ERROR: {0}", authenticatorModel.getProviderId());
|
||||||
|
logger.error("Unknown result status");
|
||||||
|
throw new AuthException(Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response sendChallenge(Result result, AuthenticationExecutionModel execution) {
|
||||||
|
clientSession.setNote(CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
|
||||||
|
return result.challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,7 @@ public interface Authenticator extends Provider {
|
||||||
*/
|
*/
|
||||||
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
|
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
|
||||||
|
|
||||||
|
void action(AuthenticatorContext context);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.authentication.authenticators;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
|
import org.keycloak.authentication.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
import org.keycloak.authentication.AuthenticatorContext;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
|
@ -24,20 +25,25 @@ import java.util.List;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @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 REGISTRATION_FORM_ACTION = "registration_form";
|
||||||
public static final String ACTION = "action";
|
public static final String EXECUTION = "execution";
|
||||||
public static final String FORM_USERNAME = "FORM_USERNAME";
|
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) {
|
protected LoginFormsProvider loginForm(AuthenticatorContext context) {
|
||||||
String accessCode = context.generateAccessCode();
|
String accessCode = context.generateAccessCode();
|
||||||
URI action = getActionUrl(context, accessCode, LOGIN_FORM_ACTION);
|
URI action = getActionUrl(context, accessCode);
|
||||||
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
|
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
.setUser(context.getUser())
|
.setUser(context.getUser())
|
||||||
.setActionUri(action)
|
.setActionUri(action)
|
||||||
|
@ -48,10 +54,10 @@ public class AbstractFormAuthenticator {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URI getActionUrl(AuthenticatorContext context, String code, String action) {
|
public URI getActionUrl(AuthenticatorContext context, String code) {
|
||||||
return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
|
return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
|
||||||
.queryParam(OAuth2Constants.CODE, code)
|
.queryParam(OAuth2Constants.CODE, code)
|
||||||
.queryParam(ACTION, action)
|
.queryParam(EXECUTION, context.getExecution().getId())
|
||||||
.build(context.getRealm().getName());
|
.build(context.getRealm().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +117,7 @@ public class AbstractFormAuthenticator {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
context.getEvent().detail(Details.USERNAME, username);
|
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);
|
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
|
||||||
if (invalidUser(context, user)) return false;
|
if (invalidUser(context, user)) return false;
|
||||||
String rememberMe = inputData.getFirst("rememberMe");
|
String rememberMe = inputData.getFirst("rememberMe");
|
||||||
|
@ -119,6 +125,8 @@ public class AbstractFormAuthenticator {
|
||||||
if (remember) {
|
if (remember) {
|
||||||
context.getClientSession().setNote(Details.REMEMBER_ME, "true");
|
context.getClientSession().setNote(Details.REMEMBER_ME, "true");
|
||||||
context.getEvent().detail(Details.REMEMBER_ME, "true");
|
context.getEvent().detail(Details.REMEMBER_ME, "true");
|
||||||
|
} else {
|
||||||
|
context.getClientSession().removeNote(Details.REMEMBER_ME);
|
||||||
}
|
}
|
||||||
context.setUser(user);
|
context.setUser(user);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -32,6 +32,11 @@ public class CookieAuthenticator implements Authenticator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void action(AuthenticatorContext context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -31,14 +31,15 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void action(AuthenticatorContext context) {
|
||||||
|
validateOTP(context);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticatorContext context) {
|
||||||
if (!isAction(context, TOTP_FORM_ACTION)) {
|
|
||||||
Response challengeResponse = challenge(context, null);
|
Response challengeResponse = challenge(context, null);
|
||||||
context.challenge(challengeResponse);
|
context.challenge(challengeResponse);
|
||||||
return;
|
|
||||||
}
|
|
||||||
validateOTP(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateOTP(AuthenticatorContext context) {
|
public void validateOTP(AuthenticatorContext context) {
|
||||||
|
@ -69,7 +70,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
||||||
|
|
||||||
protected Response challenge(AuthenticatorContext context, String error) {
|
protected Response challenge(AuthenticatorContext context, String error) {
|
||||||
String accessCode = context.generateAccessCode();
|
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)
|
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
.setActionUri(action)
|
.setActionUri(action)
|
||||||
.setClientSessionCode(accessCode);
|
.setClientSessionCode(accessCode);
|
||||||
|
@ -91,6 +92,8 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
|
|
|
@ -39,26 +39,17 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isAlreadyChallenged(AuthenticatorContext context) {
|
@Override
|
||||||
UserSessionModel.AuthenticatorStatus status = context.getClientSession().getAuthenticators().get(context.getExecution().getId());
|
public void action(AuthenticatorContext context) {
|
||||||
if (status == null) return false;
|
context.attempted();
|
||||||
return status == UserSessionModel.AuthenticatorStatus.CHALLENGED;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticatorContext context) {
|
||||||
HttpRequest request = context.getHttpRequest();
|
HttpRequest request = context.getHttpRequest();
|
||||||
String authHeader = request.getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
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 (authHeader == null) {
|
||||||
if (isAlreadyChallenged(context)) {
|
|
||||||
context.attempted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Response challenge = challengeNegotiation(context, null);
|
Response challenge = challengeNegotiation(context, null);
|
||||||
context.forceChallenge(challenge);
|
context.forceChallenge(challenge);
|
||||||
return;
|
return;
|
||||||
|
@ -131,7 +122,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
||||||
*/
|
*/
|
||||||
protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) {
|
protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) {
|
||||||
String accessCode = context.generateAccessCode();
|
String accessCode = context.generateAccessCode();
|
||||||
URI action = getActionUrl(context, accessCode, KERBEROS_DISABLED);
|
URI action = getActionUrl(context, accessCode);
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
@ -159,18 +150,6 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
||||||
.entity(builder.toString()).build();
|
.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
|
@Override
|
||||||
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
|
|
|
@ -4,24 +4,18 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
import org.keycloak.authentication.Authenticator;
|
import org.keycloak.authentication.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
import org.keycloak.authentication.AuthenticatorContext;
|
||||||
import org.keycloak.events.Details;
|
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.AuthenticatorModel;
|
import org.keycloak.models.AuthenticatorModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.protocol.LoginProtocol;
|
import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -34,13 +28,34 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void action(AuthenticatorContext context) {
|
||||||
|
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
||||||
|
if (formData.containsKey("cancel")) {
|
||||||
|
context.getEvent().error(Errors.REJECTED_BY_USER);
|
||||||
|
LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, context.getClientSession().getAuthMethod());
|
||||||
|
protocol.setRealm(context.getRealm())
|
||||||
|
.setHttpHeaders(context.getHttpRequest().getHttpHeaders())
|
||||||
|
.setUriInfo(context.getUriInfo());
|
||||||
|
Response response = protocol.cancelLogin(context.getClientSession());
|
||||||
|
context.forceChallenge(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!validateUser(context, formData)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!validatePassword(context, formData)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.success();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticatorContext context) {
|
||||||
if (isAction(context, REGISTRATION_FORM_ACTION) && context.getUser() != null) {
|
if (REGISTRATION_FORM_ACTION.equals(context.getAction()) && context.getUser() != null) {
|
||||||
context.success();
|
context.success();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isAction(context, LOGIN_FORM_ACTION)) {
|
|
||||||
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
|
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
|
||||||
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
|
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
|
||||||
|
|
||||||
|
@ -55,29 +70,8 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Response challengeResponse = challenge(context, formData);
|
Response challengeResponse = challenge(context, formData);
|
||||||
|
context.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, context.getExecution().getId());
|
||||||
context.challenge(challengeResponse);
|
context.challenge(challengeResponse);
|
||||||
return;
|
|
||||||
}
|
|
||||||
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
|
||||||
if (formData.containsKey("cancel")) {
|
|
||||||
context.getEvent().error(Errors.REJECTED_BY_USER);
|
|
||||||
LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, context.getClientSession().getAuthMethod());
|
|
||||||
protocol.setRealm(context.getRealm())
|
|
||||||
.setHttpHeaders(context.getHttpRequest().getHttpHeaders())
|
|
||||||
.setUriInfo(context.getUriInfo());
|
|
||||||
Response response = protocol.cancelLogin(context.getClientSession());
|
|
||||||
context.challenge(response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validateUser(context, formData)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!validatePassword(context, formData)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context.success();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -142,25 +142,6 @@ public class ClientSessionCode {
|
||||||
clientSession.setTimestamp(Time.currentTime());
|
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() {
|
public String getCode() {
|
||||||
return generateCode(realm, clientSession);
|
return generateCode(realm, clientSession);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ package org.keycloak.services.resources;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
import org.jboss.resteasy.spi.InternalServerErrorException;
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.ClientConnection;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
import org.keycloak.authentication.AuthenticatorUtil;
|
import org.keycloak.authentication.AuthenticatorUtil;
|
||||||
|
@ -65,6 +66,7 @@ import org.keycloak.services.ErrorPage;
|
||||||
import org.keycloak.services.Urls;
|
import org.keycloak.services.Urls;
|
||||||
import org.keycloak.services.util.CookieHelper;
|
import org.keycloak.services.util.CookieHelper;
|
||||||
import org.keycloak.services.validation.Validation;
|
import org.keycloak.services.validation.Validation;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
@ -234,7 +236,7 @@ public class LoginActionsService {
|
||||||
@Path("authenticate")
|
@Path("authenticate")
|
||||||
@GET
|
@GET
|
||||||
public Response authenticate(@QueryParam("code") String code,
|
public Response authenticate(@QueryParam("code") String code,
|
||||||
@QueryParam("action") String action) {
|
@QueryParam("execution") String execution) {
|
||||||
event.event(EventType.LOGIN);
|
event.event(EventType.LOGIN);
|
||||||
Checks checks = new Checks();
|
Checks checks = new Checks();
|
||||||
if (!checks.check(code)) {
|
if (!checks.check(code)) {
|
||||||
|
@ -249,27 +251,57 @@ public class LoginActionsService {
|
||||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
|
return processAuthentication(execution, clientSession);
|
||||||
String flowId = flow.getId();
|
}
|
||||||
|
|
||||||
|
protected Response processAuthentication(String execution, ClientSessionModel clientSession) {
|
||||||
|
String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
|
||||||
|
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
|
||||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||||
processor.setClientSession(clientSession)
|
processor.setClientSession(clientSession)
|
||||||
.setFlowId(flowId)
|
.setFlowId(flow.getId())
|
||||||
.setConnection(clientConnection)
|
.setConnection(clientConnection)
|
||||||
.setEventBuilder(event)
|
.setEventBuilder(event)
|
||||||
.setProtector(authManager.getProtector())
|
.setProtector(authManager.getProtector())
|
||||||
.setAction(action)
|
|
||||||
.setRealm(realm)
|
.setRealm(realm)
|
||||||
.setSession(session)
|
.setSession(session)
|
||||||
.setUriInfo(uriInfo)
|
.setUriInfo(uriInfo)
|
||||||
.setRequest(request);
|
.setRequest(request);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (execution != null) {
|
||||||
|
return processor.authenticationAction(execution);
|
||||||
|
} else {
|
||||||
return processor.authenticate();
|
return processor.authenticate();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return processor.handleBrowserException(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
|
* protocol independent registration page entry point
|
||||||
*
|
*
|
||||||
|
@ -302,46 +334,6 @@ public class LoginActionsService {
|
||||||
.createRegistration();
|
.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
|
* Registration
|
||||||
|
@ -946,7 +938,7 @@ public class LoginActionsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("required-actions/{action}")
|
@Path("required-actions/{action}")
|
||||||
public Object requiredAction(@QueryParam("code") String code,
|
public Object requiredAction(@QueryParam("code") final String code,
|
||||||
@PathParam("action") String action) {
|
@PathParam("action") String action) {
|
||||||
event.event(EventType.LOGIN);
|
event.event(EventType.LOGIN);
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
|
@ -1024,6 +1016,11 @@ public class LoginActionsService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String generateAccessCode(String action) {
|
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());
|
ClientSessionCode code = new ClientSessionCode(getRealm(), getClientSession());
|
||||||
code.setAction(action);
|
code.setAction(action);
|
||||||
return code.getCode();
|
return code.getCode();
|
||||||
|
|
Loading…
Reference in a new issue