From abf22edd55b776cd9072593fb634b74d8b7540a9 Mon Sep 17 00:00:00 2001 From: Rolain Djeumen Date: Tue, 18 Jun 2024 08:23:55 +0100 Subject: [PATCH] feat(jans-keycloak-integration): enhancements to jans-keycloak-integration #8614 * moved authenticator rest service spi to spi module Signed-off-by: Rolain Djeumen --- .../JansAuthResponseResourceProvider.java | 125 ++++++++++++++++++ ...nsAuthResponseResourceProviderFactory.java | 42 ++++++ 2 files changed, 167 insertions(+) create mode 100644 jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java create mode 100644 jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java new file mode 100644 index 00000000000..478a4d59fa6 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java @@ -0,0 +1,125 @@ +package io.jans.kc.spi.rest; + +import org.keycloak.forms.login.LoginFormsProvider; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.resource.RealmResourceProvider; + +import io.jans.kc.spi.auth.SessionAttributes; + +import java.util.Map; +import java.util.HashMap; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.jboss.logging.Logger; +import org.jboss.resteasy.annotations.cache.NoCache; + +public class JansAuthResponseResourceProvider implements RealmResourceProvider { + + private static final Logger log = Logger.getLogger(JansAuthResponseResourceProvider.class); + + private static final String ACTION_URI_TPL_PARAM = "actionuri"; + private static final String ERR_MSG_TPL_PARAM = "authError"; + + private static final String JANS_AUTH_RESPONSE_ERR_FTL ="jans-auth-response-error.ftl"; + private static final String JANS_AUTH_RESPONSE_COMPLETE_FTL = "jans-auth-response-complete.ftl"; + + private static final String ERR_MSG_INVALID_REALM = "jans.error-invalid-realm"; + private static final String ERR_MSG_MISSING_DATA = "jans.error-missing-data"; + + private KeycloakSession session; + + public JansAuthResponseResourceProvider(KeycloakSession session) { + + this.session = session; + } + + @Override + public Object getResource() { + + return this; + } + + @Override + public void close() { + + } + + @GET + @NoCache + @Produces(MediaType.TEXT_HTML) + @Path("/auth-complete") + public Response completeAuthentication(@QueryParam("code") String code, + @QueryParam("scope") String scope, + @QueryParam("state") String state) { + + RealmModel realm = getAuthenticationRealm(); + if(!stateIsAssociatedToRealm(realm, state)) { + log.infov("Realm {0} is not associated to authz response and state {1}",realm.getName(),state); + return createErrorResponse(ERR_MSG_INVALID_REALM); + } + + if(!realmHasActionUri(realm)) { + log.infov("Realm {0} has no action uri set to complete authentication",realm.getName()); + return createErrorResponse(ERR_MSG_MISSING_DATA); + } + saveAuthResultInRealm(realm, code, state); + return createFinalizeAuthResponse(realm.getAttribute(SessionAttributes.KC_ACTION_URI)); + } + + private final RealmModel getAuthenticationRealm() { + + return session.getContext().getRealm(); + } + + private final boolean stateIsAssociatedToRealm(RealmModel realm , String state) { + + String expectedstate = realm.getAttribute(SessionAttributes.JANS_OIDC_STATE); + + return state.equals(expectedstate); + } + + private final boolean realmHasActionUri(RealmModel realm) { + + String actionuri = realm.getAttribute(SessionAttributes.KC_ACTION_URI); + return (actionuri != null); + } + + private final void saveAuthResultInRealm(RealmModel realm, String code, String session_state) { + + realm.setAttribute(SessionAttributes.JANS_OIDC_CODE,code); + realm.setAttribute(SessionAttributes.JANS_SESSION_STATE,session_state); + } + + private final Response createResponseWithForm(String formtemplate,Map attributes) { + + LoginFormsProvider lfp = session.getProvider(LoginFormsProvider.class); + + if(attributes != null && !attributes.isEmpty()) { + for(String key: attributes.keySet()) { + lfp.setAttribute(key,attributes.get(key)); + } + } + return lfp.createForm(formtemplate); + } + + private final Response createErrorResponse(String errmsgid) { + + Map attributes = new HashMap(); + attributes.put(ERR_MSG_TPL_PARAM,errmsgid); + return createResponseWithForm(JANS_AUTH_RESPONSE_ERR_FTL,attributes); + } + + private final Response createFinalizeAuthResponse(String actionuri) { + + Map attributes = new HashMap(); + attributes.put(ACTION_URI_TPL_PARAM,actionuri); + return createResponseWithForm(JANS_AUTH_RESPONSE_COMPLETE_FTL, attributes); + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java new file mode 100644 index 00000000000..8384f1bf782 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java @@ -0,0 +1,42 @@ +package io.jans.kc.spi.rest; + +import org.keycloak.Config.Scope; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.resource.RealmResourceProvider; +import org.keycloak.services.resource.RealmResourceProviderFactory; + +import io.jans.kc.spi.ProviderIDs; + +public class JansAuthResponseResourceProviderFactory implements RealmResourceProviderFactory { + + private static final String ID = ProviderIDs.JANS_AUTH_RESPONSE_REST_PROVIDER; + + @Override + public String getId() { + + return ID; + } + + @Override + public RealmResourceProvider create(KeycloakSession session) { + + return new JansAuthResponseResourceProvider(session); + } + + @Override + public void init(Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } +} \ No newline at end of file