Skip to content
Events

Events

HmacEvents exposes three points in the HmacAuthenticationHandler flow.

builder.Services
    .AddAuthentication()
    .AddHmac(options =>
    {
        options.AddPolicy("MyPolicy", policy => { /* ... */ });

        options.Events = new HmacEvents
        {
            OnValidateKeysAsync = (context, keys) => { /* ... */ },
            OnAuthenticationSuccessAsync = (context, hmacResult) => { /* ... */ },
            OnAuthenticationFailureAsync = (context, hmacResult) => { /* ... */ }
        };
    });
EventRunsReturns
OnValidateKeysAsyncafter a signature is parsed, before any verificationTask<bool>
OnAuthenticationSuccessAsyncafter a signature verifiesTask<Claim[]>
OnAuthenticationFailureAsyncafter a signature fails to verifyTask<Exception>

The defaults pass through: true, an empty Claim[], and an HmacAuthenticationException. Setting none of them changes nothing.

OnValidateKeysAsync

Runs before verification, with the keys the request named. Returning false rejects the request without attempting to verify it — which is the cheap place to reject a revoked or unknown public key, since it skips the hashing entirely.

OnValidateKeysAsync = async (context, keys) =>
{
    var store = context.RequestServices.GetRequiredService<IKeyStore>();
    return await store.IsActiveAsync(keys.PublicKey);
}

A rejection here is logged at Warning as event 1301.

OnAuthenticationSuccessAsync

Runs after a successful verification. The claims it returns are added to the principal, on top of the ones a scheme already contributes — the place to attach roles or tenant context that the signature does not carry.

OnAuthenticationSuccessAsync = async (context, hmacResult) =>
{
    var accounts = context.RequestServices.GetRequiredService<IAccountService>();
    var account = await accounts.FindAsync(hmacResult.Hmac?.Policy);
    return [new Claim(ClaimTypes.Role, account.Role)];
}

OnAuthenticationFailureAsync

Runs after a failed verification, and returns the exception that becomes the authentication failure. Use it to record the attempt, or to return something more specific than the default.

OnAuthenticationFailureAsync = (context, hmacResult) =>
{
    // hmacResult carries the Hmac that was computed, where one could be
    Task.FromResult<Exception>(new HmacAuthenticationException("rejected"));
}

Do not put the private key, or anything derived from it, into a claim or an exception message. The library guarantees it never logs one; an exception message you write is outside that guarantee and tends to end up in logs anyway.

With configuration binding

The IConfigurationSection overload has no options delegate, so it takes events as a second argument:

builder.Services
    .AddAuthentication()
    .AddHmac(configurationSection, new HmacEvents
    {
        OnValidateKeysAsync = (context, keys) => { /* ... */ }
    });