We ended up creating a small loop that would cancel the expirable task and create a new one.
Inside our user task, we created two listeners: assignment and timeout.
The idea behind this is that, once an user claims the task, the assignment listener is triggered and we would update an ENDLESSLY LARGE timeout (created in the beggining of the process) for the time we want as “claim timeout” for the timeout listener. This is needed because Camunda demands that a timeout listener has an assigned time.

In our case, 60 days is more than enough because this user task will be triggered earlier.
Once the user claims the task and the due date is reached, the timeout is triggered and will complete the task (without actually doing anything to it) and recreate the same task, this time with no one assigned to it. This is a viable solution for us since we do not make use of Camunda’s cockpit for the completion of tasks.
The assignment listener exists so that we can update the due date of the user task and the job behind it.
Assignment listener code:
var timeout = task.getVariable('characteriseTimeoutHours');
var expirationDate = new Date();
expirationDate.setSeconds(expirationDate.getSeconds() + timeout);
task.dueDate = new java.util.Date(expirationDate.toString());
//Clear timeout flag
task.setVariable('characteriseTimedout', false);
//Get time out job
var job = managementService
.createJobQuery()
.activityId(task.execution.activityId)
.processInstanceId(task.processInstanceId)
.singleResult();
var timeout = task.getVariable('characteriseTimeoutHours');
var expirationDate = new Date();
expirationDate.setSeconds(expirationDate.getSeconds() + timeout);
//Update timeout to a new one
managementService.setJobDuedate(job.getId(), new java.util.Date(expirationDate.toString()));
Timeout listener code:
task.setVariable('characteriseTimedout', true);
taskService.complete(task.id);