Security Services
Security services in the Web API add authentication and authorization to Workflow API operations. They use standard
ASP.NET authentication and read a compact WorkflowApiPermissions claim from the current ClaimsPrincipal.
The permission claim can describe both operation access and tenant access. This is important in multi-tenant mode:
Workflow-Api-Tenant-ID selects the tenant context for the request, but it does not grant access by itself. The selected
tenant must also be allowed by the WorkflowApiPermissions claim.
To integrate security services, first include the core and database provider services.
Integration
Security services are included in the OptimaJet.Workflow.Api NuGet package, which you should have already added when
integrating the core services.
Add the security services during application startup by using an extension method on IServiceCollection. You can also
configure their options:
builder.Services.AddWorkflowApiSecurity(options =>
{
// Configure the Workflow Engine Web API Security options.
options.DisableSecurity = false;
options.SecurityScheme = null;
});
You have now integrated the Web API security services. By default, they use the security scheme that is set as the default when configuring ASP.NET authentication services. This typically looks like:
builder.Services.AddAuthentication("YourScheme");
Security Options
You can configure options for the security services by using the WorkflowApiSecurityOptions record via ASP.NET
IOptions in the service collection. The table below describes its properties:
| Name | Type | Default | Description |
|---|---|---|---|
| DisableSecurity | bool | false | Allows disabling authentication and authorization. By default, authentication and authorization are enabled when adding AddWorkflowApiSecurity(). |
| SecurityScheme | string? | null | The authentication scheme used for Workflow Engine API operations. When null, the default authentication scheme set in AuthenticationOptions will be used. |
Permission Claim
When security is enabled, every Workflow API request must be authenticated and authorized. The Web API expects one permission claim:
| Name | Value |
|---|---|
WorkflowApiConstants.PermissionsClaimType | WorkflowApiPermissions |
The value is a compact semicolon-separated list of permission rules:
<effect>:<target>[;<effect>:<target>]
The effect is a for allow and d for deny. The target is either an operation branch under workflow-api or a tenant
rule under tenants.
Examples:
| Value | Meaning |
|---|---|
a:workflow-api | Allow all Workflow API operations. |
d:workflow-api | Deny all Workflow API operations. |
d:workflow-api;a:workflow-api.liveness | Deny all operations except liveness. |
d:workflow-api;a:workflow-api.rpc | Deny all operations except the RPC branch. |
a:workflow-api;d:workflow-api.rpc.delete-instance | Allow all operations except delete-instance. |
a:tenants | Allow all configured tenants. |
d:tenants | Deny all tenants. |
a:tenants:TenantA,TenantB | Deny all tenants except TenantA and TenantB. |
d:tenants:TenantA,TenantB | Allow all tenants except TenantA and TenantB. |
Operation permissions are hierarchical. A branch target such as workflow-api.rpc applies to all operations below that
branch, so do not append a .* suffix. If several operation rules match a request, the more specific rule wins. For
example, a:workflow-api.rpc;d:workflow-api.rpc.delete-instance allows the RPC branch but denies
workflow-api.rpc.delete-instance.
Tenant permissions are evaluated as one tenant rule. Use DenyAllTenantsExcept(...) for a strict allow list, or
AllowAllTenantsExcept(...) when most tenants should be allowed with a small deny list.
The single compact claim keeps JWT payloads small, especially when the token is stored in authentication cookies. Prefer
branch rules such as workflow-api.rpc or workflow-api.data.schemes instead of listing every leaf operation when the
same access policy applies to the whole branch.
Claim Generation
Use the IWorkflowApiPermissions service to build and validate the claim value. It creates a normalized
WorkflowApiPermissions claim and rejects unauthorized operation targets or tenant ids.
var claim = permissions.BuildClaim(builder => builder
.DenyAllOperations()
.Allow(
WorkflowApiOperationId.Liveness,
WorkflowApiOperationId.DataSchemesGetCollection,
WorkflowApiOperationId.DataSchemesGet)
.DenyAllTenantsExcept("TenantA"));
This claim allows only the listed operations and only TenantA in multi-tenant mode.
To allow all operations and all tenants:
var claim = permissions.BuildClaim(builder => builder
.AllowAllOperations()
.AllowAllTenants());
To allow an operation branch with a more specific deny:
var claim = permissions.BuildClaim(builder => builder
.DenyAllOperations()
.Allow("workflow-api.rpc")
.Deny(WorkflowApiOperationId.RpcDeleteInstance)
.AllowAllTenants());
To store or send the permissions value separately from the JWT claim, use the builder directly:
var permissionsValue = permissions.CreateBuilder()
.DenyAllOperations()
.Allow("workflow-api.search")
.DenyAllTenantsExcept("TenantA", "TenantB")
.GetValue();
You can validate a stored value before saving it:
var isValid = permissions.CreateBuilder().ValidateValue(permissionsValue);
You can also build a claim from an already serialized value:
var claim = permissions.BuildClaim("d:workflow-api;a:workflow-api.search;a:tenants:TenantA,TenantB");
You can find a detailed JWT Bearer implementation in our GitHub sample.
JWT Example
The example below shows the relevant part of issuing a JWT token:
Claim[] claims =
[
permissions.BuildClaim(builder => builder
.DenyAllOperations()
.Allow("workflow-api.rpc")
.Deny(WorkflowApiOperationId.RpcDeleteInstance)
.DenyAllTenantsExcept("TenantA")),
new(ClaimTypes.Name, user.Name)
];
In multi-tenant mode, the client still sends Workflow-Api-Tenant-ID with each request:
curl -X POST \
-H "Authorization: Bearer <token>" \
-H "Workflow-Api-Tenant-ID: TenantA" \
-H "Content-Type: application/json" \
-d "{}" \
https://localhost:7169/workflow-api/rpc/runtime-get-running-status
The request is authorized only when both checks pass:
- The claim allows the requested operation.
- The claim allows the tenant resolved from
Workflow-Api-Tenant-IDor fromWorkflowApiCoreOptions.DefaultTenantId.
If the operation is denied, tenant permission is missing, the tenant header is missing and no default tenant is
configured, the tenant id is invalid, the tenant is unknown, or the header is changed to a tenant not allowed by the
token, the request returns 403 Forbidden. In single-tenant mode, the tenant permission rule is not required.
Operation Identifiers
Permissions in the Web API are organized in a tree structure, where each node represents either an operation identifier or a group of operations. You can use either exact operation identifiers or branch targets in permission rules. Below is a list of all existing operation identifiers.
WorkflowApi API
workflow-api.liveness— Check whether the API service is alive.workflow-api.readiness— Check whether all tenants are ready to accept requests.workflow-api.tenant-readiness— Check whether the current tenant is ready to accept requests.
Search API
workflow-api.search.runtimes— Search runtimes.workflow-api.search.global-parameters— Search global parameters.workflow-api.search.schemes— Search schemes.workflow-api.search.statuses— Search process statuses.workflow-api.search.processes— Search processes.workflow-api.search.processes.parameters— Search process parameters.workflow-api.search.processes.timers— Search process timers.workflow-api.search.processes.transitions— Search process transitions.workflow-api.search.processes.approvals— Search process approvals.workflow-api.search.processes.inbox-entries— Search process inbox entries.
Data API
Runtimes
workflow-api.data.runtimes.get-collection— Get runtime collection.workflow-api.data.runtimes.get— Get runtime.
Global Parameters
workflow-api.data.global-parameters.get-collection— Get global parameters collection.workflow-api.data.global-parameters.create-collection— Create global parameters collection.workflow-api.data.global-parameters.delete-collection— Delete global parameters collection.workflow-api.data.global-parameters.get— Get global parameter.workflow-api.data.global-parameters.create— Create global parameter.workflow-api.data.global-parameters.update— Update global parameter.workflow-api.data.global-parameters.delete— Delete global parameter.
Schemes
workflow-api.data.schemes.get-collection— Get schemes collection.workflow-api.data.schemes.create-collection— Create schemes collection.workflow-api.data.schemes.delete-collection— Delete schemes collection.workflow-api.data.schemes.get— Get scheme.workflow-api.data.schemes.create— Create scheme.workflow-api.data.schemes.update— Update scheme.workflow-api.data.schemes.delete— Delete scheme.
Statuses
workflow-api.data.statuses.get-collection— Get statuses collection.workflow-api.data.statuses.get— Get status.
Processes
workflow-api.data.processes.get-collection— Get processes collection.workflow-api.data.processes.get— Get process.workflow-api.data.processes.update— Update process.
Process Parameters
workflow-api.data.processes.parameters.get-collection— Get process parameters collection.workflow-api.data.processes.parameters.create-collection— Create process parameters collection.workflow-api.data.processes.parameters.delete-collection— Delete process parameters collection.workflow-api.data.processes.parameters.get— Get process parameter.workflow-api.data.processes.parameters.create— Create process parameter.workflow-api.data.processes.parameters.update— Update process parameter.workflow-api.data.processes.parameters.delete— Delete process parameter.
Process Timers
workflow-api.data.processes.timers.get-collection— Get process timers collection.workflow-api.data.processes.timers.create-collection— Create process timers collection.workflow-api.data.processes.timers.delete-collection— Delete process timers collection.workflow-api.data.processes.timers.get— Get process timer.workflow-api.data.processes.timers.create— Create process timer.workflow-api.data.processes.timers.update— Update process timer.workflow-api.data.processes.timers.delete— Delete process timer.
Process Transitions
workflow-api.data.processes.transitions.get-collection— Get process transitions collection.workflow-api.data.processes.transitions.delete-collection— Delete process transitions collection.workflow-api.data.processes.transitions.get— Get process transition.workflow-api.data.processes.transitions.delete— Delete process transition.
Process Approvals
workflow-api.data.processes.approvals.get-collection— Get process approvals collection.workflow-api.data.processes.approvals.delete-collection— Delete process approvals collection.workflow-api.data.processes.approvals.get— Get process approval.workflow-api.data.processes.approvals.delete— Delete process approval.
Process Inbox Entries
workflow-api.data.processes.inbox-entries.get-collection— Get process inbox entries collection.workflow-api.data.processes.inbox-entries.delete-collection— Delete process inbox entries collection.workflow-api.data.processes.inbox-entries.get— Get process inbox entry.workflow-api.data.processes.inbox-entries.delete— Delete process inbox entry.
RPC API
Bulk API
workflow-api.rpc.bulk-create-instance— Create multiple process instances.workflow-api.rpc.bulk-get-process-instance— Get multiple process instances with parameters.workflow-api.rpc.bulk-get-process-instances-tree— Get multiple root process instances with all subprocesses as trees.workflow-api.rpc.bulk-get-available-commands— Get multiple lists of commands available for current activities and identities.workflow-api.rpc.bulk-execute-command— Execute multiple commands with specified identity.workflow-api.rpc.bulk-update-scheme-if-obsolete— Update multiple schemes of process instances if they are obsolete.workflow-api.rpc.bulk-delete-instance— Delete multiple process instances with all their subprocesses.workflow-api.rpc.bulk-is-process-exists— Check existence of multiple process instances.
Commands API
workflow-api.rpc.get-available-commands— Get the list of commands available for current activity and identities.workflow-api.rpc.execute-command— Execute command with specified identity.workflow-api.rpc.get-initial-commands— Get the list of commands available for initial activity and identities.
Instance API
workflow-api.rpc.create-instance— Create instance of the process.workflow-api.rpc.delete-instance— Delete process instance and all child subprocesses.workflow-api.rpc.is-process-exists— Check existence of the process instance.workflow-api.rpc.get-process-instance-tree— Get the root process instance and all subprocesses as a tree.workflow-api.rpc.get-process-instance— Get the process instance with all parameters.workflow-api.rpc.get-process-history— Get the history records for the process instance.workflow-api.rpc.get-process-history-count— Get the count of history records for the process instance.workflow-api.rpc.set-process-new-status— Set a new status for the process instance.workflow-api.rpc.get-process-status— Get the status of the process instance.workflow-api.rpc.delete-all-subprocesses— Delete all subprocesses of the process instance.workflow-api.rpc.check-all-subprocesses-completed— Check if all subprocesses of the process instance are completed.workflow-api.rpc.set-process-parameter— Set the value of a process parameter with persistence purpose.workflow-api.rpc.get-process-parameter— Get the value of a process parameter.
Log API
workflow-api.rpc.log-debug— Logs a debug message with parameters.workflow-api.rpc.log-debug-if-logger-exists— Logs a debug message with parameters if the logger exists.workflow-api.rpc.log-info— Logs an info message with parameters.workflow-api.rpc.log-info-if-logger-exists— Logs an info message with parameters if the logger exists.workflow-api.rpc.log-error— Logs an error message with parameters.workflow-api.rpc.log-error-if-logger-exists— Logs an error message with parameters if the logger exists.
Pre-Execution API
workflow-api.rpc.pre-execute-from-initial-activity— Pre-execute from the initial activity of the process instance.workflow-api.rpc.pre-execute-from-current-activity— Pre-execute from the current activity of the process instance.workflow-api.rpc.pre-execute— Pre-execute from the specified activity of the process instance.
Runtime API
workflow-api.rpc.runtime-shut-down— Shuts down the workflow runtime.workflow-api.rpc.runtime-start— Starts the workflow runtime.workflow-api.rpc.runtime-cold-start— Starts the workflow runtime in cold start mode.workflow-api.rpc.runtime-get-running-status— Gets the running status of the workflow runtime.
Scheme API
workflow-api.rpc.get-scheme-codes— Get scheme codes with optional filtering by tags.workflow-api.rpc.set-scheme-is-obsolete— Set all process schemes with the specified scheme code as obsolete.workflow-api.rpc.update-scheme-if-obsolete— Update scheme of the process instance if it is obsolete.workflow-api.rpc.get-process-scheme— Get the scheme of the process instance.
State API
workflow-api.rpc.get-available-states-to-set— Get the list of all available to set states for the process instance.workflow-api.rpc.get-available-states-to-set-by-scheme-code— Get the list of all available to set states for the scheme.workflow-api.rpc.set-state-without-execution— Set state for the process instance without execution.workflow-api.rpc.set-state-with-execution— Set state for the process instance with execution.workflow-api.rpc.set-activity-without-execution— Set activity for the process instance without execution.workflow-api.rpc.set-activity-with-execution— Set activity for the process instance with execution.workflow-api.rpc.resume— Resumes the process instance execution.workflow-api.rpc.get-current-state-name— Get the current state name of the process instance.workflow-api.rpc.get-current-activity-name— Get the current activity name of the process instance.workflow-api.rpc.get-current-state— Get the current state of the process instance.workflow-api.rpc.get-initial-state— Get the initial state of the scheme.
Designer API
workflow-api.designer— Workflow Designer connector access.workflow-api.designer.get— Workflow Designer connector access.
IWorkflowApiPermissions
Service for building and authorizing Workflow Engine API permission claims.
CreateBuilder()
Creates a new IWorkflowApiPermissionsBuilder instance. Builder instances are independent and can be used to create,
validate, or normalize serialized permission values.
Returns
| Type | Description |
|---|---|
IWorkflowApiPermissionsBuilder | A new permissions builder. |
BuildClaim(string value)
Builds a WorkflowApiPermissions claim from a serialized permissions value. The value is validated and normalized by
the builder before the claim is returned.
Parameters
| Name | Type | Description |
|---|---|---|
| value | string | A serialized permissions value. |
Returns
| Type | Description |
|---|---|
Claim | A Workflow API permissions claim. |
BuildClaim(Action<IWorkflowApiPermissionsBuilder> configure)
Builds a WorkflowApiPermissions claim using a temporary permissions builder.
Parameters
| Name | Type | Description |
|---|---|---|
| configure | Action<IWorkflowApiPermissionsBuilder> | A delegate that configures builder. |
Returns
| Type | Description |
|---|---|
Claim | A Workflow API permissions claim. |
HttpContextHasAccess(ClaimsPrincipal principal, string operationId)
Checks whether the principal has access to the specified operation and to the tenant resolved from the current HTTP context. This method is used by the Workflow API authorization pipeline.
Parameters
| Name | Type | Description |
|---|---|---|
| principal | ClaimsPrincipal | The current claim principal. |
| operationId | string | The operation identifier to authorize, such as workflow-api.liveness. |
Returns
| Type | Description |
|---|---|
bool | true when access is granted; otherwise false. |
IWorkflowApiPermissionsBuilder
Fluent builder for serialized Workflow API permission values.
AllowAllOperations()
Replaces the current operation permissions with a policy that allows all operations by default. Additional explicit operation rules may deny specific branches or exact operations.
DenyAllOperations()
Replaces the current operation permissions with a policy that denies all operations by default. Additional explicit operation rules may allow specific branches or exact operations.
Allow(params string[] targets) and Allow(IEnumerable<string> targets)
Adds explicit allow operation rules to the selected operation policy. This method is available after
AllowAllOperations() or DenyAllOperations() because it belongs to IWorkflowApiOperationPermissionsBuilder.
Parameters
| Name | Type | Description |
|---|---|---|
| targets | string[] or IEnumerable<string> | Exact operation identifiers or operation branch targets to allow. |
Deny(params string[] targets) and Deny(IEnumerable<string> targets)
Adds explicit deny operation rules to the selected operation policy. This method is available after
AllowAllOperations() or DenyAllOperations() because it belongs to IWorkflowApiOperationPermissionsBuilder.
Parameters
| Name | Type | Description |
|---|---|---|
| targets | string[] or IEnumerable<string> | Exact operation identifiers or operation branch targets to deny. |
AllowAllTenants()
Replaces the current tenant permissions with a policy that allows all tenants by default.
AllowAllTenantsExcept(params string[] tenantIds) and AllowAllTenantsExcept(IEnumerable<string> tenantIds)
Replaces the current tenant permissions with a policy that allows all tenants by default except the specified tenant ids.
Parameters
| Name | Type | Description |
|---|---|---|
| tenantIds | string[] or IEnumerable<string> | Tenant ids to deny. |
DenyAllTenants()
Replaces the current tenant permissions with a policy that denies all tenants by default.
DenyAllTenantsExcept(params string[] tenantIds) and DenyAllTenantsExcept(IEnumerable<string> tenantIds)
Replaces the current tenant permissions with a policy that denies all tenants by default except the specified tenant ids.
Parameters
| Name | Type | Description |
|---|---|---|
| tenantIds | string[] or IEnumerable<string> | Tenant ids to allow. |
GetValue()
Returns the serialized permissions value from the current builder state.
Returns
| Type | Description |
|---|---|
string | A serialized permissions value. |
SetValue(string value)
Replaces the current builder state with the rules from a serialized permissions value.
Parameters
| Name | Type | Description |
|---|---|---|
| value | string | A serialized permissions value. |
Returns
| Type | Description |
|---|---|
IWorkflowApiPermissionsBuilder | The current permissions builder. |
ValidateValue(string value)
Validates a serialized permissions value.
Parameters
| Name | Type | Description |
|---|---|---|
| value | string | A serialized permissions value. |
Returns
| Type | Description |
|---|---|
bool | true when value is valid; otherwise false. |