Skip to content

Commit

Permalink
Upgrade the forked Home IdP extension to latest commit (#121)
Browse files Browse the repository at this point in the history
* Upgrade the fork of sventorben/keycloak-home-idp-discovery to the latest commit 04b9becfb37df63784559c936f0b49609686439e

* Apply custom p2-inc patches to home-idp-discovery needed for keycloak-orgs functionality

* Reinstate AutoService annotation to HomeIdpDiscoveryAuthenticatorFactory
  • Loading branch information
phamann committed Sep 15, 2023
1 parent 57ff72b commit 636682f
Show file tree
Hide file tree
Showing 21 changed files with 1,051 additions and 513 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import org.keycloak.models.IdentityProviderModel;

import java.util.HashMap;
import java.util.Map;

final class AlwaysSelectableIdentityProviderModel extends IdentityProviderModel {

AlwaysSelectableIdentityProviderModel(IdentityProviderModel delegate) {
super(delegate);
}

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

@Override
public Map<String, String> getConfig() {
Map<String, String> superConfig = new HashMap<>(super.getConfig());
superConfig.put("hideOnLoginPage", Boolean.FALSE.toString());
return superConfig;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.services.managers.AuthenticationManager;

import java.util.List;

final class AuthenticationChallenge {

private final AuthenticationFlowContext context;
private final RememberMe rememberMe;
private final LoginHint loginHint;
private final LoginForm loginForm;

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

void forceChallenge() {
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
String loginHintUsername = loginHint.getFromSession();

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");
}
}
Response challengeResponse = loginForm.create(formData);
context.challenge(challengeResponse);
}

void forceChallenge(List<IdentityProviderModel> homeIdps) {
context.forceChallenge(loginForm.create(homeIdps));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import jakarta.ws.rs.core.UriBuilder;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider;
import org.keycloak.services.resources.LoginActionsService;

import java.net.URI;

/**
* Workaround to reuse the logic in FreeMarkerLoginFormsProvider.prepareBaseUriBuilder, so no need to reimplement it.
*/
final class BaseUriLoginFormsProvider extends FreeMarkerLoginFormsProvider {

public BaseUriLoginFormsProvider(AuthenticationFlowContext context) {
super(context.getSession());
super.setAuthenticationSession(context.getAuthenticationSession());
super.setClientSessionCode(context.generateAccessCode());
}

public URI getBaseUriWithCodeAndClientId() {
UriBuilder baseUriBuilder = super.prepareBaseUriBuilder(false);
if (accessCode != null) {
baseUriBuilder.queryParam(LoginActionsService.SESSION_CODE, accessCode);
}
return baseUriBuilder.build();
}
}
39 changes: 39 additions & 0 deletions src/main/java/io/phasetwo/service/auth/idp/Domain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import java.util.Objects;

class Domain {

private final String value;

Domain(String value) {
Objects.requireNonNull(value);
this.value = value.toLowerCase();
}

boolean isSubDomainOf(Domain domain) {
return this.value.endsWith("." + domain.value);
}

@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Domain))
return false;
if (this == obj)
return true;
return this.value.equalsIgnoreCase(((Domain) obj).value);
}

@Override
public int hashCode() {
return this.value.hashCode();
}

@Override
public String toString() {
return this.value;
}
}
51 changes: 51 additions & 0 deletions src/main/java/io/phasetwo/service/auth/idp/DomainExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import org.jboss.logging.Logger;
import org.keycloak.models.UserModel;

import java.util.Optional;

final class DomainExtractor {

private static final Logger LOG = Logger.getLogger(DomainExtractor.class);
private static final String EMAIL_ATTRIBUTE = "email";

private final HomeIdpDiscoveryConfig config;

DomainExtractor(HomeIdpDiscoveryConfig config) {
this.config = config;
}

Optional<Domain> extractFrom(UserModel user) {
if (!user.isEnabled()) {
LOG.warnf("User '%s' not enabled", user.getId());
return Optional.empty();
}
String userAttribute = user.getFirstAttribute(config.userAttribute());
if (userAttribute == null) {
LOG.warnf("Could not find user attribute '%s' for user '%s'", config.userAttribute(), user.getId());
return Optional.empty();
}
if (EMAIL_ATTRIBUTE.equalsIgnoreCase(config.userAttribute()) && !user.isEmailVerified()) {
LOG.warnf("Email address of user '%s' is not verified", user.getId());
return Optional.empty();
}
return extractFrom(userAttribute);
}

Optional<Domain> extractFrom(String usernameOrEmail) {
Domain domain = null;
if (usernameOrEmail != null) {
int atIndex = usernameOrEmail.trim().lastIndexOf("@");
if (atIndex >= 0) {
String strDomain = usernameOrEmail.trim().substring(atIndex + 1);
if (strDomain.length() > 0) {
domain = new Domain(strDomain);
}
}
}
return Optional.ofNullable(domain);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//package de.sventorben.keycloak.authentication.hidpd;
package io.phasetwo.service.auth.idp;

import org.keycloak.authentication.AuthenticationFlowContext;

final class HomeIdpAuthenticationFlowContext {

private final AuthenticationFlowContext context;
private HomeIdpDiscoveryConfig config;
private LoginPage loginPage;
private LoginHint loginHint;
private HomeIdpDiscoverer discoverer;
private RememberMe rememberMe;
private AuthenticationChallenge authenticationChallenge;
private Redirector redirector;
private BaseUriLoginFormsProvider loginFormsProvider;
private LoginForm loginForm;

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

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

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

LoginHint loginHint() {
if (loginHint == null) {
loginHint = new LoginHint(context);
}
return loginHint;
}

HomeIdpDiscoverer discoverer() {
if (discoverer == null) {
discoverer = new HomeIdpDiscoverer(context);
}
return discoverer;
}

RememberMe rememberMe() {
if (rememberMe == null) {
rememberMe = new RememberMe(context);
}
return rememberMe;
}

AuthenticationChallenge authenticationChallenge() {
if (authenticationChallenge == null) {
authenticationChallenge = new AuthenticationChallenge(context, rememberMe(), loginHint(), loginForm());
}
return authenticationChallenge;
}

Redirector redirector() {
if (redirector == null) {
redirector = new Redirector(context);
}
return redirector;
}

LoginForm loginForm() {
if (loginForm == null) {
loginForm = new LoginForm(context, loginFormsProvider());
}
return loginForm;
}

BaseUriLoginFormsProvider loginFormsProvider() {
if (loginFormsProvider == null) {
loginFormsProvider = new BaseUriLoginFormsProvider(context);
}
return loginFormsProvider;
}
}
Loading

0 comments on commit 636682f

Please sign in to comment.