elsa-workflows/elsa-core · error · InvalidOperationException

The broker authorization request is invalid.

Error message

The broker authorization request is invalid.

What it means

A validation guard in ValidateAuthorizationRequest (called from InitiateExternalAsync and InitiateLocalAsync): the authorization request does not meet the broker's invariants — for example a missing or malformed redirect/pkce parameter, or a flow-state element that failed construction — so the request is rejected as invalid before any outbound authorization call.

Solutions

  1. Re-initiate the broker authorization flow so a fresh, correctly-formed request is generated.
  2. Check that all required request parameters (connection id, redirect URI, state) were supplied by the caller.
  3. Verify the connection referenced by the request exists and is enabled before initiating.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at src/modules/Elsa.ExternalAuthentication/Services/ExternalAuthenticationBroker.cs:539 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication/Services/ExternalAuthenticationBroker.cs:539

        var connection = await connectionRegistry.FindByIdAsync(transaction.TenantId, transaction.ConnectionId!, cancellationToken);
        return connection is null || connection.IsShadowed || !connection.Connection.IsEnabled || connection.Connection.ArchivedAt is not null || !string.Equals(connection.Connection.MaterialRevision, transaction.ConnectionMaterialRevision, StringComparison.Ordinal)
            ? await CallbackOutcomeAsync(BrokerCallbackResult.Fail(BrokerErrorFactory.Create(BrokerErrorCategory.FlowChanged)), "logout", "callback", transaction.TenantId, connectionKey, cancellationToken)
            : await CallbackOutcomeAsync(BrokerCallbackResult.Redirect(transaction.CallbackUri), "logout", "callback", transaction.TenantId, connectionKey, cancellationToken);
    }

    public async ValueTask<BrokerLogoutResult> ContinueLogoutAsync(string handle, CancellationToken cancellationToken = default)
    {
        var taken = await stateStore.TryTakeAsync<BrokerTransaction>("UpstreamLogoutContinue", Hash(handle), cancellationToken);
        if (taken is not TakeResult<BrokerTransaction>.Taken { Value: var transaction } || !Uri.TryCreate(transaction.ReturnPath, UriKind.Absolute, out var navigation))
            return await LogoutOutcomeAsync(BrokerLogoutResult.Fail(BrokerErrorFactory.Create(BrokerErrorCategory.InvalidRequest)), "continue", cancellationToken);
        return await LogoutOutcomeAsync(BrokerLogoutResult.Navigate(navigation), "continue", cancellationToken);
    }

    private AuthenticationClient EnsureClient(string clientId) => options.Value.Clients.FirstOrDefault(x => x.IsEnabled && string.Equals(x.ClientId, clientId, StringComparison.Ordinal)) ?? throw new InvalidOperationException("Authentication client is unavailable.");
    private static void ValidateAuthorizationRequest(BrokerAuthorizationRequest request, AuthenticationClient client)
    {
        if (!string.Equals(request.ResponseType, "code", StringComparison.Ordinal) || !string.Equals(request.CodeChallengeMethod, "S256", StringComparison.Ordinal) || !client.CallbackUris.Contains(request.RedirectUri) || !ClientReturnPathValidator.TryValidateForClient(request.ReturnPath, client.AllowedReturnPathPrefixes, out _))
            throw new InvalidOperationException("The broker authorization request is invalid.");
    }
    private static bool VerifyPkce(string challenge, string? verifier) => !string.IsNullOrWhiteSpace(verifier) && string.Equals(challenge, Base64Url(SHA256.HashData(Encoding.ASCII.GetBytes(verifier))), StringComparison.Ordinal);
    private static string CreateOpaqueValue() => Base64Url(RandomNumberGenerator.GetBytes(32));

    private string Hash(string value) => handleHasher.Hash(value);
    private static string Base64Url(byte[] value) => Convert.ToBase64String(value).TrimEnd('=').Replace('+', '-').Replace('/', '_');
    private static Uri AppendCallbackParameters(Uri uri, string code, string? clientState)
    {
        var result = AppendQuery(uri, "code", code);
        return string.IsNullOrWhiteSpace(clientState) ? result : AppendQuery(result, "state", clientState);
    }
    private static Uri AppendQuery(Uri uri, string key, string value) { var separator = string.IsNullOrEmpty(uri.Query) ? "?" : "&"; return new(uri + separator + Uri.EscapeDataString(key) + "=" + Uri.EscapeDataString(value), uri.IsAbsoluteUri ? UriKind.Absolute : UriKind.Relative); }
    private async ValueTask<BrokerCallbackResult> FailTrustedCallbackAsync(BrokerTransaction transaction, BrokerErrorCategory category, CancellationToken cancellationToken)
    {
        var error = BrokerErrorFactory.Create(category);
        return await CallbackOutcomeAsync(BrokerCallbackResult.Fail(error, AppendQuery(AppendQuery(transaction.CallbackUri, "error", error.Error), "correlation_id", error.CorrelationId)), "external", "callback", transaction.TenantId, transaction.ConnectionId, cancellationToken);
    }

View on GitHub (pinned to fe9217bdfa)