Integrate hmac authentication seamlessly into your ASP.NET Core applications, fortifying security measures and ensuring robust authentication protocols.
$ dotnet add package HmacManagerHmacManager objectHmacManagerFactory objectHmacEvents objectIHmacPolicyCollectionHmacA short and sweet overview of how to register HmacManager to help you get up and running. There are two methods of dependency injection registration. You should choose the one appropriate for your situation and how much flexibility you might require.
HmacManager object through an IHmacManagerFactory service where you will be required to handle signatures and verification manually. An implementation of IHmacManagerFactory is registered with the DI container automatically. This is how you will instantiate HmacManager objects.Use the IServiceCollection extension method AddHmacManager to add all of the necessary components for HmacManager to the DI container.
builder.Services.AddHmacManager(options => ...);
Configure one or more policies with the options builder.
options.AddPolicy("SomePolicy", policy =>
{
policy.UsePublicKey(...);
policy.UsePrivateKey(...);
policy.UseMemoryCache(...);
});
Access an instance of a HmacManager responsible for a specified policy from IHmacManagerFactory.
var hmacManager = hmacManagerFactory.Create("SomePolicy")
[!NOTE] An implementation of
IHmacManagerFactoryis automatically registered with the DI container so it can be accessed anywhere services can be injected.
A policy can be extended with schemes. These schemes represent the required headers that must be present in a request. These become a part of the signing content.
builder.Services.AddHmacManager(options =>
{
options.AddPolicy("SomePolicy", policy =>
{
policy.UsePublicKey(...);
policy.AddScheme("SomeScheme", scheme =>
{
scheme.AddHeader("X-UserId");
scheme.AddHeader("X-Email");
});
});
});
[!IMPORTANT] All headers that are defined on a scheme must be added to the
HttpRequestMessageprior to callingSignAsyncon anHmacManagerinstance.
The AddHmacManager extension method can be bypassed in favor of the IAuthenticationBuilder extension method AddHmac.
builder.Services
.AddAuthentication()
.AddHmac(options => options.AddPolicy("SomePolicy", policy => ...));
HmacAuthenticationHandler handles parsing incoming requests and authenticating the correct scheme.
Use HmacAuthenticateAttribute to specify exact policies and schemes required to authenticate a given endpoint.
[HmacAuthenticate(Policy = "HmacPolicy", Scheme = "HmacScheme")]
public class HomeController : Controller
Use HmacAuthenticateAttribute as an IAuthorizationRequirement to an authorization policy and register the HmacAuthorizationHandler to handle the requirement automatically.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireHmac", policy =>
policy.AddRequirements(new HmacAuthenticateAttribute
{
Policy = "HmacPolicy",
Scheme = "HmacScheme"
}));
});
Use the AuthorizationPolicyBuilder extensions RequireHmacPolicy and RequireHmacScheme to add hmac policy and scheme requirements to an authorization policy.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireHmac", policy =>
{
policy.RequireHmacPolicy("HmacPolicy");
policy.RequireHmacScheme("HmacScheme");
});
});
Use the AuthorizationPolicyBuilder extension RequireHmacAuthentication to add hmac policy and scheme requirements to an authorization policy.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireHmac", policy =>
{
policy.RequireHmacAuthentication("HmacPolicy", "HmacScheme");
});
});
[!NOTE] Any scheme headers are mapped to their specified claim types. If no claim type is specified, the name of the header is used instead.
IConfigurationSectionBoth AddHmacManager and AddHmac have an overload which accepts an IConfigurationSection that corresponds to the json schema below. An example can be found here.
[
{
"Name": "Some_Policy",
"Keys": {
"PublicKey": "37e3e675-370a-4ba9-af74-68f99b539f03",
"PrivateKey": "zvg29s2cQ4idOqbUJWETOw=="
},
"Algorithms": {
"ContentHashAlgorithm": "SHA256",
"SigningHashAlgorithm": "HMACSHA256"
},
"Nonce": {
"CacheType": "Memory",
"MaxAgeInSeconds": 100
},
"HeaderSchemes": [
{
"Name": "Some_Scheme",
"Headers": [
{
"Name": "Some_Header_1",
"ClaimType": "Header_1_ClaimType"
}
]
}
]
}
]
The following properties are restricted to the following values.
| Property | Values | Additional Information |
|---|---|---|
PublicKey | Guid String | Validation Details |
PrivateKey | Base64 Encoded String | Validation Details |
ContentHashAlgorithm | SHA1, SHA256, SHA512 | Enum |
SigningHashAlgorithm | HMACSHA1, HMACSHA256, HMACSHA512 | Enum |
CacheType | Memory, Distributed | Enum |
HttpClient with HmacHttpMessageHandlerThe AddHmacHttpMessageHandler extension method registers an instance of HmacDelegatingHandler to the specified HttpClient with the specified policy and the optional scheme. This handler will automatically sign outgoing requests for that client. If the request cannot be signed, then an HmacSigningException exception is thrown.
builder.Services.AddHttpClient("Hmac", client => ...)
.AddHmacHttpMessageHandler("MyPolicy", "MyScheme");
[!NOTE]
If a scheme is specified, then all headers in that scheme must be added to the request prior to callingSendorSendAsyncon theHttpClient. By default the corresponding header values will become part of the signing content used to create the hmac.
This is where you can find a comprehensive guide on all of the functionality available to your disposal. This is currently a work in progress.
HmacEvents objectOne or more event handlers can be defined within the AuthenticationBuilder extension method AddHmac.
options.Events = new HmacEvents
{
OnValidateKeys = (context, keys) => {...},
OnAuthenticationSuccess = (context, hmacResult) => {...},
OnAuthenticationFailure = (context, hmacResult) => {...}
};
If using the IConfigurationSection overload of AddHmac then there is
an optional second parameter for HmacEvents.
builder.Services.AddAuthentication()
.AddHmac(configurationSection, new HmacEvents
{
OnValidateKeys = (context, keys) => {...},
OnAuthenticationSuccess = (context, hmacResult) => {...},
OnAuthenticationFailure = (context, hmacResult) => {...}
});
Events are executed through user defined delegates at different points within the HmacAuthenticationHandler flow.
| Event | Path | Return |
|---|---|---|
| OnValidateKeys | Executes after a signature has been parsed from an incoming request but before any attempts at verification | bool |
| OnAuthenticationSuccess | Executes upon a successful signature verification | Claim[] |
| OnAuthenticationFailure | Executes upon a failed signature verification | Exception |
[!NOTE] The default values for
HmacEventsreturn pass through values, i.e. OnValidateKeys returnstrue, OnAuthenticationSuccess returns an emptyClaim[]and OnAuthenticationFailure returns aHmacAuthenticationException.
IHmacPolicyCollectionAn implementation of IHmacPolicyCollection can be requested through the DI container and manipulated at runtime.
[!NOTE] An implementation of
IHmacPolicyCollectionis automatically registered as a singleton when using the extension methodsAddHmacManagerorAddHmac.
A policy can be added by constructing a new HmacPolicy.
Policies.Add(new HmacPolicy {...});
A policy can be removed by specifying the name of the policy to remove.
Policies.Remove("Some Policy");
Hmac[!CAUTION] If this method is used, then the requirement of determining unique signing content per request falls on the user. Components like the date requested or nonce are NOT automatically added to the content for hashing and should be added by the implementation.
The signing content for an Hmac can be configured per policy. This allows user defined structures to be used as the input to the signature hash function.
policy.UseSigningContentBuilder(context =>
{
var method = context.Request.Method;
var suffix = $"{context.DateRequested}:{context.Nonce}";
return $"{method}:{suffix}";
});