Identity Workflow with AM using Zeebe and Cloud Functions

Written by: Javed Shah

Please note that this is only a sample. The statements, examples or code in this post are not supported by ForgeRock.

Functions as a Service

Serverless computing has emerged as a new and compelling paradigm for the deployment of cloud applications. Offering a true “utility” model for software, developers do not need to worry about low-level details of server management and scaling, and only pay for when processing requests or events.

The most natural way to use serverless computing is to provide a piece of code (function) to be executed by the serverless computing platform. This is where Functions-as-a-Service (FaaS) come in to the picture, allowing small pieces of code to run for limited amount of time (minutes) with executions triggered by events (or HTTP requests) and not allowed to keep persistent state.

From a cost perspective, the benefits of a serverless architecture are most apparent for bursty workloads, because the developer offloads the elasticity of the function to the platform.

Zeebe: Workflow as a Service

Zeebe is a cloud-native workflow engine for BPMN style process automation. It is also Camunda’s open source workflow orchestration engine. With Zeebe, you can define workflows graphically in BPMN 2.0 with the Zeebe Modeler.

Workflows built in Zeebe can react to events from messaging platforms. Zeebe can scale horizontally to handle very high throughput, and provides fault tolerance. There is state representation or state persistence in Zeebe.

Zeebe architecture, taken from their documentation site, is shown below:

The gateway, which proxies requests to brokers, serves as a single entry point to a Zeebe cluster. It is stateless and session-less. Zeebe cloud adds gateways as necessary for load balancing and high availability, thereby removing the burden from the developer of scaling the workflow engine.

The Zeebe broker is the distributed workflow engine that keeps state of active workflow instances. No application business logic lives in the broker. It is responsible for storing and managing the state of active workflow instances.

Zeebe uses BPMN 2.0 for representing workflows. BPMN is an industry standard which is widely supported by different vendors and implementations. Using BPMN ensures that workflows can be interchanged between Zeebe and other workflow systems.

Zeebe Basics

Sequences – In Zeebe, the simplest kind of BPMN workflow is an ordered sequence of tasks. Whenever workflow execution reaches a task, Zeebe creates a job that can be requested and completed by a job worker.

State Machine – Zeebe’s workflow orchestration operates like a state machine. A workflow instance reaches a task, and Zeebe creates a job that can be requested by a worker. Zeebe then waits for the worker to request a job and complete the work. Once the work is completed, the flow continues to the next step. If the worker fails to complete the work, the workflow remains at the current step, and the job could be retried until it’s successfully completed.

Data Flow – As Zeebe progresses from one task to the next in a workflow, it can move custom data in the form of variables. Variables are key-value-pairs and part of the workflow instance.

Parallel Gateway – With Zeebe, you can use the Fork / Join concurrency available with parallel gateways to perform multiple tasks in parallel.

Enforcing Gateway – For implementing data-based Conditions, you can have some workflow nodes choose between different tasks based on variables and conditions. The diamond shape with the “X” in the middle is an element indicating that the workflow decides to take one of many paths.

Onboarding Workflow

For reference, the BPMN workflow used in the example is shown below:

Sample Integration with ForgeRock

This blog post demonstrates invoking an on-boarding BPMN workflow from ForgeRock AM.

Google Cloud Functions

I used the serverless framework to setup my cloud functions in GCP. Each cloud function implements a discrete piece of business logic without storing any state whatsoever. Each cloud function is invoked separately by a specific BPMN workflow node from the Zeebe Cluster (discussed next).

A snippet from the serverless.yml file shows the definition of the create-account cloud function with a handler “createAccount”. My GCP project will host this cloud function at the specific handler.

Running serverless deploy will create the cloud function from the index.js file, from which a quick sample is pasted below:

const body = req.body; let responseBody; console.log(body) responseBody = JSON.stringify({ success: true, cMessage: 'Create successful' }); res.status(200).send( responseBody ); };

Here I am simply returning success from the cloud function. As such, the workflow node should receive a body.success parameter, which it could map to an output parameter, say success to pass along to the next workflow node.

To complete the API composer pattern, we will also create a cloud function that serves to initiate the workflow. This way the initiator of the workflow does not need to know the specifics of the root workflow node.

