elsa-workflows/elsa-core · error · InvalidOperationException

External Authentication handle-hashing settings are…

Error message

External Authentication handle-hashing settings are required.

What it means

HmacExternalAuthenticationHandleHasher derives its HMAC-SHA-256 key from Elsa.ExternalAuthenticationOptions.HandleHashing at construction. When the bound HandleHashing section is null — meaning the options object was built without going through options binding/validation — GetKey refuses to run and throws this InvalidOperationException. The library throws instead of silently falling back so handle hashing is never keyed by an unexpected default.

Solutions

  1. Register the hasher through the module's standard service-registration extension (AddExternalAuthenticationCore / feature plumbing) so ExternalAuthenticationOptions is bound and validated by options infrastructure.
  2. Bind the configuration explicitly: services.AddOptions<ExternalAuthenticationOptions>().BindConfiguration("ExternalAuthentication") (or your section name) before the hasher resolves.
  3. In tests, populate the options: Options.Create(new ExternalAuthenticationOptions { HandleHashing = new ExternalAuthenticationHandleHashingOptions { SharedKeyBase64 = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32)) } }) — or use the parameterless constructor meant for tests/dev.
  4. Verify the configuration section name matches ("ExternalAuthentication:HandleHashing") and the values are loaded in the environment being run.

Example fix

// before
services.AddSingleton<IExternalAuthenticationHandleHasher, HmacExternalAuthenticationHandleHasher>();

// after
services.AddOptions<ExternalAuthenticationOptions>()
    .BindConfiguration("ExternalAuthentication");
services.AddSingleton<IExternalAuthenticationHandleHasher, HmacExternalAuthenticationHandleHasher>();
Defensive patterns

Strategy: validation

Validate before calling

// before constructing the hasher
var opts = serviceProvider.GetRequiredService<IOptions<ExternalAuthenticationOptions>>().Value;
if (opts.HandleHashing is null)
    throw new InvalidOperationException("Bind ExternalAuthentication:HandleHashing before registering the handle hasher.");

Type guard

static bool HasHandleHashing(ExternalAuthenticationOptions options) => options?.HandleHashing is not null;

Try / catch

try
{
    var hasher = new HmacExternalAuthenticationHandleHasher(optionsAccessor);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("handle-hashing settings are required"))
{
    logger.LogError(ex, "ExternalAuthentication:HandleHashing options section is not bound.");
    throw; // fail fast — hashing with defaults would break handle lookups across restarts
}

Prevention

When it happens

Trigger: Constructing HmacExternalAuthenticationHandleHasher with IOptions<ExternalAuthenticationOptions> whose Value.HandleHashing is null, i.e. registering the hasher manually (e.g. services.AddSingleton<IExternalAuthenticationHandleHasher, HmacExternalAuthenticationHandleHasher>()) without binding the ExternalAuthentication options section, or calling the IOptions constructor in a test with an empty ExternalAuthenticationOptions instance.

Common situations: Hand-wiring the hasher in DI without services.AddOptions<ExternalAuthenticationOptions>().BindConfiguration(...); unit tests newing up IOptionsMock with Options.Create(new ExternalAuthenticationOptions()); a missing or misnamed configuration section so the HandleHashing property is never populated; upgrading Elsa versions where options wiring changed and custom DI registration no longer re-binds the section.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication/Services/HmacExternalAuthenticationHandleHasher.cs:44

    /// </summary>
    public HmacExternalAuthenticationHandleHasher(IOptions<ExternalAuthenticationOptions> options)
        : this(GetKey(options.Value.HandleHashing))
    {
    }

    private HmacExternalAuthenticationHandleHasher(byte[] key)
    {
        _key = key;
    }

    public string Hash(string value) => Convert.ToHexString(HMACSHA256.HashData(_key, Encoding.UTF8.GetBytes(value)));

    public void Dispose() => CryptographicOperations.ZeroMemory(_key);

    private static byte[] GetKey(ExternalAuthenticationHandleHashingOptions? options)
    {
        if (options is null)
            throw new InvalidOperationException("External Authentication handle-hashing settings are required.");

        if (string.IsNullOrWhiteSpace(options.SharedKeyBase64))
            return RandomNumberGenerator.GetBytes(32);

        try
        {
            var key = Convert.FromBase64String(options.SharedKeyBase64);
            if (key.Length >= 32)
                return key;

            CryptographicOperations.ZeroMemory(key);
        }
        catch (FormatException)
        {
            // The options validator reports the actionable configuration error at startup.
        }

        throw new InvalidOperationException("The External Authentication shared handle-hashing key must be valid base64 containing at least 32 bytes.");

View on GitHub (pinned to fe9217bdfa)