pefernan commented on code in PR #4213:
URL: 
https://github.com/apache/incubator-kie-kogito-runtimes/pull/4213#discussion_r3152952207


##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {
+            logger.debug("No process instance available, cannot propagate 
token");
+            return Optional.empty();
+        }
+
+        try {
+            // Cast to ProcessInstanceImpl to access getKnowledgeRuntime()
+            if (!(processInstance instanceof 
org.jbpm.process.instance.ProcessInstance)) {
+                logger.debug("Process instance is not a jBPM ProcessInstance, 
cannot access runtime");
+                return Optional.empty();
+            }
+
+            org.jbpm.process.instance.ProcessInstance jbpmProcessInstance =
+                    (org.jbpm.process.instance.ProcessInstance) 
processInstance;
+
+            // Get the AuthTokenProvider from the ProcessConfig via 
KogitoProcessRuntime
+            org.kie.kogito.internal.process.runtime.KogitoProcessRuntime 
processRuntime =
+                    ((org.jbpm.process.instance.InternalProcessRuntime) 
jbpmProcessInstance.getKnowledgeRuntime().getProcessRuntime())
+                            .getKogitoProcessRuntime();
+
+            if (processRuntime == null) {
+                logger.debug("KogitoProcessRuntime not available, cannot 
access AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            ProcessConfig processConfig = 
processRuntime.getApplication().config().get(ProcessConfig.class);
+            if (processConfig == null) {
+                logger.debug("ProcessConfig not available, cannot access 
AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            AuthTokenProvider authTokenProvider = 
processConfig.authTokenProvider();
+            if (authTokenProvider == null) {
+                logger.debug("AuthTokenProvider not available");
+                return Optional.empty();
+            }
+
+            Optional<String> token = authTokenProvider.getAuthToken();
+            if (token.isPresent()) {
+                logger.debug("Using propagated token from AuthTokenProvider");
+                return token;
+            } else {
+                logger.debug("No token available from AuthTokenProvider");
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to retrieve token from AuthTokenProvider", e);
+            return Optional.empty();
+        }
+    }
+
+    private Optional<String> getConfiguredToken(KogitoWorkItem item, 
Map<String, Object> parameters) {
+        Optional<String> paramToken = Optional.ofNullable((String) 
parameters.get(PROPAGATE_TOKEN_PARAM));

Review Comment:
   Token won't be received as a param, it will be either read from config or 
propagated



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {
+            logger.debug("No process instance available, cannot propagate 
token");
+            return Optional.empty();
+        }
+
+        try {
+            // Cast to ProcessInstanceImpl to access getKnowledgeRuntime()
+            if (!(processInstance instanceof 
org.jbpm.process.instance.ProcessInstance)) {
+                logger.debug("Process instance is not a jBPM ProcessInstance, 
cannot access runtime");
+                return Optional.empty();
+            }
+
+            org.jbpm.process.instance.ProcessInstance jbpmProcessInstance =
+                    (org.jbpm.process.instance.ProcessInstance) 
processInstance;
+
+            // Get the AuthTokenProvider from the ProcessConfig via 
KogitoProcessRuntime
+            org.kie.kogito.internal.process.runtime.KogitoProcessRuntime 
processRuntime =
+                    ((org.jbpm.process.instance.InternalProcessRuntime) 
jbpmProcessInstance.getKnowledgeRuntime().getProcessRuntime())
+                            .getKogitoProcessRuntime();
+
+            if (processRuntime == null) {
+                logger.debug("KogitoProcessRuntime not available, cannot 
access AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            ProcessConfig processConfig = 
processRuntime.getApplication().config().get(ProcessConfig.class);
+            if (processConfig == null) {
+                logger.debug("ProcessConfig not available, cannot access 
AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            AuthTokenProvider authTokenProvider = 
processConfig.authTokenProvider();
+            if (authTokenProvider == null) {
+                logger.debug("AuthTokenProvider not available");
+                return Optional.empty();
+            }
+
+            Optional<String> token = authTokenProvider.getAuthToken();
+            if (token.isPresent()) {
+                logger.debug("Using propagated token from AuthTokenProvider");
+                return token;
+            } else {
+                logger.debug("No token available from AuthTokenProvider");
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to retrieve token from AuthTokenProvider", e);
+            return Optional.empty();
+        }
+    }
+
+    private Optional<String> getConfiguredToken(KogitoWorkItem item, 
Map<String, Object> parameters) {
+        Optional<String> paramToken = Optional.ofNullable((String) 
parameters.get(PROPAGATE_TOKEN_PARAM));
+        if (paramToken.isPresent()) {
+            logger.debug("Using configured token from parameters (BPMN or 
injected)");
+            return paramToken;
+        }
+
+        try {
+            if (item.getProcessInstance() == null) {

Review Comment:
   No need for this if, process instance shouldn't be null. If, by any chance 
it is, it is ok that we have a NPE in line 170



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";

Review Comment:
   Not needed



##########
kogito-workitems/kogito-rest-workitem/src/test/java/org/kogito/workitem/rest/decorators/TokenPropagationDecoratorTest.java:
##########
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.drools.core.common.InternalKnowledgeRuntime;
+import org.jbpm.process.instance.InternalProcessRuntime;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.kie.kogito.Application;
+import org.kie.kogito.Config;
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoNodeInstance;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class TokenPropagationDecoratorTest {
+
+    private static final String CONFIGURED_TOKEN = "configured-token-12345";
+    private static final String PROPAGATED_TOKEN = "propagated-token-67890";
+    private static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";

Review Comment:
   Cannot we use the constants in `TokenPropagationDecorator`?



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {
+            logger.debug("No process instance available, cannot propagate 
token");
+            return Optional.empty();
+        }
+
+        try {
+            // Cast to ProcessInstanceImpl to access getKnowledgeRuntime()
+            if (!(processInstance instanceof 
org.jbpm.process.instance.ProcessInstance)) {
+                logger.debug("Process instance is not a jBPM ProcessInstance, 
cannot access runtime");
+                return Optional.empty();
+            }
+
+            org.jbpm.process.instance.ProcessInstance jbpmProcessInstance =
+                    (org.jbpm.process.instance.ProcessInstance) 
processInstance;
+
+            // Get the AuthTokenProvider from the ProcessConfig via 
KogitoProcessRuntime
+            org.kie.kogito.internal.process.runtime.KogitoProcessRuntime 
processRuntime =
+                    ((org.jbpm.process.instance.InternalProcessRuntime) 
jbpmProcessInstance.getKnowledgeRuntime().getProcessRuntime())
+                            .getKogitoProcessRuntime();
+
+            if (processRuntime == null) {
+                logger.debug("KogitoProcessRuntime not available, cannot 
access AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            ProcessConfig processConfig = 
processRuntime.getApplication().config().get(ProcessConfig.class);
+            if (processConfig == null) {
+                logger.debug("ProcessConfig not available, cannot access 
AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            AuthTokenProvider authTokenProvider = 
processConfig.authTokenProvider();
+            if (authTokenProvider == null) {
+                logger.debug("AuthTokenProvider not available");
+                return Optional.empty();
+            }
+
+            Optional<String> token = authTokenProvider.getAuthToken();
+            if (token.isPresent()) {
+                logger.debug("Using propagated token from AuthTokenProvider");
+                return token;
+            } else {
+                logger.debug("No token available from AuthTokenProvider");
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to retrieve token from AuthTokenProvider", e);
+            return Optional.empty();
+        }
+    }
+
+    private Optional<String> getConfiguredToken(KogitoWorkItem item, 
Map<String, Object> parameters) {
+        Optional<String> paramToken = Optional.ofNullable((String) 
parameters.get(PROPAGATE_TOKEN_PARAM));
+        if (paramToken.isPresent()) {
+            logger.debug("Using configured token from parameters (BPMN or 
injected)");
+            return paramToken;
+        }
+
+        try {
+            if (item.getProcessInstance() == null) {
+                logger.debug("No process instance available, cannot read from 
ConfigResolver");
+                return Optional.empty();
+            }
+
+            String processId = item.getProcessInstance().getProcessId();
+            String taskName = (String) 
parameters.getOrDefault("RestServiceCallTaskId", item.getName());
+
+            if (processId == null || taskName == null) {
+                logger.debug("Process ID or task name is null, cannot build 
configuration key");
+                return Optional.empty();
+            }
+
+            String configKey = 
String.format("kogito.processes.%s.%s.access_token", processId, taskName);
+
+            Optional<String> configToken = 
ConfigResolverHolder.getConfigResolver()
+                    .getConfigProperty(configKey, String.class);
+
+            if (configToken.isPresent()) {
+                logger.debug("Using configured token from ConfigResolver with 
key: {}", configKey);
+                return configToken;
+            } else {
+                logger.debug("No token found in ConfigResolver for key: {}", 
configKey);
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to retrieve token from ConfigResolver", e);

Review Comment:
   I'd throw here too, if we cannot read the token by any chance it means that 
something wrong happens and the request shouldn't be sent



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {
+            logger.debug("No process instance available, cannot propagate 
token");
+            return Optional.empty();
+        }
+
+        try {
+            // Cast to ProcessInstanceImpl to access getKnowledgeRuntime()
+            if (!(processInstance instanceof 
org.jbpm.process.instance.ProcessInstance)) {
+                logger.debug("Process instance is not a jBPM ProcessInstance, 
cannot access runtime");
+                return Optional.empty();
+            }
+
+            org.jbpm.process.instance.ProcessInstance jbpmProcessInstance =
+                    (org.jbpm.process.instance.ProcessInstance) 
processInstance;
+
+            // Get the AuthTokenProvider from the ProcessConfig via 
KogitoProcessRuntime
+            org.kie.kogito.internal.process.runtime.KogitoProcessRuntime 
processRuntime =
+                    ((org.jbpm.process.instance.InternalProcessRuntime) 
jbpmProcessInstance.getKnowledgeRuntime().getProcessRuntime())
+                            .getKogitoProcessRuntime();
+
+            if (processRuntime == null) {
+                logger.debug("KogitoProcessRuntime not available, cannot 
access AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            ProcessConfig processConfig = 
processRuntime.getApplication().config().get(ProcessConfig.class);
+            if (processConfig == null) {
+                logger.debug("ProcessConfig not available, cannot access 
AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            AuthTokenProvider authTokenProvider = 
processConfig.authTokenProvider();
+            if (authTokenProvider == null) {
+                logger.debug("AuthTokenProvider not available");
+                return Optional.empty();
+            }
+
+            Optional<String> token = authTokenProvider.getAuthToken();
+            if (token.isPresent()) {
+                logger.debug("Using propagated token from AuthTokenProvider");
+                return token;
+            } else {
+                logger.debug("No token available from AuthTokenProvider");
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to retrieve token from AuthTokenProvider", e);
+            return Optional.empty();
+        }
+    }
+
+    private Optional<String> getConfiguredToken(KogitoWorkItem item, 
Map<String, Object> parameters) {
+        Optional<String> paramToken = Optional.ofNullable((String) 
parameters.get(PROPAGATE_TOKEN_PARAM));
+        if (paramToken.isPresent()) {
+            logger.debug("Using configured token from parameters (BPMN or 
injected)");
+            return paramToken;
+        }
+
+        try {
+            if (item.getProcessInstance() == null) {
+                logger.debug("No process instance available, cannot read from 
ConfigResolver");
+                return Optional.empty();
+            }
+
+            String processId = item.getProcessInstance().getProcessId();
+            String taskName = (String) 
parameters.getOrDefault("RestServiceCallTaskId", item.getName());

Review Comment:
   Maybe use a constant for `RestServiceCallTaskId` 



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");

Review Comment:
   In general I'm in favor of throwing exception when we have a wrong config 
that can lead into a `possible` wrong execution and make it clear to the user 
that something wrong happened. 
   
   Getting an `unexpected` value here (ej a number) shouldn't be prevented by 
the editor, so I'm okish  falling back to `none` (or empty). @martinweiler 
@Christine-Jose wdyt?



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {
+            logger.debug("No process instance available, cannot propagate 
token");
+            return Optional.empty();
+        }
+
+        try {
+            // Cast to ProcessInstanceImpl to access getKnowledgeRuntime()
+            if (!(processInstance instanceof 
org.jbpm.process.instance.ProcessInstance)) {
+                logger.debug("Process instance is not a jBPM ProcessInstance, 
cannot access runtime");
+                return Optional.empty();
+            }
+
+            org.jbpm.process.instance.ProcessInstance jbpmProcessInstance =
+                    (org.jbpm.process.instance.ProcessInstance) 
processInstance;
+
+            // Get the AuthTokenProvider from the ProcessConfig via 
KogitoProcessRuntime
+            org.kie.kogito.internal.process.runtime.KogitoProcessRuntime 
processRuntime =
+                    ((org.jbpm.process.instance.InternalProcessRuntime) 
jbpmProcessInstance.getKnowledgeRuntime().getProcessRuntime())
+                            .getKogitoProcessRuntime();
+
+            if (processRuntime == null) {
+                logger.debug("KogitoProcessRuntime not available, cannot 
access AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            ProcessConfig processConfig = 
processRuntime.getApplication().config().get(ProcessConfig.class);
+            if (processConfig == null) {
+                logger.debug("ProcessConfig not available, cannot access 
AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            AuthTokenProvider authTokenProvider = 
processConfig.authTokenProvider();
+            if (authTokenProvider == null) {
+                logger.debug("AuthTokenProvider not available");
+                return Optional.empty();
+            }

Review Comment:
   I think that there's no need for all this checks. I think that all this 
conditions should be always satisfied and if not (ej, processRuntime being 
null) is it ok that and exception is thrown and we propagate in the `catch` 
statement.
   
   adding a debug trace and returning an empty optional may hide a possible 
wrong execution



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {

Review Comment:
   No need for this if, this shouldn't ever be the case and if it is, exception 
should be thrown later not swallowed.



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {
+            logger.debug("No process instance available, cannot propagate 
token");
+            return Optional.empty();
+        }
+
+        try {
+            // Cast to ProcessInstanceImpl to access getKnowledgeRuntime()
+            if (!(processInstance instanceof 
org.jbpm.process.instance.ProcessInstance)) {

Review Comment:
   No need for this if, I think we can directly cast and not swallow any 
possible exception.



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";
+
+    @Override
+    public void decorate(KogitoWorkItem item, Map<String, Object> parameters, 
HttpRequest<?> request) {
+        Optional<String> bearerToken = getBearerToken(item, parameters);
+
+        bearerToken.ifPresentOrElse(
+                token -> {
+                    logger.debug("Rest workItem `{}`: Bearer token available, 
request will be sent with authentication", item.getNodeInstance().getId());
+                    request.bearerTokenAuthentication(token);
+                },
+                () -> logger.debug("Rest workItem `{}`: No Bearer Token 
available, request will be sent without authentication", 
item.getNodeInstance().getId()));
+    }
+
+    Optional<String> getBearerToken(KogitoWorkItem item, Map<String, Object> 
parameters) {
+        Object strategyParam = 
parameters.get(ACCESS_TOKEN_ACQUISITION_STRATEGY);
+
+        if (!(strategyParam instanceof String)) {
+            logger.debug("No token acquisition strategy specified, skipping 
authentication");
+            return Optional.empty();
+        }
+
+        String strategyName = (String) strategyParam;
+        AccessTokenAcquisitionStrategy strategy = 
AccessTokenAcquisitionStrategy.fromName(strategyName);
+
+        return switch (strategy) {
+            case PROPAGATED -> getPropagatedTokenFromHeaders(item);
+            case CONFIGURED -> getConfiguredToken(item, parameters);
+            default -> {
+                logger.debug("Strategy {} is NONE or unknown, skipping", 
strategyName);
+                yield Optional.empty();
+            }
+        };
+    }
+
+    private Optional<String> getPropagatedTokenFromHeaders(KogitoWorkItem 
item) {
+
+        KogitoProcessInstance processInstance = item.getProcessInstance();
+        if (processInstance == null) {
+            logger.debug("No process instance available, cannot propagate 
token");
+            return Optional.empty();
+        }
+
+        try {
+            // Cast to ProcessInstanceImpl to access getKnowledgeRuntime()
+            if (!(processInstance instanceof 
org.jbpm.process.instance.ProcessInstance)) {
+                logger.debug("Process instance is not a jBPM ProcessInstance, 
cannot access runtime");
+                return Optional.empty();
+            }
+
+            org.jbpm.process.instance.ProcessInstance jbpmProcessInstance =
+                    (org.jbpm.process.instance.ProcessInstance) 
processInstance;
+
+            // Get the AuthTokenProvider from the ProcessConfig via 
KogitoProcessRuntime
+            org.kie.kogito.internal.process.runtime.KogitoProcessRuntime 
processRuntime =
+                    ((org.jbpm.process.instance.InternalProcessRuntime) 
jbpmProcessInstance.getKnowledgeRuntime().getProcessRuntime())
+                            .getKogitoProcessRuntime();
+
+            if (processRuntime == null) {
+                logger.debug("KogitoProcessRuntime not available, cannot 
access AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            ProcessConfig processConfig = 
processRuntime.getApplication().config().get(ProcessConfig.class);
+            if (processConfig == null) {
+                logger.debug("ProcessConfig not available, cannot access 
AuthTokenProvider");
+                return Optional.empty();
+            }
+
+            AuthTokenProvider authTokenProvider = 
processConfig.authTokenProvider();
+            if (authTokenProvider == null) {
+                logger.debug("AuthTokenProvider not available");
+                return Optional.empty();
+            }
+
+            Optional<String> token = authTokenProvider.getAuthToken();
+            if (token.isPresent()) {
+                logger.debug("Using propagated token from AuthTokenProvider");
+                return token;
+            } else {
+                logger.debug("No token available from AuthTokenProvider");
+                return Optional.empty();
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to retrieve token from AuthTokenProvider", e);
+            return Optional.empty();

Review Comment:
   I think that here we must propagate the exception instead of swallowing it, 
otherwise we may be sending wrong requests



##########
jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/KogitoWorkItemImpl.java:
##########
@@ -251,7 +251,18 @@ public String toString() {
             Map.Entry<String, Object> entry = iterator.next();
             b.append(entry.getKey());
             b.append("=");
-            b.append(entry.getValue());
+            Object value = entry.getValue();
+            // Mask sensitive token data for both legacy propagateToken and 
new AccessTokenAcquisitionStrategy
+            if (("propagateToken".equals(entry.getKey()) || 
"AccessTokenAcquisitionStrategy".equals(entry.getKey())) && value != null) {

Review Comment:
   @Christine-Jose I think this won't be required since token won't be directly 
stored in the workitem parameters Map, 



##########
kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/TokenPropagationDecorator.java:
##########
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kogito.workitem.rest.decorators;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+
+import org.kie.kogito.auth.AuthTokenProvider;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.workitems.impl.ConfigResolverHolder;
+import org.kogito.workitem.rest.auth.AuthDecorator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.mutiny.ext.web.client.HttpRequest;
+
+enum AccessTokenAcquisitionStrategy {
+    NONE("none"),
+    CONFIGURED("configured"),
+    PROPAGATED("propagated");
+
+    private String name;
+
+    AccessTokenAcquisitionStrategy(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static AccessTokenAcquisitionStrategy fromName(String strategyName) 
{
+        return Arrays.stream(AccessTokenAcquisitionStrategy.values())
+                .filter(strategy -> strategy.getName().equals(strategyName))
+                .findFirst()
+                .orElse(NONE);
+    }
+}
+
+public class TokenPropagationDecorator implements AuthDecorator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TokenPropagationDecorator.class);
+
+    public static final String ACCESS_TOKEN_ACQUISITION_STRATEGY = 
"AccessTokenAcquisitionStrategy";
+
+    private static final String PROPAGATE_TOKEN_PARAM = "propagateToken";
+
+    private static final String AUTHORIZATION_HEADER = "Authorization";
+    private static final String BEARER_PREFIX = "Bearer ";

Review Comment:
   I think they are not used.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to