Spring boot security looses context after TimerCatchingEvent

Hi,

I have integrated Camunda with Spring boot.
My workflow contains one TimerCatchingEvent, which is used because I want to call a service task taskB after 5 mins of service task taskA. So I have used TimerCatchingEvent.

But in taskB, I’m getting current logged in user from Spring Security Context. I’m getting null in taskB.

Help is apprecited.
I’m stuck at this and process is failing.

Thanks
Ashish

Hi Ashish,

You need to give a lot more context. Upload your model and the specific error message

This is the workflow in which I’m facing issues. There is not exception in the code.
But when I try to get current user context, SecurityUtils.getCurrentUser(), which should give me the current logged in user, I’m getting null in the service task immediately after the timer catching event (highlighted one).
But same thing works fine before the timer event.

Can you upload the BPMN file itself, as well as the specific error being thrown

AssignmentFlow.bpmn (33.1 KB)
Attaching the bpmn file.
As i mentioned, I’m not getting any exception,
I’m just not getting the current logged in user. I’m not using Camunda’s identityService to do the authentication, instead I’m using the Spring security for authentication.

Hi @ash6892
Are you still facing this issue or did you solve it yourself?
I am facing the same issue, will be helpful if you could post your findings.
@Niall This seems like an issue with timer events and asynchronous continuation. after it is enabled the securitycontext is loosing authentication object.

Hi,

I’m not an expert on Spring security, but I think I can help you with the general direction. Usually such context is bound to the thread that is handling a HTTP request. So when the process encounters a transaction boundary, the HTTP thread returns and eventually a job executor thread that doesn’t have this context takes over. If you want to preserve any properties from the context for later parts in the process, you could store it in process variables and then recover it, e.g. from an execution listener.

Cheers,
Thorben

@Shibin_Thomas2 This is expected behaviour. Whenever waiting state happens thread will return and the following tasks/events will loose the context. If you’re generating some authorization tokens in those waiting states you can store the metadata as variables and use it to regenerate the context until the next http request in the subsequent tasks.

Hmm i understand the complications now.
Actually i was thinking i can enable the spring SecurityContext MODE_INHERITABLETHREADLOCAL and make it working. But unfortunately that didn’t
Now i came to know camunda has a property to control camunda.bpm.job-execution.enabled, by default it will be false. So, enabling that can create a new bean camundaTaskExecutor.
I am currently trying to override this bean and create my version with DelegatingSecurityContextAsyncTaskExecutor. So far no luck

The problem is, i am using webclient and it internally manages its authenticated token and its refresh and setting the access token to camunda variables may cause potential security issues / token refresh issues if expired. So i need an alternative solution
@thorben @aravindhrs

@Shibin_Thomas2 The idea was to store attributes (tenant id, userid, permission, etc) as variables (possibly encryption can be applied) to generate the token in async execution context, not to store the context/token itself. So that you can invoke a mechanism using those attributes stored as variables to regenerate the token.

Make sense
I am also curious to know how camunda manages to carry execution variables from one thread to another. Does it copy? or execution variables are thread bound?
I just want to see if i can copy context from one thread to another without involving execution variable
@aravindhrs @thorben @Niall appreciate your response.

When the thread completes successfully it simply commits the state of the process as well as the variables to the database. The next time a token is active on the process it will have the context by loading it from the DB (or from cache)

@Niall @thorben @aravindhrs @ash6892

After analysing the issue on asynchronous continuation I am listing below my findings and available solution.

The issue was losing context when asynchronous continuation (before / after) is enabled.

Due to this, the call to webapi service was failing because the authenticated call needed to have a security context / authentication object but that was null.

Now, the root cause of this is listed below.

  • During an asynchronous continuation / timer boundary event, camunda commits its existing thread data and starts a new thread as a job and runs it using a JobExecuter / ExecuterService.
  • When a new thread is created and to continue carrying process variables, camunda persists the variables in the datasource and reloads them.
  • This affects Spring Security since spring security uses THREADLOCAL as the default strategy to store all the authentication related data and that will not propagate authentication data eg: token , authentication object.

The solution available is to increase the scope of webclient from HttpServletRequest to outside HttpServletRequest (AuthorizedClientServiceOAuth2AuthorizedClientManager), but this only solves half of the issue.

(ServletRequest cannot be null) - resolved

The potential issue is if any listener uses SecurityContextHolder.getContext().getAuthentication() call to capture authentication data like group / role / any claim data then it produces a nullpointerexception because authentication is null in the new thread.

To resolve this while designing the workflow add below script as an execution start script.

Add below line before time boundary event / asynchronous continuation

execution.setVariable(“authentication”,org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication());

Add below line after time boundary event / asynchronous continuation

org.springframework.security.core.context.SecurityContextHolder.getContext().setAuthentication(execution.getVariable(“authentication”));

This saves authentication data in the camunda memory and restores them in the new thread.