Skip to content

Commit

Permalink
conditional amr authenticator
Browse files Browse the repository at this point in the history
  • Loading branch information
dasniko committed Oct 7, 2024
1 parent 2e6aabf commit af7e325
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dasniko.keycloak.authentication.conditional;

import org.keycloak.Config;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.authenticators.conditional.ConditionalAuthenticator;
import org.keycloak.authentication.authenticators.conditional.ConditionalAuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;

public abstract class AbstractConditionalAuthenticator implements ConditionalAuthenticatorFactory, ConditionalAuthenticator {

static final String CONF_NOT = "not";

static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED};

ProviderConfigProperty negateOutputConfProperty = new ProviderConfigProperty(CONF_NOT, "Negate output", "Apply a NOT to the check result", ProviderConfigProperty.BOOLEAN_TYPE, false);

@Override
public ConditionalAuthenticator getSingleton() {
return this;
}

@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}

@Override
public void init(Config.Scope config) {
}

@Override
public void postInit(KeycloakSessionFactory factory) {
}

@Override
public void close() {
}

@Override
public void action(AuthenticationFlowContext authenticationFlowContext) {
}

@Override
public void setRequiredActions(KeycloakSession keycloakSession, RealmModel realmModel, UserModel userModel) {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package dasniko.keycloak.authentication.conditional;

import com.google.auto.service.AutoService;
import de.keycloak.util.AuthenticatorUtil;
import lombok.extern.jbosslog.JBossLog;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.authenticators.util.AuthenticatorUtils;
import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.utils.AmrUtils;
import org.keycloak.provider.ProviderConfigProperty;

import java.util.List;
import java.util.Map;

@JBossLog
@AutoService(AuthenticatorFactory.class)
public class ConditionalAmrAuthenticator extends AbstractConditionalAuthenticator {

public static final String PROVIDER_ID = "conditional-amr";

static final String CONF_AMR_VALUE = "amr_value";

@Override
public boolean matchCondition(AuthenticationFlowContext context) {
String expectedValue = AuthenticatorUtil.getConfig(context, CONF_AMR_VALUE, "");
List<String> amrValues = getAmr(context);
boolean negateOutput = AuthenticatorUtil.getConfig(context, ConditionalAuthNoteAuthenticatorFactory.CONF_NOT, Boolean.FALSE);

return negateOutput != amrValues.contains(expectedValue);
}

@Override
public boolean requiresUser() {
return false;
}

@Override
public String getDisplayType() {
return "Condition - Authentication Method Reference";
}

@Override
public boolean isConfigurable() {
return true;
}

@Override
public boolean isUserSetupAllowed() {
return false;
}

@Override
public String getHelpText() {
return "...some help text...";
}

@Override
public List<ProviderConfigProperty> getConfigProperties() {
ProviderConfigProperty amrExpectedValue = new ProviderConfigProperty();
amrExpectedValue.setType(ProviderConfigProperty.STRING_TYPE);
amrExpectedValue.setName(CONF_AMR_VALUE);
amrExpectedValue.setLabel("AMR value");
amrExpectedValue.setHelpText("Expected authenticator method reference value.");

return List.of(amrExpectedValue, negateOutputConfProperty);
}

@Override
public String getId() {
return PROVIDER_ID;
}

protected List<String> getAmr(AuthenticationFlowContext context) {
Map<String, String> userSessionNotes = context.getAuthenticationSession().getUserSessionNotes();
Map<String, Integer> executions = AuthenticatorUtils.parseCompletedExecutions(userSessionNotes.get(Constants.AUTHENTICATORS_COMPLETED));
log.debugf("found the following completed authentication executions: %s", executions.toString());
List<String> refs = AmrUtils.getAuthenticationExecutionReferences(executions, context.getRealm());
log.debugf("amr %s set in session", refs);
return refs;
}

}

0 comments on commit af7e325

Please sign in to comment.