form auth
This commit is contained in:
parent
000159226d
commit
064d677fdc
32 changed files with 794 additions and 235 deletions
|
@ -12,6 +12,16 @@
|
||||||
<constraints nullable="true"/>
|
<constraints nullable="true"/>
|
||||||
</column>
|
</column>
|
||||||
</addColumn>
|
</addColumn>
|
||||||
|
<addColumn tableName="AUTHENTICATION_FLOW">
|
||||||
|
<column name="PROVIDER_ID" type="VARCHAR(36)" defaultValue="basic-flow">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
<addColumn tableName="AUTHENTICATION_EXECUTION">
|
||||||
|
<column name="AUTH_FLOW_ID" type="VARCHAR(36)">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
<dropColumn tableName="AUTHENTICATOR" columnName="PROVIDER_ID"/>
|
<dropColumn tableName="AUTHENTICATOR" columnName="PROVIDER_ID"/>
|
||||||
<renameTable oldTableName="AUTHENTICATOR_CONFIG" newTableName="AUTHENTICATOR_CONFIG_ENTRY"/>
|
<renameTable oldTableName="AUTHENTICATOR_CONFIG" newTableName="AUTHENTICATOR_CONFIG_ENTRY"/>
|
||||||
<renameTable oldTableName="AUTHENTICATOR" newTableName="AUTHENTICATOR_CONFIG"/>
|
<renameTable oldTableName="AUTHENTICATOR" newTableName="AUTHENTICATOR_CONFIG"/>
|
||||||
|
|
|
@ -22,6 +22,7 @@ public class AuthenticationExecutionModel implements Serializable {
|
||||||
private String id;
|
private String id;
|
||||||
private String authenticatorConfig;
|
private String authenticatorConfig;
|
||||||
private String authenticator;
|
private String authenticator;
|
||||||
|
private String flowId;
|
||||||
private boolean autheticatorFlow;
|
private boolean autheticatorFlow;
|
||||||
private Requirement requirement;
|
private Requirement requirement;
|
||||||
private boolean userSetupAllowed;
|
private boolean userSetupAllowed;
|
||||||
|
@ -84,6 +85,19 @@ public class AuthenticationExecutionModel implements Serializable {
|
||||||
this.parentFlow = parentFlow;
|
this.parentFlow = parentFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this execution is a flow, this is the flowId pointing to an AuthenticationFlowModel
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getFlowId() {
|
||||||
|
return flowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlowId(String flowId) {
|
||||||
|
this.flowId = flowId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the referenced authenticator a flow?
|
* Is the referenced authenticator a flow?
|
||||||
*
|
*
|
||||||
|
|
|
@ -12,6 +12,7 @@ public class AuthenticationFlowModel implements Serializable {
|
||||||
private String id;
|
private String id;
|
||||||
private String alias;
|
private String alias;
|
||||||
private String description;
|
private String description;
|
||||||
|
private String providerId;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -36,4 +37,12 @@ public class AuthenticationFlowModel implements Serializable {
|
||||||
public void setDescription(String description) {
|
public void setDescription(String description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getProviderId() {
|
||||||
|
return providerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProviderId(String providerId) {
|
||||||
|
this.providerId = providerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
public class AuthenticationExecutionEntity {
|
public class AuthenticationExecutionEntity {
|
||||||
protected String id;
|
protected String id;
|
||||||
protected String authenticator;
|
protected String authenticator;
|
||||||
|
protected String flowId;
|
||||||
protected AuthenticationExecutionModel.Requirement requirement;
|
protected AuthenticationExecutionModel.Requirement requirement;
|
||||||
protected int priority;
|
protected int priority;
|
||||||
private boolean userSetupAllowed;
|
private boolean userSetupAllowed;
|
||||||
|
@ -71,4 +72,12 @@ public class AuthenticationExecutionEntity {
|
||||||
public void setParentFlow(String parentFlow) {
|
public void setParentFlow(String parentFlow) {
|
||||||
this.parentFlow = parentFlow;
|
this.parentFlow = parentFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFlowId() {
|
||||||
|
return flowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlowId(String flowId) {
|
||||||
|
this.flowId = flowId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ public class AuthenticationFlowEntity {
|
||||||
protected String id;
|
protected String id;
|
||||||
protected String alias;
|
protected String alias;
|
||||||
protected String description;
|
protected String description;
|
||||||
|
protected String providerId;
|
||||||
|
|
||||||
List<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
|
List<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -44,4 +46,12 @@ public class AuthenticationFlowEntity {
|
||||||
public void setExecutions(List<AuthenticationExecutionEntity> executions) {
|
public void setExecutions(List<AuthenticationExecutionEntity> executions) {
|
||||||
this.executions = executions;
|
this.executions = executions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getProviderId() {
|
||||||
|
return providerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProviderId(String providerId) {
|
||||||
|
this.providerId = providerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ public class DefaultAuthenticationFlows {
|
||||||
AuthenticationFlowModel browser = new AuthenticationFlowModel();
|
AuthenticationFlowModel browser = new AuthenticationFlowModel();
|
||||||
browser.setAlias(BROWSER_FLOW);
|
browser.setAlias(BROWSER_FLOW);
|
||||||
browser.setDescription("browser based authentication");
|
browser.setDescription("browser based authentication");
|
||||||
|
browser.setProviderId("basic-flow");
|
||||||
browser = realm.addAuthenticationFlow(browser);
|
browser = realm.addAuthenticationFlow(browser);
|
||||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||||
execution.setParentFlow(browser.getId());
|
execution.setParentFlow(browser.getId());
|
||||||
|
@ -40,11 +41,12 @@ public class DefaultAuthenticationFlows {
|
||||||
AuthenticationFlowModel forms = new AuthenticationFlowModel();
|
AuthenticationFlowModel forms = new AuthenticationFlowModel();
|
||||||
forms.setAlias(FORMS_FLOW);
|
forms.setAlias(FORMS_FLOW);
|
||||||
forms.setDescription("Username, password, otp and other auth forms.");
|
forms.setDescription("Username, password, otp and other auth forms.");
|
||||||
|
forms.setProviderId("basic-flow");
|
||||||
forms = realm.addAuthenticationFlow(forms);
|
forms = realm.addAuthenticationFlow(forms);
|
||||||
execution = new AuthenticationExecutionModel();
|
execution = new AuthenticationExecutionModel();
|
||||||
execution.setParentFlow(browser.getId());
|
execution.setParentFlow(browser.getId());
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||||
execution.setAuthenticator(forms.getId());
|
execution.setFlowId(forms.getId());
|
||||||
execution.setPriority(30);
|
execution.setPriority(30);
|
||||||
execution.setUserSetupAllowed(false);
|
execution.setUserSetupAllowed(false);
|
||||||
execution.setAutheticatorFlow(true);
|
execution.setAutheticatorFlow(true);
|
||||||
|
|
|
@ -1233,6 +1233,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
model.setId(entity.getId());
|
model.setId(entity.getId());
|
||||||
model.setAlias(entity.getAlias());
|
model.setAlias(entity.getAlias());
|
||||||
model.setDescription(entity.getDescription());
|
model.setDescription(entity.getDescription());
|
||||||
|
model.setProviderId(entity.getProviderId());
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1266,6 +1267,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
if (toUpdate == null) return;
|
if (toUpdate == null) return;
|
||||||
toUpdate.setAlias(model.getAlias());
|
toUpdate.setAlias(model.getAlias());
|
||||||
toUpdate.setDescription(model.getDescription());
|
toUpdate.setDescription(model.getDescription());
|
||||||
|
toUpdate.setProviderId(model.getProviderId());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1275,6 +1277,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
entity.setId(KeycloakModelUtils.generateId());
|
entity.setId(KeycloakModelUtils.generateId());
|
||||||
entity.setAlias(model.getAlias());
|
entity.setAlias(model.getAlias());
|
||||||
entity.setDescription(model.getDescription());
|
entity.setDescription(model.getDescription());
|
||||||
|
entity.setProviderId(model.getProviderId());
|
||||||
realm.getAuthenticationFlows().add(entity);
|
realm.getAuthenticationFlows().add(entity);
|
||||||
model.setId(entity.getId());
|
model.setId(entity.getId());
|
||||||
return model;
|
return model;
|
||||||
|
@ -1303,6 +1306,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
model.setPriority(entity.getPriority());
|
model.setPriority(entity.getPriority());
|
||||||
model.setAuthenticator(entity.getAuthenticator());
|
model.setAuthenticator(entity.getAuthenticator());
|
||||||
model.setParentFlow(entity.getParentFlow());
|
model.setParentFlow(entity.getParentFlow());
|
||||||
|
model.setFlowId(entity.getFlowId());
|
||||||
model.setAutheticatorFlow(entity.isAuthenticatorFlow());
|
model.setAutheticatorFlow(entity.isAuthenticatorFlow());
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -1334,6 +1338,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
entity.setRequirement(model.getRequirement());
|
entity.setRequirement(model.getRequirement());
|
||||||
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
||||||
entity.setAuthenticatorFlow(model.isAutheticatorFlow());
|
entity.setAuthenticatorFlow(model.isAutheticatorFlow());
|
||||||
|
entity.setFlowId(model.getFlowId());
|
||||||
AuthenticationFlowEntity flow = getFlowEntity(model.getId());
|
AuthenticationFlowEntity flow = getFlowEntity(model.getId());
|
||||||
flow.getExecutions().add(entity);
|
flow.getExecutions().add(entity);
|
||||||
model.setId(entity.getId());
|
model.setId(entity.getId());
|
||||||
|
@ -1355,6 +1360,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
entity.setAuthenticator(model.getAuthenticator());
|
entity.setAuthenticator(model.getAuthenticator());
|
||||||
entity.setPriority(model.getPriority());
|
entity.setPriority(model.getPriority());
|
||||||
entity.setRequirement(model.getRequirement());
|
entity.setRequirement(model.getRequirement());
|
||||||
|
entity.setFlowId(model.getFlowId());
|
||||||
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1542,6 +1542,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
AuthenticationFlowModel model = new AuthenticationFlowModel();
|
AuthenticationFlowModel model = new AuthenticationFlowModel();
|
||||||
model.setId(entity.getId());
|
model.setId(entity.getId());
|
||||||
model.setAlias(entity.getAlias());
|
model.setAlias(entity.getAlias());
|
||||||
|
model.setProviderId(entity.getProviderId());
|
||||||
model.setDescription(entity.getDescription());
|
model.setDescription(entity.getDescription());
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -1567,6 +1568,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
if (entity == null) return;
|
if (entity == null) return;
|
||||||
entity.setAlias(model.getAlias());
|
entity.setAlias(model.getAlias());
|
||||||
entity.setDescription(model.getDescription());
|
entity.setDescription(model.getDescription());
|
||||||
|
entity.setProviderId(model.getProviderId());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1576,6 +1578,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
entity.setId(KeycloakModelUtils.generateId());
|
entity.setId(KeycloakModelUtils.generateId());
|
||||||
entity.setAlias(model.getAlias());
|
entity.setAlias(model.getAlias());
|
||||||
entity.setDescription(model.getDescription());
|
entity.setDescription(model.getDescription());
|
||||||
|
entity.setProviderId(model.getProviderId());
|
||||||
entity.setRealm(realm);
|
entity.setRealm(realm);
|
||||||
realm.getAuthenticationFlows().add(entity);
|
realm.getAuthenticationFlows().add(entity);
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
|
@ -1589,7 +1592,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
TypedQuery<AuthenticationExecutionEntity> query = em.createNamedQuery("getAuthenticationExecutionsByFlow", AuthenticationExecutionEntity.class);
|
TypedQuery<AuthenticationExecutionEntity> query = em.createNamedQuery("getAuthenticationExecutionsByFlow", AuthenticationExecutionEntity.class);
|
||||||
AuthenticationFlowEntity flow = em.getReference(AuthenticationFlowEntity.class, flowId);
|
AuthenticationFlowEntity flow = em.getReference(AuthenticationFlowEntity.class, flowId);
|
||||||
query.setParameter("realm", realm);
|
query.setParameter("realm", realm);
|
||||||
query.setParameter("flow", flow);
|
query.setParameter("parentFlow", flow);
|
||||||
List<AuthenticationExecutionEntity> queryResult = query.getResultList();
|
List<AuthenticationExecutionEntity> queryResult = query.getResultList();
|
||||||
List<AuthenticationExecutionModel> executions = new LinkedList<>();
|
List<AuthenticationExecutionModel> executions = new LinkedList<>();
|
||||||
for (AuthenticationExecutionEntity entity : queryResult) {
|
for (AuthenticationExecutionEntity entity : queryResult) {
|
||||||
|
@ -1607,7 +1610,8 @@ public class RealmAdapter implements RealmModel {
|
||||||
model.setRequirement(entity.getRequirement());
|
model.setRequirement(entity.getRequirement());
|
||||||
model.setPriority(entity.getPriority());
|
model.setPriority(entity.getPriority());
|
||||||
model.setAuthenticator(entity.getAuthenticator());
|
model.setAuthenticator(entity.getAuthenticator());
|
||||||
model.setParentFlow(entity.getFlow().getId());
|
model.setFlowId(entity.getFlowId());
|
||||||
|
model.setParentFlow(entity.getParentFlow().getId());
|
||||||
model.setAutheticatorFlow(entity.isAutheticatorFlow());
|
model.setAutheticatorFlow(entity.isAutheticatorFlow());
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -1625,9 +1629,10 @@ public class RealmAdapter implements RealmModel {
|
||||||
entity.setId(KeycloakModelUtils.generateId());
|
entity.setId(KeycloakModelUtils.generateId());
|
||||||
entity.setAuthenticator(model.getAuthenticator());
|
entity.setAuthenticator(model.getAuthenticator());
|
||||||
entity.setPriority(model.getPriority());
|
entity.setPriority(model.getPriority());
|
||||||
|
entity.setFlowId(model.getFlowId());
|
||||||
entity.setRequirement(model.getRequirement());
|
entity.setRequirement(model.getRequirement());
|
||||||
AuthenticationFlowEntity flow = em.find(AuthenticationFlowEntity.class, model.getParentFlow());
|
AuthenticationFlowEntity flow = em.find(AuthenticationFlowEntity.class, model.getParentFlow());
|
||||||
entity.setFlow(flow);
|
entity.setParentFlow(flow);
|
||||||
flow.getExecutions().add(entity);
|
flow.getExecutions().add(entity);
|
||||||
entity.setRealm(realm);
|
entity.setRealm(realm);
|
||||||
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
||||||
|
@ -1648,6 +1653,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
entity.setPriority(model.getPriority());
|
entity.setPriority(model.getPriority());
|
||||||
entity.setRequirement(model.getRequirement());
|
entity.setRequirement(model.getRequirement());
|
||||||
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
||||||
|
entity.setFlowId(model.getFlowId());
|
||||||
em.flush();
|
em.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,9 @@ import javax.persistence.Table;
|
||||||
@Table(name="AUTHENTICATION_EXECUTION")
|
@Table(name="AUTHENTICATION_EXECUTION")
|
||||||
@Entity
|
@Entity
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name="getAuthenticationExecutionsByFlow", query="select authenticator from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.flow = :flow"),
|
@NamedQuery(name="getAuthenticationExecutionsByFlow", query="select authenticator from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.parentFlow = :parentFlow"),
|
||||||
@NamedQuery(name="deleteAuthenticationExecutionsByRealm", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm"),
|
@NamedQuery(name="deleteAuthenticationExecutionsByRealm", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm"),
|
||||||
@NamedQuery(name="deleteAuthenticationExecutionsByRealmAndFlow", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.flow = :flow"),
|
@NamedQuery(name="deleteAuthenticationExecutionsByRealmAndFlow", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.parentFlow = :parentFlow"),
|
||||||
})
|
})
|
||||||
public class AuthenticationExecutionEntity {
|
public class AuthenticationExecutionEntity {
|
||||||
@Id
|
@Id
|
||||||
|
@ -34,11 +34,14 @@ public class AuthenticationExecutionEntity {
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "FLOW_ID")
|
@JoinColumn(name = "FLOW_ID")
|
||||||
protected AuthenticationFlowEntity flow;
|
protected AuthenticationFlowEntity parentFlow;
|
||||||
|
|
||||||
@Column(name="AUTHENTICATOR")
|
@Column(name="AUTHENTICATOR")
|
||||||
protected String authenticator;
|
protected String authenticator;
|
||||||
|
|
||||||
|
@Column(name="AUTH_FLOW_ID")
|
||||||
|
protected String flowId;
|
||||||
|
|
||||||
@Column(name="REQUIREMENT")
|
@Column(name="REQUIREMENT")
|
||||||
protected AuthenticationExecutionModel.Requirement requirement;
|
protected AuthenticationExecutionModel.Requirement requirement;
|
||||||
|
|
||||||
|
@ -107,11 +110,19 @@ public class AuthenticationExecutionEntity {
|
||||||
this.autheticatorFlow = autheticatorFlow;
|
this.autheticatorFlow = autheticatorFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationFlowEntity getFlow() {
|
public AuthenticationFlowEntity getParentFlow() {
|
||||||
return flow;
|
return parentFlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFlow(AuthenticationFlowEntity flow) {
|
public void setParentFlow(AuthenticationFlowEntity flow) {
|
||||||
this.flow = flow;
|
this.parentFlow = flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFlowId() {
|
||||||
|
return flowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlowId(String flowId) {
|
||||||
|
this.flowId = flowId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,13 @@ public class AuthenticationFlowEntity {
|
||||||
@Column(name="ALIAS")
|
@Column(name="ALIAS")
|
||||||
protected String alias;
|
protected String alias;
|
||||||
|
|
||||||
|
@Column(name="PROVIDER_ID")
|
||||||
|
protected String providerId;
|
||||||
|
|
||||||
@Column(name="DESCRIPTION")
|
@Column(name="DESCRIPTION")
|
||||||
protected String description;
|
protected String description;
|
||||||
|
|
||||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "flow")
|
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "parentFlow")
|
||||||
Collection<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
|
Collection<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -80,4 +83,12 @@ public class AuthenticationFlowEntity {
|
||||||
public void setExecutions(Collection<AuthenticationExecutionEntity> executions) {
|
public void setExecutions(Collection<AuthenticationExecutionEntity> executions) {
|
||||||
this.executions = executions;
|
this.executions = executions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getProviderId() {
|
||||||
|
return providerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProviderId(String providerId) {
|
||||||
|
this.providerId = providerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1341,6 +1341,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
if (toUpdate == null) return;
|
if (toUpdate == null) return;
|
||||||
toUpdate.setAlias(model.getAlias());
|
toUpdate.setAlias(model.getAlias());
|
||||||
toUpdate.setDescription(model.getDescription());
|
toUpdate.setDescription(model.getDescription());
|
||||||
|
toUpdate.setProviderId(model.getProviderId());
|
||||||
updateMongoEntity();
|
updateMongoEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1350,6 +1351,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
entity.setId(KeycloakModelUtils.generateId());
|
entity.setId(KeycloakModelUtils.generateId());
|
||||||
entity.setAlias(model.getAlias());
|
entity.setAlias(model.getAlias());
|
||||||
entity.setDescription(model.getDescription());
|
entity.setDescription(model.getDescription());
|
||||||
|
entity.setProviderId(model.getProviderId());
|
||||||
getMongoEntity().getAuthenticationFlows().add(entity);
|
getMongoEntity().getAuthenticationFlows().add(entity);
|
||||||
model.setId(entity.getId());
|
model.setId(entity.getId());
|
||||||
updateMongoEntity();
|
updateMongoEntity();
|
||||||
|
@ -1378,6 +1380,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
model.setRequirement(entity.getRequirement());
|
model.setRequirement(entity.getRequirement());
|
||||||
model.setPriority(entity.getPriority());
|
model.setPriority(entity.getPriority());
|
||||||
model.setAuthenticator(entity.getAuthenticator());
|
model.setAuthenticator(entity.getAuthenticator());
|
||||||
|
model.setFlowId(entity.getFlowId());
|
||||||
model.setParentFlow(entity.getParentFlow());
|
model.setParentFlow(entity.getParentFlow());
|
||||||
model.setAutheticatorFlow(entity.isAuthenticatorFlow());
|
model.setAutheticatorFlow(entity.isAuthenticatorFlow());
|
||||||
return model;
|
return model;
|
||||||
|
@ -1410,6 +1413,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
entity.setRequirement(model.getRequirement());
|
entity.setRequirement(model.getRequirement());
|
||||||
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
||||||
entity.setAuthenticatorFlow(model.isAutheticatorFlow());
|
entity.setAuthenticatorFlow(model.isAutheticatorFlow());
|
||||||
|
entity.setFlowId(model.getFlowId());
|
||||||
entity.setParentFlow(model.getParentFlow());
|
entity.setParentFlow(model.getParentFlow());
|
||||||
AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
|
AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
|
||||||
flow.getExecutions().add(entity);
|
flow.getExecutions().add(entity);
|
||||||
|
@ -1433,6 +1437,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
entity.setAuthenticator(model.getAuthenticator());
|
entity.setAuthenticator(model.getAuthenticator());
|
||||||
entity.setPriority(model.getPriority());
|
entity.setPriority(model.getPriority());
|
||||||
entity.setRequirement(model.getRequirement());
|
entity.setRequirement(model.getRequirement());
|
||||||
|
entity.setFlowId(model.getFlowId());
|
||||||
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
entity.setUserSetupAllowed(model.isUserSetupAllowed());
|
||||||
updateMongoEntity();
|
updateMongoEntity();
|
||||||
}
|
}
|
||||||
|
|
12
services/src/main/java/org/keycloak/authentication/AuthenticationFlow.java
Executable file
12
services/src/main/java/org/keycloak/authentication/AuthenticationFlow.java
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface AuthenticationFlow {
|
||||||
|
Response processAction(String actionExecution);
|
||||||
|
Response processFlow();
|
||||||
|
}
|
|
@ -26,7 +26,6 @@ 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.Iterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -64,6 +63,7 @@ public class AuthenticationProcessor {
|
||||||
ATTEMPTED
|
ATTEMPTED
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enum Error {
|
public static enum Error {
|
||||||
EXPIRED_CODE,
|
EXPIRED_CODE,
|
||||||
INVALID_CLIENT_SESSION,
|
INVALID_CLIENT_SESSION,
|
||||||
|
@ -229,12 +229,14 @@ public class AuthenticationProcessor {
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void forceChallenge(Response challenge) {
|
public void forceChallenge(Response challenge) {
|
||||||
this.status = Status.FORCE_CHALLENGE;
|
this.status = Status.FORCE_CHALLENGE;
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failureChallenge(Error error, Response challenge) {
|
public void failureChallenge(Error error, Response challenge) {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
|
@ -242,6 +244,7 @@ public class AuthenticationProcessor {
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failure(Error error, Response challenge) {
|
public void failure(Error error, Response challenge) {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
|
@ -264,7 +267,8 @@ public class AuthenticationProcessor {
|
||||||
@Override
|
@Override
|
||||||
public void setUser(UserModel user) {
|
public void setUser(UserModel user) {
|
||||||
UserModel previousUser = getUser();
|
UserModel previousUser = getUser();
|
||||||
if (previousUser != null && !user.getId().equals(previousUser.getId())) throw new AuthException(Error.USER_CONFLICT);
|
if (previousUser != null && !user.getId().equals(previousUser.getId()))
|
||||||
|
throw new AuthException(Error.USER_CONFLICT);
|
||||||
validateUser(user);
|
validateUser(user);
|
||||||
getClientSession().setAuthenticatedUser(user);
|
getClientSession().setAuthenticatedUser(user);
|
||||||
}
|
}
|
||||||
|
@ -390,7 +394,7 @@ public class AuthenticationProcessor {
|
||||||
|
|
||||||
public Response handleBrowserException(Exception failure) {
|
public Response handleBrowserException(Exception failure) {
|
||||||
if (failure instanceof AuthException) {
|
if (failure instanceof AuthException) {
|
||||||
AuthException e = (AuthException)failure;
|
AuthException e = (AuthException) failure;
|
||||||
logger.error("failed authentication: " + e.getError().toString(), e);
|
logger.error("failed authentication: " + e.getError().toString(), e);
|
||||||
if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
|
if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
|
||||||
event.error(Errors.USER_NOT_FOUND);
|
event.error(Errors.USER_NOT_FOUND);
|
||||||
|
@ -406,11 +410,11 @@ public class AuthenticationProcessor {
|
||||||
event.error(Errors.INVALID_CODE);
|
event.error(Errors.INVALID_CODE);
|
||||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||||
|
|
||||||
} else if (e.getError() == Error.EXPIRED_CODE) {
|
} else if (e.getError() == Error.EXPIRED_CODE) {
|
||||||
event.error(Errors.EXPIRED_CODE);
|
event.error(Errors.EXPIRED_CODE);
|
||||||
return ErrorPage.error(session, Messages.EXPIRED_CODE);
|
return ErrorPage.error(session, Messages.EXPIRED_CODE);
|
||||||
|
|
||||||
}else {
|
} else {
|
||||||
event.error(Errors.INVALID_USER_CREDENTIALS);
|
event.error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
return ErrorPage.error(session, Messages.INVALID_USER);
|
return ErrorPage.error(session, Messages.INVALID_USER);
|
||||||
}
|
}
|
||||||
|
@ -423,15 +427,22 @@ public class AuthenticationProcessor {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowExecution createFlowExecution(String flowId) {
|
public AuthenticationFlow createFlowExecution(String flowId, AuthenticationExecutionModel execution) {
|
||||||
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
|
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
|
||||||
if (flow == null) {
|
if (flow == null) {
|
||||||
logger.error("Unknown flow to execute with");
|
logger.error("Unknown flow to execute with");
|
||||||
throw new AuthException(Error.INTERNAL_ERROR);
|
throw new AuthException(Error.INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
FlowExecution flowExecution = new FlowExecution();
|
if (flow.getProviderId() == null || flow.getProviderId().equals("basic-flow")) {
|
||||||
flowExecution.executions = realm.getAuthenticationExecutions(flow.getId()).iterator();
|
DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this);
|
||||||
return flowExecution;
|
flowExecution.executions = realm.getAuthenticationExecutions(flow.getId()).iterator();
|
||||||
|
return flowExecution;
|
||||||
|
|
||||||
|
} else if (flow.getProviderId().equals("form-flow")) {
|
||||||
|
FormAuthenticationFlow flowExecution = new FormAuthenticationFlow(this, execution);
|
||||||
|
return flowExecution;
|
||||||
|
}
|
||||||
|
throw new AuthException("Unknown flow provider type", Error.INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response authenticate() throws AuthException {
|
public Response authenticate() throws AuthException {
|
||||||
|
@ -447,8 +458,8 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
UserModel authUser = clientSession.getAuthenticatedUser();
|
UserModel authUser = clientSession.getAuthenticatedUser();
|
||||||
validateUser(authUser);
|
validateUser(authUser);
|
||||||
FlowExecution flowExecution = createFlowExecution(this.flowId);
|
AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
|
||||||
Response challenge = flowExecution.processFlow();
|
Response challenge = authenticationFlow.processFlow();
|
||||||
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);
|
||||||
|
@ -456,7 +467,7 @@ public class AuthenticationProcessor {
|
||||||
return authenticationComplete();
|
return authenticationComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void resetFlow(ClientSessionModel clientSession) {
|
public static void resetFlow(ClientSessionModel clientSession) {
|
||||||
clientSession.setAuthenticatedUser(null);
|
clientSession.setAuthenticatedUser(null);
|
||||||
clientSession.clearExecutionStatus();
|
clientSession.clearExecutionStatus();
|
||||||
clientSession.clearUserSessionNotes();
|
clientSession.clearUserSessionNotes();
|
||||||
|
@ -488,8 +499,8 @@ public class AuthenticationProcessor {
|
||||||
event.detail(Details.AUTH_TYPE, authType);
|
event.detail(Details.AUTH_TYPE, authType);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlowExecution flowExecution = createFlowExecution(this.flowId);
|
AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, model);
|
||||||
Response challenge = flowExecution.action(execution);
|
Response challenge = authenticationFlow.processAction(execution);
|
||||||
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);
|
||||||
|
@ -520,8 +531,8 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
UserModel authUser = clientSession.getAuthenticatedUser();
|
UserModel authUser = clientSession.getAuthenticatedUser();
|
||||||
validateUser(authUser);
|
validateUser(authUser);
|
||||||
FlowExecution flowExecution = createFlowExecution(this.flowId);
|
AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
|
||||||
Response challenge = flowExecution.processFlow();
|
Response challenge = authenticationFlow.processFlow();
|
||||||
if (challenge != null) return challenge;
|
if (challenge != null) return challenge;
|
||||||
|
|
||||||
String username = clientSession.getAuthenticatedUser().getUsername();
|
String username = clientSession.getAuthenticatedUser().getUsername();
|
||||||
|
@ -569,189 +580,16 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
TokenManager.attachClientSession(userSession, clientSession);
|
TokenManager.attachClientSession(userSession, clientSession);
|
||||||
event.user(userSession.getUser())
|
event.user(userSession.getUser())
|
||||||
.detail(Details.USERNAME, username)
|
.detail(Details.USERNAME, username)
|
||||||
.session(userSession);
|
.session(userSession);
|
||||||
|
|
||||||
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, connection, request, uriInfo, event);
|
return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, connection, request, uriInfo, event);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlowExecution {
|
public AuthenticatorContext createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator) {
|
||||||
Response alternativeChallenge = null;
|
return new Result(model, authenticator);
|
||||||
AuthenticationExecutionModel challengedAlternativeExecution = null;
|
}
|
||||||
boolean alternativeSuccessful = false;
|
|
||||||
Iterator<AuthenticationExecutionModel> executions;
|
|
||||||
|
|
||||||
protected boolean isProcessed(AuthenticationExecutionModel model) {
|
|
||||||
if (model.isDisabled()) return true;
|
|
||||||
ClientSessionModel.ExecutionStatus status = clientSession.getExecutionStatus().get(model.getId());
|
|
||||||
if (status == null) return false;
|
|
||||||
return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
|
|
||||||
|| status == ClientSessionModel.ExecutionStatus.ATTEMPTED
|
|
||||||
|| status == ClientSessionModel.ExecutionStatus.SETUP_REQUIRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Response action(String actionExecution) {
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
throw new AuthException("action is not current execution", Error.INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
} else { // we found the action
|
|
||||||
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
|
||||||
Authenticator authenticator = factory.create();
|
|
||||||
Result result = new Result(model, authenticator);
|
|
||||||
authenticator.action(result);
|
|
||||||
Response response = processResult(result);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
|
||||||
Authenticator authenticator = factory.create();
|
|
||||||
logger.debugv("authenticator: {0}", factory.getId());
|
|
||||||
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: " + factory.getId(), 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}", factory.getId());
|
|
||||||
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, authenticator);
|
|
||||||
authenticator.authenticate(context);
|
|
||||||
Response response = processResult(context);
|
|
||||||
if (response != null) return response;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Response processResult(AuthenticatorContext result) {
|
|
||||||
AuthenticationExecutionModel execution = result.getExecution();
|
|
||||||
Status status = result.getStatus();
|
|
||||||
if (status == Status.SUCCESS){
|
|
||||||
logger.debugv("authenticator SUCCESS: {0}", execution.getAuthenticator());
|
|
||||||
clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
|
||||||
if (execution.isAlternative()) alternativeSuccessful = true;
|
|
||||||
return null;
|
|
||||||
} else if (status == Status.FAILED) {
|
|
||||||
logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
|
|
||||||
logFailure();
|
|
||||||
clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
|
|
||||||
if (result.getChallenge() != null) {
|
|
||||||
return sendChallenge(result, execution);
|
|
||||||
}
|
|
||||||
throw new AuthException(result.getError());
|
|
||||||
} 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}", execution.getAuthenticator());
|
|
||||||
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.getChallenge();
|
|
||||||
challengedAlternativeExecution = execution;
|
|
||||||
} else {
|
|
||||||
clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} else if (status == Status.FAILURE_CHALLENGE) {
|
|
||||||
logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
|
|
||||||
logFailure();
|
|
||||||
clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
|
||||||
return sendChallenge(result, execution);
|
|
||||||
} else if (status == Status.ATTEMPTED) {
|
|
||||||
logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
|
|
||||||
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}", execution.getAuthenticator());
|
|
||||||
logger.error("Unknown result status");
|
|
||||||
throw new AuthException(Error.INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
|
|
||||||
clientSession.setNote(CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
|
|
||||||
return result.getChallenge();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import org.keycloak.provider.Provider;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface Authenticator extends Provider {
|
public interface Authenticator extends Provider {
|
||||||
boolean requiresUser();
|
|
||||||
void authenticate(AuthenticatorContext context);
|
void authenticate(AuthenticatorContext context);
|
||||||
|
boolean requiresUser();
|
||||||
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
|
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class AuthenticatorUtil {
|
||||||
for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
|
for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
|
||||||
executions.add(model);
|
executions.add(model);
|
||||||
if (model.isAutheticatorFlow() && model.isEnabled()) {
|
if (model.isAutheticatorFlow() && model.isEnabled()) {
|
||||||
recurseExecutions(realm, model.getAuthenticator(), executions);
|
recurseExecutions(realm, model.getFlowId(), executions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public class AuthenticatorUtil {
|
||||||
public static AuthenticationExecutionModel findExecutionByAuthenticator(RealmModel realm, String flowId, String authProviderId) {
|
public static AuthenticationExecutionModel findExecutionByAuthenticator(RealmModel realm, String flowId, String authProviderId) {
|
||||||
for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
|
for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
|
||||||
if (model.isAutheticatorFlow()) {
|
if (model.isAutheticatorFlow()) {
|
||||||
AuthenticationExecutionModel recurse = findExecutionByAuthenticator(realm, model.getAuthenticator(), authProviderId);
|
AuthenticationExecutionModel recurse = findExecutionByAuthenticator(realm, model.getFlowId(), authProviderId);
|
||||||
if (recurse != null) return recurse;
|
if (recurse != null) return recurse;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
||||||
|
Response alternativeChallenge = null;
|
||||||
|
AuthenticationExecutionModel challengedAlternativeExecution = null;
|
||||||
|
boolean alternativeSuccessful = false;
|
||||||
|
Iterator<AuthenticationExecutionModel> executions;
|
||||||
|
AuthenticationProcessor processor;
|
||||||
|
|
||||||
|
public DefaultAuthenticationFlow(AuthenticationProcessor processor) {
|
||||||
|
this.processor = processor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isProcessed(AuthenticationExecutionModel model) {
|
||||||
|
if (model.isDisabled()) return true;
|
||||||
|
ClientSessionModel.ExecutionStatus status = processor.getClientSession().getExecutionStatus().get(model.getId());
|
||||||
|
if (status == null) return false;
|
||||||
|
return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
|
||||||
|
|| status == ClientSessionModel.ExecutionStatus.ATTEMPTED
|
||||||
|
|| status == ClientSessionModel.ExecutionStatus.SETUP_REQUIRED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response processAction(String actionExecution) {
|
||||||
|
while (executions.hasNext()) {
|
||||||
|
AuthenticationExecutionModel model = executions.next();
|
||||||
|
if (isProcessed(model)) {
|
||||||
|
AuthenticationProcessor.logger.debug("execution is processed");
|
||||||
|
if (!alternativeSuccessful && model.isAlternative() && processor.isSuccessful(model))
|
||||||
|
alternativeSuccessful = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!model.getId().equals(actionExecution)) {
|
||||||
|
if (model.isAutheticatorFlow()) {
|
||||||
|
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
|
||||||
|
return authenticationFlow.processAction(actionExecution);
|
||||||
|
} else {
|
||||||
|
throw new AuthenticationProcessor.AuthException("action is not current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
} else { // we found the action
|
||||||
|
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
||||||
|
Authenticator authenticator = factory.create();
|
||||||
|
AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator);
|
||||||
|
authenticator.action(result);
|
||||||
|
Response response = processResult(result);
|
||||||
|
if (response == null) return processFlow();
|
||||||
|
else return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AuthenticationProcessor.AuthException("action is not in current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response processFlow() {
|
||||||
|
while (executions.hasNext()) {
|
||||||
|
AuthenticationExecutionModel model = executions.next();
|
||||||
|
if (isProcessed(model)) {
|
||||||
|
AuthenticationProcessor.logger.debug("execution is processed");
|
||||||
|
if (!alternativeSuccessful && model.isAlternative() && processor.isSuccessful(model))
|
||||||
|
alternativeSuccessful = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (model.isAlternative() && alternativeSuccessful) {
|
||||||
|
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (model.isAutheticatorFlow()) {
|
||||||
|
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
|
||||||
|
Response flowChallenge = authenticationFlow.processFlow();
|
||||||
|
if (flowChallenge == null) {
|
||||||
|
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||||
|
if (model.isAlternative()) alternativeSuccessful = true;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (model.isAlternative()) {
|
||||||
|
alternativeChallenge = flowChallenge;
|
||||||
|
challengedAlternativeExecution = model;
|
||||||
|
} else if (model.isRequired()) {
|
||||||
|
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return flowChallenge;
|
||||||
|
} else if (model.isOptional()) {
|
||||||
|
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return flowChallenge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
||||||
|
Authenticator authenticator = factory.create();
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator: {0}", factory.getId());
|
||||||
|
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
|
||||||
|
|
||||||
|
if (authenticator.requiresUser() && authUser == null) {
|
||||||
|
if (alternativeChallenge != null) {
|
||||||
|
processor.getClientSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return alternativeChallenge;
|
||||||
|
}
|
||||||
|
throw new AuthenticationProcessor.AuthException("authenticator: " + factory.getId(), AuthenticationProcessor.Error.UNKNOWN_USER);
|
||||||
|
}
|
||||||
|
boolean configuredFor = false;
|
||||||
|
if (authenticator.requiresUser() && authUser != null) {
|
||||||
|
configuredFor = authenticator.configuredFor(processor.getSession(), processor.getRealm(), authUser);
|
||||||
|
if (!configuredFor) {
|
||||||
|
if (model.isRequired()) {
|
||||||
|
if (model.isUserSetupAllowed()) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
|
||||||
|
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
|
||||||
|
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
|
||||||
|
}
|
||||||
|
} else if (model.isOptional()) {
|
||||||
|
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AuthenticatorContext context = processor.createAuthenticatorContext(model, authenticator);
|
||||||
|
authenticator.authenticate(context);
|
||||||
|
Response response = processResult(context);
|
||||||
|
if (response != null) return response;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Response processResult(AuthenticatorContext result) {
|
||||||
|
AuthenticationExecutionModel execution = result.getExecution();
|
||||||
|
AuthenticationProcessor.Status status = result.getStatus();
|
||||||
|
if (status == AuthenticationProcessor.Status.SUCCESS) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator SUCCESS: {0}", execution.getAuthenticator());
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||||
|
if (execution.isAlternative()) alternativeSuccessful = true;
|
||||||
|
return null;
|
||||||
|
} else if (status == AuthenticationProcessor.Status.FAILED) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
|
||||||
|
processor.logFailure();
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
|
||||||
|
if (result.getChallenge() != null) {
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
}
|
||||||
|
throw new AuthenticationProcessor.AuthException(result.getError());
|
||||||
|
} else if (status == AuthenticationProcessor.Status.FORCE_CHALLENGE) {
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
} else if (status == AuthenticationProcessor.Status.CHALLENGE) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator CHALLENGE: {0}", execution.getAuthenticator());
|
||||||
|
if (execution.isRequired()) {
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
}
|
||||||
|
UserModel authenticatedUser = processor.getClientSession().getAuthenticatedUser();
|
||||||
|
if (execution.isOptional() && authenticatedUser != null && result.getAuthenticator().configuredFor(processor.getSession(), processor.getRealm(), authenticatedUser)) {
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
}
|
||||||
|
if (execution.isAlternative()) {
|
||||||
|
alternativeChallenge = result.getChallenge();
|
||||||
|
challengedAlternativeExecution = execution;
|
||||||
|
} else {
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} else if (status == AuthenticationProcessor.Status.FAILURE_CHALLENGE) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
|
||||||
|
processor.logFailure();
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
} else if (status == AuthenticationProcessor.Status.ATTEMPTED) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
|
||||||
|
if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
|
||||||
|
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
|
||||||
|
}
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
|
||||||
|
AuthenticationProcessor.logger.error("Unknown result status");
|
||||||
|
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
|
||||||
|
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
|
||||||
|
return result.getChallenge();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
24
services/src/main/java/org/keycloak/authentication/FormAction.java
Executable file
24
services/src/main/java/org/keycloak/authentication/FormAction.java
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface FormAction extends Provider {
|
||||||
|
void authenticate(FormContext context);
|
||||||
|
|
||||||
|
boolean requiresUser();
|
||||||
|
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set actions to configure authenticator
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
|
||||||
|
|
||||||
|
}
|
10
services/src/main/java/org/keycloak/authentication/FormActionFactory.java
Executable file
10
services/src/main/java/org/keycloak/authentication/FormActionFactory.java
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface FormActionFactory extends ProviderFactory<FormAction> {
|
||||||
|
}
|
32
services/src/main/java/org/keycloak/authentication/FormActionSpi.java
Executable file
32
services/src/main/java/org/keycloak/authentication/FormActionSpi.java
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.provider.Spi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class FormActionSpi implements Spi {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInternal() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "form-action";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Provider> getProviderClass() {
|
||||||
|
return FormAction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||||
|
return FormActionFactory.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
290
services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
Executable file
290
services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
Executable file
|
@ -0,0 +1,290 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
import org.keycloak.ClientConnection;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class FormAuthenticationFlow implements AuthenticationFlow {
|
||||||
|
AuthenticationProcessor processor;
|
||||||
|
AuthenticationExecutionModel execution;
|
||||||
|
|
||||||
|
|
||||||
|
public FormAuthenticationFlow(AuthenticationProcessor processor, AuthenticationExecutionModel execution) {
|
||||||
|
this.processor = processor;
|
||||||
|
this.execution = execution;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FormActionResult implements FormContext {
|
||||||
|
AuthenticatorContext delegate;
|
||||||
|
FormAuthenticator authenticator;
|
||||||
|
|
||||||
|
FormActionResult(AuthenticatorContext delegate, FormAuthenticator authenticator) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.authenticator = authenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FormAuthenticator getFormAuthenticator() {
|
||||||
|
return authenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EventBuilder getEvent() {
|
||||||
|
return delegate.getEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthenticationExecutionModel getExecution() {
|
||||||
|
return delegate.getExecution();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExecution(AuthenticationExecutionModel execution) {
|
||||||
|
delegate.setExecution(execution);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthenticatorConfigModel getAuthenticatorConfig() {
|
||||||
|
return delegate.getAuthenticatorConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAction() {
|
||||||
|
return delegate.getAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authenticator getAuthenticator() {
|
||||||
|
return delegate.getAuthenticator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticator(Authenticator authenticator) {
|
||||||
|
delegate.setAuthenticator(authenticator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthenticationProcessor.Status getStatus() {
|
||||||
|
return delegate.getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserModel getUser() {
|
||||||
|
return delegate.getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUser(UserModel user) {
|
||||||
|
delegate.setUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RealmModel getRealm() {
|
||||||
|
return delegate.getRealm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientSessionModel getClientSession() {
|
||||||
|
return delegate.getClientSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachUserSession(UserSessionModel userSession) {
|
||||||
|
delegate.attachUserSession(userSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientConnection getConnection() {
|
||||||
|
return delegate.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UriInfo getUriInfo() {
|
||||||
|
return delegate.getUriInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeycloakSession getSession() {
|
||||||
|
return delegate.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpRequest getHttpRequest() {
|
||||||
|
return delegate.getHttpRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BruteForceProtector getProtector() {
|
||||||
|
return delegate.getProtector();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void success() {
|
||||||
|
delegate.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failure(AuthenticationProcessor.Error error) {
|
||||||
|
delegate.failure(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failure(AuthenticationProcessor.Error error, Response response) {
|
||||||
|
delegate.failure(error, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void challenge(Response challenge) {
|
||||||
|
delegate.challenge(challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forceChallenge(Response challenge) {
|
||||||
|
delegate.forceChallenge(challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failureChallenge(AuthenticationProcessor.Error error, Response challenge) {
|
||||||
|
delegate.failureChallenge(error, challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attempted() {
|
||||||
|
delegate.attempted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForwardedErrorMessage() {
|
||||||
|
return delegate.getForwardedErrorMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateAccessCode() {
|
||||||
|
return delegate.generateAccessCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response getChallenge() {
|
||||||
|
return delegate.getChallenge();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthenticationProcessor.Error getError() {
|
||||||
|
return delegate.getError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response processAction(String actionExecution) {
|
||||||
|
if (!actionExecution.equals(execution.getId())) {
|
||||||
|
throw new AuthenticationProcessor.AuthException("action is not current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
FormAuthenticator authenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
|
||||||
|
for (AuthenticationExecutionModel formActionExecution : processor.getRealm().getAuthenticationExecutions(execution.getFlowId())) {
|
||||||
|
FormAction action = processor.getSession().getProvider(FormAction.class, execution.getAuthenticator());
|
||||||
|
|
||||||
|
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
|
||||||
|
if (action.requiresUser() && authUser == null) {
|
||||||
|
throw new AuthenticationProcessor.AuthException("form action: " + execution.getAuthenticator() + " requires user", AuthenticationProcessor.Error.UNKNOWN_USER);
|
||||||
|
}
|
||||||
|
boolean configuredFor = false;
|
||||||
|
if (action.requiresUser() && authUser != null) {
|
||||||
|
configuredFor = action.configuredFor(processor.getSession(), processor.getRealm(), authUser);
|
||||||
|
if (!configuredFor) {
|
||||||
|
if (formActionExecution.isRequired()) {
|
||||||
|
if (formActionExecution.isUserSetupAllowed()) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", execution.getAuthenticator());
|
||||||
|
processor.getClientSession().setExecutionStatus(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
|
||||||
|
action.setRequiredActions(processor.getSession(), processor.getRealm(), authUser);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
|
||||||
|
}
|
||||||
|
} else if (formActionExecution.isOptional()) {
|
||||||
|
processor.getClientSession().setExecutionStatus(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormActionResult result = new FormActionResult(processor.createAuthenticatorContext(formActionExecution, null), authenticator);
|
||||||
|
action.authenticate(result);
|
||||||
|
return processResult(result, formActionExecution);
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response processFlow() {
|
||||||
|
FormAuthenticator authenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
|
||||||
|
AuthenticatorContext context = processor.createAuthenticatorContext(execution, null);
|
||||||
|
authenticator.authenticate(context);
|
||||||
|
return processResult(context, execution);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Response processResult(AuthenticatorContext result, AuthenticationExecutionModel execution) {
|
||||||
|
AuthenticationProcessor.Status status = result.getStatus();
|
||||||
|
if (status == AuthenticationProcessor.Status.SUCCESS) {
|
||||||
|
return null;
|
||||||
|
} else if (status == AuthenticationProcessor.Status.FAILED) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
|
||||||
|
processor.logFailure();
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
|
||||||
|
if (result.getChallenge() != null) {
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
}
|
||||||
|
throw new AuthenticationProcessor.AuthException(result.getError());
|
||||||
|
} else if (status == AuthenticationProcessor.Status.FORCE_CHALLENGE) {
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
} else if (status == AuthenticationProcessor.Status.CHALLENGE) {
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
} else if (status == AuthenticationProcessor.Status.FAILURE_CHALLENGE) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
|
||||||
|
processor.logFailure();
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
|
return sendChallenge(result, execution);
|
||||||
|
} else if (status == AuthenticationProcessor.Status.ATTEMPTED) {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
|
||||||
|
if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
|
||||||
|
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
|
||||||
|
}
|
||||||
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
|
||||||
|
AuthenticationProcessor.logger.error("Unknown result status");
|
||||||
|
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
|
||||||
|
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
|
||||||
|
return result.getChallenge();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
14
services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
Executable file
14
services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface FormAuthenticator extends Provider {
|
||||||
|
void authenticate(AuthenticatorContext context);
|
||||||
|
Response createChallenge(FormContext context, String... errorMessages);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface FormAuthenticatorFactory extends ProviderFactory<FormAuthenticator> {
|
||||||
|
}
|
32
services/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java
Executable file
32
services/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.provider.Spi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class FormAuthenticatorSpi implements Spi {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInternal() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "form-authenticator";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Provider> getProviderClass() {
|
||||||
|
return FormAuthenticator.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||||
|
return FormAuthenticatorFactory.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
9
services/src/main/java/org/keycloak/authentication/FormContext.java
Executable file
9
services/src/main/java/org/keycloak/authentication/FormContext.java
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
package org.keycloak.authentication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface FormContext extends AuthenticatorContext {
|
||||||
|
FormAuthenticator getFormAuthenticator();
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.keycloak.authentication.actions;
|
package org.keycloak.authentication.requiredactions;
|
||||||
|
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.authentication.RequiredActionContext;
|
import org.keycloak.authentication.RequiredActionContext;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.keycloak.authentication.actions;
|
package org.keycloak.authentication.requiredactions;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
@ -6,13 +6,11 @@ import org.keycloak.authentication.RequiredActionContext;
|
||||||
import org.keycloak.authentication.RequiredActionFactory;
|
import org.keycloak.authentication.RequiredActionFactory;
|
||||||
import org.keycloak.authentication.RequiredActionProvider;
|
import org.keycloak.authentication.RequiredActionProvider;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.services.managers.ClientSessionCode;
|
|
||||||
import org.keycloak.util.Time;
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.keycloak.authentication.actions;
|
package org.keycloak.authentication.requiredactions;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
@ -6,13 +6,9 @@ import org.keycloak.authentication.RequiredActionContext;
|
||||||
import org.keycloak.authentication.RequiredActionFactory;
|
import org.keycloak.authentication.RequiredActionFactory;
|
||||||
import org.keycloak.authentication.RequiredActionProvider;
|
import org.keycloak.authentication.RequiredActionProvider;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
|
||||||
import org.keycloak.services.managers.ClientSessionCode;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.keycloak.authentication.actions;
|
package org.keycloak.authentication.requiredactions;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
@ -6,19 +6,13 @@ import org.keycloak.authentication.RequiredActionContext;
|
||||||
import org.keycloak.authentication.RequiredActionFactory;
|
import org.keycloak.authentication.RequiredActionFactory;
|
||||||
import org.keycloak.authentication.RequiredActionProvider;
|
import org.keycloak.authentication.RequiredActionProvider;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
import org.keycloak.models.RequiredCredentialModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.services.managers.ClientSessionCode;
|
|
||||||
import org.keycloak.util.Time;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,4 +1,4 @@
|
||||||
package org.keycloak.authentication.actions;
|
package org.keycloak.authentication.requiredactions;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
@ -8,13 +8,11 @@ import org.keycloak.authentication.RequiredActionProvider;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.services.managers.ClientSessionCode;
|
|
||||||
import org.keycloak.services.resources.LoginActionsService;
|
import org.keycloak.services.resources.LoginActionsService;
|
||||||
import org.keycloak.services.validation.Validation;
|
import org.keycloak.services.validation.Validation;
|
||||||
import org.keycloak.util.Time;
|
import org.keycloak.util.Time;
|
|
@ -124,7 +124,7 @@ public class AuthenticationManagementResource {
|
||||||
rep.setSubFlow(false);
|
rep.setSubFlow(false);
|
||||||
rep.setRequirementChoices(new LinkedList<String>());
|
rep.setRequirementChoices(new LinkedList<String>());
|
||||||
if (execution.isAutheticatorFlow()) {
|
if (execution.isAutheticatorFlow()) {
|
||||||
AuthenticationFlowModel flowRef = realm.getAuthenticationFlowById(execution.getAuthenticator());
|
AuthenticationFlowModel flowRef = realm.getAuthenticationFlowById(execution.getFlowId());
|
||||||
rep.setReferenceType(flowRef.getAlias());
|
rep.setReferenceType(flowRef.getAlias());
|
||||||
rep.setExecution(execution.getId());
|
rep.setExecution(execution.getId());
|
||||||
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name());
|
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
org.keycloak.authentication.actions.UpdatePassword
|
org.keycloak.authentication.requiredactions.UpdatePassword
|
||||||
org.keycloak.authentication.actions.UpdateProfile
|
org.keycloak.authentication.requiredactions.UpdateProfile
|
||||||
org.keycloak.authentication.actions.UpdateTotp
|
org.keycloak.authentication.requiredactions.UpdateTotp
|
||||||
org.keycloak.authentication.actions.VerifyEmail
|
org.keycloak.authentication.requiredactions.VerifyEmail
|
||||||
org.keycloak.authentication.actions.TermsAndConditions
|
org.keycloak.authentication.requiredactions.TermsAndConditions
|
|
@ -4,4 +4,6 @@ org.keycloak.exportimport.ClientImportSpi
|
||||||
org.keycloak.wellknown.WellKnownSpi
|
org.keycloak.wellknown.WellKnownSpi
|
||||||
org.keycloak.messages.MessagesSpi
|
org.keycloak.messages.MessagesSpi
|
||||||
org.keycloak.authentication.AuthenticatorSpi
|
org.keycloak.authentication.AuthenticatorSpi
|
||||||
org.keycloak.authentication.RequiredActionSpi
|
org.keycloak.authentication.RequiredActionSpi
|
||||||
|
org.keycloak.authentication.FormAuthenticatorSpi
|
||||||
|
org.keycloak.authentication.FormActionSpi
|
||||||
|
|
Loading…
Reference in a new issue