{"record":{"id":"e00729c58eff19fc","repo":"elsa-workflows/elsa-core","slug":"access-denied-workflowinstancehub","errorCode":null,"errorMessage":"Access denied.","messagePattern":"Access denied\\.","errorType":"exception","errorClass":"HubException","httpStatus":403,"severity":"error","filePath":"src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs","lineNumber":40,"sourceCode":"    private static readonly Permission ReadInstances = new(Elsa.Workflows.Api.Permissions.WorkflowPermissions.Instances, CoreVerbs.View);\n    private readonly IWorkflowInstanceStore _workflowInstanceStore;\n    private readonly ITenantAccessor? _tenantAccessor;\n\n    /// <inheritdoc />\n    public WorkflowInstanceHub(IWorkflowInstanceStore workflowInstanceStore, ITenantAccessor? tenantAccessor = null)\n    {\n        _workflowInstanceStore = workflowInstanceStore;\n        _tenantAccessor = tenantAccessor;\n    }\n    \n    /// <summary>\n    /// Observes a workflow instance.\n    /// </summary>\n    /// <param name=\"instanceId\">The ID of the workflow instance to observe.</param>\n    public async Task ObserveInstanceAsync(string instanceId)\n    {\n        if (!CanReadWorkflowInstances())\n            throw new HubException(\"Access denied.\");\n\n        var workflowInstance = await _workflowInstanceStore.FindAsync(new WorkflowInstanceFilter { Id = instanceId }, Context.ConnectionAborted);\n\n        if (!CanAccessTenant(workflowInstance, _tenantAccessor))\n            throw new HubException(\"Access denied.\");\n\n        // Join the user to the workflow instance group.\n        await Groups.AddToGroupAsync(Context.ConnectionId, instanceId, Context.ConnectionAborted);\n    }\n\n    private bool CanReadWorkflowInstances()\n    {\n        var user = Context.User;\n\n        if (user?.Identity?.IsAuthenticated != true)\n            return false;\n\n        return PermissionEvaluator.Shared.HasPermission(user, ReadInstances);","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the SignalR connection is authenticated with a valid token/cookie and the user has the workflow-instance read permission/policy.","Fix the Elsa authorization configuration (policies/claims/roles) so intended users can read workflow instances.","Handle the HubException on the client and prompt the user to re-authenticate or request access."],"exampleFix":"// before\nconnection = new HubConnectionBuilder().WithUrl(\"https://host/hubs/workflow-instance\").Build(); // no token\n// after\nconnection = new HubConnectionBuilder().WithUrl(\"https://host/hubs/workflow-instance\", opts =>\n    opts.AccessTokenProvider = () => Task.FromResult(authToken)).Build();","handlingStrategy":"type-guard","validationCode":"var permitted = user.HasClaim(\"permissions\", \"WorkflowInstances:Read\");\nif (!permitted) throw new UnauthorizedAccessException(\"User cannot observe workflow instances.\");","typeGuard":"var mayObserve = hubContextUser?.Identity?.IsAuthenticated == true && user.Claims.Any(c => c.Type == \"permission\" && c.Value.Contains(\"workflow-instance\"));","tryCatchPattern":"try { await connection.InvokeAsync(\"ObserveInstanceAsync\", instanceId); }\ncatch (HubException ex) when (ex.Message == \"Access denied.\") { /* redirect to login / request permissions */ }","preventionTips":["Gate hub subscriptions in the UI based on the user's permissions","Pass access tokens via AccessTokenProvider when building the hub connection","Test hub authorization with unauthenticated and under-privileged users"],"tags":["signalr","authorization","hub"],"backgroundTag":"permission-denied","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}