My Journey With Camunda — Part 6 — Custom Tasklist Application

Nurul Arif Setiawan
5 min readMar 1, 2021
Let’s create our own tasklist. Photo by Hal Gatewood on Unsplash

This is multipart series where I wrote my journey using Camunda from a non-java developer perspective.

In Part 5, we have seen Camunda Tasklist. It’s a good web app that normal user can use to perform their daily tasks. However, to further customize task form in Camunda Tasklist, we will need to use Java stack. For example, in some cases, user might want to have different UI for their applications or maybe they already have existing application that need to be updated with Tasklist capability. In this part, we will see how to create a custom Tasklist app.

Another component that we should add is authentication to Camunda REST API. I have mentioned in previous post that Camunda REST API is not protected by default. We can add basic authentication but I think this is not a good approach for web app because users will need to specify their username and password each time they want to do something. Depending on how the app will be implemented, we can have session (similar to Camunda apps) or token based authentication (e.g. JWT). Here, we will create an authentication proxy for Camunda REST API that uses JWT. User will login with his/her username and password that we created in Camunda Admin, proxy will return with a JWT token that will be used for next requests.

Overall architecture of our applications will be like following image:

Our applications and Camunda

Frontend (Nexus)

For the frontend, I will use CoreUI Vue.js Admin Template to create our UI. I will call my application Nexus. If you have to ask why Nexus? Nexus means a connection or series of connections linking two or more things. So this app is intended as connection between people and teams inside a company. That is what workflow is used for.

Below is the login page.

nexus login page
Login page

After logged in, user can see homepage. Obviously this needs improvements, but we will leave that for later.

home page. feels empty :(

Then, we have forms. Currently we only have single form that is Leave Requests.

leave request page. we can have auto-fill

When a user submit a Leave Request form, it will trigger process instance in Camunda and it will create user tasks assigned to specific group to approve the requests. For example, in above Leave Request form, we logged in as Sam Purple and we ask for 10 days leave. Note that because we developed this application, we can auto-fill the forms with user information that we already have.

Our DMN will assign this group manager and if we logged in as Sophia Green (who is the Manager), we will see that she has tasks to approve the requests.

user task list
task approval form

Proxy

The next part is the proxy. This is a web application that use Echo framework. Echo is a high performance and simple Golang web application framework. Proxy APIs is protected by JWT middleware and used by Nexus for all UI functionality. This proxy will call Camunda REST API to start process instance, get user tasks and complete user tasks. I used Resty as my HTTP client.

When log in, we will check to Camunda if username and password is correct using verify-user API. This is shown in following snippet. Below is Camunda REST API client.

// IdentityVerifyRequest ...
type IdentityVerifyRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
// IdentityVerifyResponse ...
type IdentityVerifyResponse struct {
AuthenticatedUser string `json:"authenticatedUser"`
Authenticated bool `json:"authenticated"`
}
// Client ...
type Client struct {
APIURL string
Client *resty.Client
}
// NewClient ...
func NewClient(url string) *Client {
c := &Client{
APIURL: url,
}
c.Client = resty.New()
return c
}
// IdentityVerify ...
func (c *Client) IdentityVerify(param *IdentityVerifyRequest) (response *IdentityVerifyResponse, err error) {
resp, err := c.Client.R().
SetHeader("Content-Type", "application/json").
SetBody(param).
SetResult(&IdentityVerifyResponse{}).
Post(c.APIURL + "/identity/verify")
if err != nil {
return nil, err
}
if resp.StatusCode() != 200 {
return nil, errors.New(resp.String())
}
return resp.Result().(*IdentityVerifyResponse), err
}

And below is how I used in Echo request handler

// SetRoutes ...
func (h *AuthHandler) SetRoutes(e *echo.Group) {
e.POST("/login", h.login)
}
func (h *AuthHandler) login(c echo.Context) (err error) {
login := new(Login)
err = c.Bind(login)
if err != nil {
return errors.New("Failed to get login data. Probably content-type is not match with actual body type")
}
// verify user to camunda
user, err := h.CamundaClient.IdentityVerify(&camunda.IdentityVerifyRequest{
Username: login.Username,
Password: login.Password,
})
if err != nil {
return err
}
if !user.Authenticated {
return c.JSON(http.StatusUnauthorized, user)
}
// Set custom claims
claims := &jwt.StandardClaims{
Audience: "Camunda Custom Tasklist",
IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Add(time.Hour * 72).Unix(),
Issuer: "Camunda Proxy",
Subject: user.AuthenticatedUser,
}
// Create token with claims
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte("secret"))
if err != nil {
return err
}
return c.JSON(http.StatusOK, echo.Map{
"token": t,
})
}

This is the pattern that I used

  1. See what API is needed by the Frontend
  2. Add Camunda REST API wrapper
  3. Add handler in Echo so that Frontend can use it.

Source Code

Source code for this part is available in Github.

The code is enough for demonstrating the how we can develop application that use Camunda BPM. But there will be some efforts needed to polish the application if you want to use it for real world usage. Please fork it, improve it and do pull requests (if you want).

This repo also contain external task service that we already have in Part 4 — External Service Task with Golang. External tasks handler is currently still doing nothing because it will be very implementation specific.

Next Step

The next part will be deploying this application to servers and get logs and monitoring that we needed to operate this application. We want to be close to real deployment scenario. There are also some improvements that I want to add to Nexus app.

--

--

Nurul Arif Setiawan

software engineer. product design, planning and development.