elsa-workflows/elsa-core · error · HubException

Access denied.

Error message

Access denied.

What it means

WorkflowInstanceHub.ObserveInstanceAsync first checks whether the connected client is authorized to read workflow instances (CanReadWorkflowInstances). If that permission check fails, the hub throws HubException("Access denied.") before touching the store, and the client's SignalR invocation fails.

Solutions

  1. Ensure the SignalR connection is authenticated with a valid token/cookie and the user has the workflow-instance read permission/policy.
  2. Fix the Elsa authorization configuration (policies/claims/roles) so intended users can read workflow instances.
  3. Handle the HubException on the client and prompt the user to re-authenticate or request access.

Example fix

// before
connection = new HubConnectionBuilder().WithUrl("https://host/hubs/workflow-instance").Build(); // no token
// after
connection = new HubConnectionBuilder().WithUrl("https://host/hubs/workflow-instance", opts =>
    opts.AccessTokenProvider = () => Task.FromResult(authToken)).Build();
Defensive patterns

Strategy: type-guard

Validate before calling

var permitted = user.HasClaim("permissions", "WorkflowInstances:Read");
if (!permitted) throw new UnauthorizedAccessException("User cannot observe workflow instances.");

Type guard

var mayObserve = hubContextUser?.Identity?.IsAuthenticated == true && user.Claims.Any(c => c.Type == "permission" && c.Value.Contains("workflow-instance"));

Try / catch

try { await connection.InvokeAsync("ObserveInstanceAsync", instanceId); }
catch (HubException ex) when (ex.Message == "Access denied.") { /* redirect to login / request permissions */ }

Prevention

When it happens

Trigger: A SignalR client invokes ObserveInstanceAsync while the connection's user lacks the permission/claim/policy required to read workflow instances — e.g. an unauthenticated connection, or a user whose role does not grant workflow-instance read access.

Common situations: Connecting to the hub without an auth cookie/token; tokens lacking the workflow-read claim after a role change; misconfigured Elsa authorization policies that deny the read permission by default; forgetting to configure authentication middleware for the hub path.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/e00729c58eff19fc. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs:40

    private static readonly Permission ReadInstances = new(Elsa.Workflows.Api.Permissions.WorkflowPermissions.Instances, CoreVerbs.View);
    private readonly IWorkflowInstanceStore _workflowInstanceStore;
    private readonly ITenantAccessor? _tenantAccessor;

    /// <inheritdoc />
    public WorkflowInstanceHub(IWorkflowInstanceStore workflowInstanceStore, ITenantAccessor? tenantAccessor = null)
    {
        _workflowInstanceStore = workflowInstanceStore;
        _tenantAccessor = tenantAccessor;
    }
    
    /// <summary>
    /// Observes a workflow instance.
    /// </summary>
    /// <param name="instanceId">The ID of the workflow instance to observe.</param>
    public async Task ObserveInstanceAsync(string instanceId)
    {
        if (!CanReadWorkflowInstances())
            throw new HubException("Access denied.");

        var workflowInstance = await _workflowInstanceStore.FindAsync(new WorkflowInstanceFilter { Id = instanceId }, Context.ConnectionAborted);

        if (!CanAccessTenant(workflowInstance, _tenantAccessor))
            throw new HubException("Access denied.");

        // Join the user to the workflow instance group.
        await Groups.AddToGroupAsync(Context.ConnectionId, instanceId, Context.ConnectionAborted);
    }

    private bool CanReadWorkflowInstances()
    {
        var user = Context.User;

        if (user?.Identity?.IsAuthenticated != true)
            return false;

        return PermissionEvaluator.Shared.HasPermission(user, ReadInstances);

View on GitHub (pinned to fe9217bdfa)