Skip to content

Commit

Permalink
p2-inc#235 Update to latest HomeIdpDiscovery API
Browse files Browse the repository at this point in the history
  • Loading branch information
rtufisi committed Jul 20, 2024
1 parent 5677e6a commit beaa3e9
Show file tree
Hide file tree
Showing 37 changed files with 1,192 additions and 336 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.java.package>io.phasetwo.service</main.java.package>
<junit.version>5.8.2</junit.version>
<keycloak.version>25.0.0</keycloak.version>
<keycloak.version>25.0.1</keycloak.version>
<resteasy.version>6.2.7.Final</resteasy.version>
<lombok.version>1.18.32</lombok.version>
<guava.version>33.0.0-jre</guava.version>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
import io.phasetwo.service.auth.idp.discovery.spi.HomeIdpDiscoverer;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.keycloak.models.AuthenticationExecutionModel.Requirement.ALTERNATIVE;
import static org.keycloak.models.AuthenticationExecutionModel.Requirement.DISABLED;
import static org.keycloak.models.AuthenticationExecutionModel.Requirement.REQUIRED;

/**
* Provides a base implementation for authenticator factories that integrate custom identity provider
* discovery mechanisms within authentication flow of this extension. This abstract class simplifies
* the creation of authenticator instances by encapsulating common logic and providing a framework
* for extending the discovery functionality through custom {@link HomeIdpDiscoverer} implementations.
* <p>
* Implementors of this class need to provide their own {@link DiscovererConfig}, which includes
* the discovery logic specifics and configuration properties. This approach ensures flexibility and
* customizability, enabling developers to tailor the identity provider discovery process to specific
* organizational needs or authentication scenarios.
* </p>
* <p>
* By inheriting from this class, developers can focus on the specifics of their discovery logic
* without worrying about the boilerplate associated with UI integration and redirection logic.
* </p>
*
* @apiNote This interface is part of the public API, but is currently unstable and may change in future releases.
*
* @see HomeIdpDiscoverer
* @see DiscovererConfig
*/
@PublicAPI(unstable = true)
public abstract class AbstractHomeIdpDiscoveryAuthenticatorFactory implements AuthenticatorFactory, ServerInfoAwareProviderFactory {
private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = new AuthenticationExecutionModel.Requirement[]{REQUIRED, ALTERNATIVE, DISABLED};

private final DiscovererConfig discovererConfig;

protected AbstractHomeIdpDiscoveryAuthenticatorFactory(DiscovererConfig discovererConfig) {
this.discovererConfig = discovererConfig;
}

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

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

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

@Override
public final List<ProviderConfigProperty> getConfigProperties() {
return Stream.concat(
HomeIdpForwarderConfigProperties.CONFIG_PROPERTIES.stream(),
discovererConfig.getProperties().stream())
.collect(Collectors.toList());
}

@Override
public final Authenticator create(KeycloakSession session) {
return new HomeIdpDiscoveryAuthenticator(discovererConfig);
}

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

@Override
public final void postInit(KeycloakSessionFactory factory) {
}

@Override
public final void close() {
}

@Override
public final Map<String, String> getOperationalInfo() {
return OperationalInfo.get();
}

/**
* Represents the configuration settings for a {@link HomeIdpDiscoverer} implementation. This interface
* is designed to allow for dynamic specification of configuration properties necessary for the
* discovery of home Identity Providers (IdPs). The configurations defined by an implementation of
* this interface provide the parameters and metadata required by a discoverer to properly integrate
* with {@link HomeIdpDiscoveryAuthenticator}.
*
* @apiNote This interface is part of the public API, but is currently unstable and may change in future releases.
*
* @see HomeIdpDiscoverer
*/
@PublicAPI(unstable = true)
public interface DiscovererConfig {
/**
* Retrieves a list of {@link ProviderConfigProperty} objects that define the configuration
* properties available for the discoverer. Each {@code ProviderConfigProperty} includes metadata
* such as the property name, type, label, default value, and other attributes necessary for
* configuring the discoverer identified by {@link #getProviderId()} dynamically at runtime.
*
* @return a list of {@link ProviderConfigProperty} that describes each configuration property
* required by the discoverer. If no home properties are need for configuration, this method must
* return an empty list.
*/
List<ProviderConfigProperty> getProperties();

/**
* Returns the unique provider ID associated with the discoverer. This ID is used to uniquely
* identify and reference the specific discoverer implementation within the Keycloak system.
* The provider ID should be unique across all discoverer configurations to prevent conflicts
* and ensure correct operation.
*
* @return the unique string identifier for the discoverer provider.
*/
String getProviderId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ final class AuthenticationChallenge {
private final RememberMe rememberMe;
private final LoginHint loginHint;
private final LoginForm loginForm;
private final Reauthentication reauthentication;

AuthenticationChallenge(AuthenticationFlowContext context, RememberMe rememberMe, LoginHint loginHint, LoginForm loginForm) {
AuthenticationChallenge(AuthenticationFlowContext context, RememberMe rememberMe, LoginHint loginHint, LoginForm loginForm, Reauthentication reauthentication) {
this.context = context;
this.rememberMe = rememberMe;
this.loginHint = loginHint;
this.loginForm = loginForm;
this.reauthentication = reauthentication;
}

void forceChallenge() {
Expand All @@ -30,15 +32,27 @@ void forceChallenge() {

String rememberMeUsername = rememberMe.getUserName();

if (loginHintUsername != null || rememberMeUsername != null) {
if (loginHintUsername != null) {
formData.add(AuthenticationManager.FORM_USERNAME, loginHintUsername);
} else {
formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
formData.add("rememberMe", "on");
if (reauthentication.required() && context.getUser() != null) {
String attribute = context.getAuthenticatorConfig().getConfig().getOrDefault("userAttribute", "username");
formData.add(AuthenticationManager.FORM_USERNAME, context.getUser().getFirstAttribute(attribute));
} else {
if (loginHintUsername != null || rememberMeUsername != null) {
if (loginHintUsername != null) {
formData.add(AuthenticationManager.FORM_USERNAME, loginHintUsername);
} else {
formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
formData.add("rememberMe", "on");
}
}
}
Response challengeResponse = loginForm.create(formData);

Response challengeResponse;
if (reauthentication.required()) {
challengeResponse = loginForm.createWithSignInButtonOnly(formData);
} else {
challengeResponse = loginForm.create(formData);
}

context.challenge(challengeResponse);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import io.phasetwo.service.auth.idp.discovery.spi.HomeIdpDiscoverer;
import org.keycloak.authentication.AuthenticationFlowContext;

final class HomeIdpAuthenticationFlowContext {

private final AuthenticationFlowContext context;
private HomeIdpDiscoveryConfig config;
private HomeIdpForwarderConfig config;
private LoginPage loginPage;
private LoginHint loginHint;
private HomeIdpDiscoverer discoverer;
Expand All @@ -15,35 +16,36 @@ final class HomeIdpAuthenticationFlowContext {
private Redirector redirector;
private BaseUriLoginFormsProvider loginFormsProvider;
private LoginForm loginForm;
private Reauthentication reauthentication;

HomeIdpAuthenticationFlowContext(AuthenticationFlowContext context) {
this.context = context;
}

HomeIdpDiscoveryConfig config() {
HomeIdpForwarderConfig config() {
if (config == null) {
config = new HomeIdpDiscoveryConfig(context.getAuthenticatorConfig());
config = new HomeIdpForwarderConfig(context.getAuthenticatorConfig());
}
return config;
}

LoginPage loginPage() {
if (loginPage == null) {
loginPage = new LoginPage(context, config());
loginPage = new LoginPage(context, config(), reauthentication());
}
return loginPage;
}

LoginHint loginHint() {
if (loginHint == null) {
loginHint = new LoginHint(context);
loginHint = new LoginHint(context, new Users(context.getSession()));
}
return loginHint;
}

HomeIdpDiscoverer discoverer() {
HomeIdpDiscoverer discoverer(AbstractHomeIdpDiscoveryAuthenticatorFactory.DiscovererConfig discovererConfig) {
if (discoverer == null) {
discoverer = new HomeIdpDiscoverer(context);
discoverer = context.getSession().getProvider(HomeIdpDiscoverer.class, discovererConfig.getProviderId());
}
return discoverer;
}
Expand All @@ -57,7 +59,7 @@ RememberMe rememberMe() {

AuthenticationChallenge authenticationChallenge() {
if (authenticationChallenge == null) {
authenticationChallenge = new AuthenticationChallenge(context, rememberMe(), loginHint(), loginForm());
authenticationChallenge = new AuthenticationChallenge(context, rememberMe(), loginHint(), loginForm(), reauthentication());
}
return authenticationChallenge;
}
Expand All @@ -82,4 +84,11 @@ BaseUriLoginFormsProvider loginFormsProvider() {
}
return loginFormsProvider;
}

Reauthentication reauthentication() {
if (reauthentication == null) {
reauthentication = new Reauthentication(context);
}
return reauthentication;
}
}
Loading

0 comments on commit beaa3e9

Please sign in to comment.