What is the pattern for Impersonation in Camunda 8

Hi there
I am wondering, what would be the right pattern to implement impersonation in Camunda 8?

With impersonation I mean that you can call a REST API later in the process with the user that started the process, even the token of the user is not valid anymore.

We use OAuth2 with Keycloak. In Camunda 7 we do this manually with Keycloak REST API and the grant type urn:ietf:params:oauth:grant-type:token-exchange.
For this we get the authenticatedUserId from Camunda.

Hey @pme123 - I’ve been thinking about this today, and I don’t quite have a good pattern for you yet. I have asked around to gather thoughts from others and am waiting on their response.

Are the calls that need to be impersonated to your API, or to a third party? Does your Keycloak setup allow impersonation?

My first thought is to use Keycloak’s built-in impersonation features, using a “zeebe” user to fetch a token that impersonates the user, and use that token in further requests. As for implementing that, I think I would first try an execution listener on any tasks need to use impersonation and generate the token securely in your listener. (I don’t know that this is best, just my first thought!)

I’ll ask around for more ideas!

1 Like

Hi @nathan.loding thanks for the response.
We have a gateway that handles the token stuff (so for all APIs the same token). And yes our Keycloak supports impersonation (see the grant type in my question).
With Camunda 7 we did the following:

  1. In a Starter Listener Skript we persisted the UserId in a process variable.
    execution.getProcessEngineServices().getIdentityService().getCurrentAuthentication().getUserId().
  2. And then when calling the service, we get the token from keycloak for that user.
    So like your proposal, only that we fetch the token for each service (as the token may not be active anymore).

@pme123 - for step 1, I would instead start a process with the userId variable and then consume it in the execution listener (which should be passed all the in scope variables).

While I don’t pretend to be an expert in this area, this is the pattern I would try in my process if I were developing it right now. I’d love to hear any other solutions people have!

@nathan.loding - How do I get to the userId? Is it provided by Camunda, as I don’t have access to the token (Camunda Service)?

So I think that we need a gateway service that extracts the userId and adds it to the input variables before calling the Camunda REST service.

@pme123 - I was imagining that the ID would be provided when you start the process. Something like:

final ProcessInstanceEvent processInstanceEvent =
    client
        .newCreateInstanceCommand()
        .bpmnProcessId(bpmnProcessId)
        .latestVersion()
        .variables(mapWithUserId) // userId: 123
        .send()
        .join();

One of our consultants had another suggestion, which is to use a credentials provider as a secrets provider: camunda-8-examples/secret-provider-as-credentials-provider at main · camunda-community-hub/camunda-8-examples · GitHub

I am not sure I understand this secret provider. How does the userId go into the provider, if the userId is only in the token?

@pme123 - yes, there are a lot of questions still, and we don’t know all the factors at play in your environments.

The provider could be used to fetch secure values based on other data in the process. For instance, if you had a process variable with the user ID, and a set of secrets for that user ID (perhaps a client ID and client secret), the provider could fetch those secrets to be used in the REST connector.

Both approaches assume that the user ID is available as a global process variable, and there are pros and cons to each.

1 Like

ok, thanks @nathan.loding !