const ZB = require('zeebe-node') module.exports.customerOnboarding = async (req, res) => { const body = req.body; const zbc = new ZB.ZBClient({ camundaCloud: { clientId: "yXtccn6lWclShJGUQ1aRSfrhJZikYang", clientSecret: "<bleeped>", clusterId: "0263777a-114c-4d2f-9445-47a2290b0320", cacheOnDisk: false } }) const result = await zbc.createWorkflowInstanceWithResult('CustomerOnboarding', body); const responseBody = JSON.stringify({ message: 'onboarding started', data: result }); res.status(200).send( responseBody ); };

The initiator cloud function is able to instantiate the workflow in one of two modes: async, or await. I chose to use the latter in order to receive a workflow processing outcome.

Deployed in GCP:

customerOnboarding will be invoked by our Authentication Tree from AM. createAccount will be invoked by the service task of the same name from inside our BPMN workflow running on a Zeebe workflow cluster. Read on!

Zeebe Cluster

I also created a free Zeebe cluster on the public Camunda cloud, and set it up for HTTP access:

The Zeebe cluster can be invoked over HTTP as depicted by the HTTP worker “job type”. I also defined a worker variable that will serve as the web address for the cloud functions used by the worker nodes to invoke relevant business logic.

The BPMN workflow was modeled using the Zeebe Modeler. The picture below is from Camunda Operate, which offers a cool dashboard to visualize in-flight, completed and failed workflow instances.:

Each “service task”, or worker node must be configured for input/output parameters and headers that must be supplied to it by the root node. The workflow operates like a state machine, with each successive node using the context of the operation to make decisions. In the configuration settings for each node, things that can be passed under “Input parameters” include the request body and those under “Output parameters” can include information needed for successor nodes to correctly operate. An example for input and output parameters, and headers is shown using the createAccount node:

The success output parameter is also the state of the workflow if set by the last worker node! You will see later that this workflow outcome is used by the Authentication Tree to decide whether to authenticate a user or not.

The headers passed into the worker node will be used by the worker node to invoke the cloud function responsible for executing the node’s business logic. In this case, it would be the google cloud function createAccount specified in the serverless.yml file in the previous section. Also, notice the body.success being mapped to a workflow variable, called success. The success variable is used by the “certified?” Enforcing Gateway to determine whether manual review is needed, or if it is okay to finish on-boarding the customer.

Let’s look at the configuration for one of the SequenceFlows, the outbound connectors from the “certified?” Enforcing Gateway:

The SequenceFlow connector labelled Yes only triggers if the workflow variable success evaluates to true, otherwise the alternate flow is triggered resulting in a manual review.

AM Integration

First. I created a workflow script, which is responsible for building out the request body with user and context data. It calls our workflow-initiator cloud function:

Next, I set up a really simple authentication tree called WorkflowLogin to use the workflow script. This is just to demonstrate use. The use case is that the user must be on-boarded before they can be authenticated and given a session by AM. As mentioned earlier, the Zeebe workflow is able to return a workflow outcome in the form of result.get("variables").get("status"), which can be checked for by in the scripted decision node.

A simple dry run from the CLI directly invoking the customerOnboarding initiator cloud function shows how to manually send an HTTPS request to the workflow initiator cloud function:

curl -H "Content-Type: application/json" -X PUT -d @request-zeebe.json https://<bleeped> | jq

  "message": "onboarding started",
"result": {
   "workflowKey": "2251799813746599",
   "bpmnProcessId": "CustomerOnboarding",
  "version": 1,
   "workflowInstanceKey": "2251799813757247",
   "variables": {
     "body": {
       "success": true,
       "cMessage": "welcome email successful"
     "success": true,
     "statusCode": 200,
     "attributeValues": "joe, black, 999881"

Similarly, invoking the authentication tree directly from the CLI presents a similar response:

curl --request POST --header "Accept-API-Version: resource=2.0, protocol=1.0" --header "Content-Type: application/json" --header "X-OpenAM-Username: demo" --header "X-OpenAM-Password: <>" '' | jq'


This completes the demonstration of invoking a powerful cloud workflow engine, Zeebe from an authentication tree in AM via Google Cloud Functions. Leave your thoughts and comments below…

Thanks for reading!