HMAC request authentication, in your app or in your mesh.
Sign and verify requests against named policies, with replay protection built in. Add it to an ASP.NET Core API as a NuGet package, or enforce it across an Istio mesh with no application changes at all.
Install
Three artifacts, versioned independently. Take the one that matches where you want the check to happen.
In your ASP.NET Core app
Register a policy and the built-in authentication handler verifies every incoming request — which check failed is reported, not just that one did.
builder.Services
.AddAuthentication()
.AddHmac(options =>
{
options.AddPolicy("MyPolicy", policy =>
{
policy.UsePublicKey(Guid.Parse("00000000-0000-0000-0000-000000000001"));
policy.UsePrivateKey("zvg29s2cQ4idOqbUJWETOw==");
policy.UseMemoryCache(maxAgeInSeconds: 300); // nonce / replay window
});
});On the calling side, attach the handler to an HttpClient and
outgoing requests are signed for you.
builder.Services
.AddHttpClient("api", client => client.BaseAddress = new Uri("https://api.example.com"))
.AddHmacHttpMessageHandler("MyPolicy");Or at the edge, with no application changes
HmacManager ships as an Envoy ext-authz HTTP server. An Istio ingress gateway or an ambient waypoint calls it before forwarding a request, so the check happens outside your service entirely.
Declare policies as Kubernetes resources and the operator reconciles them into the ConfigMap and Secret the verifier mounts. Keys come from Secrets, and changes hot-reload without a pod restart.
apiVersion: hmac-manager.io/v1alpha1
kind: HmacPolicy
metadata:
name: my-policy
namespace: hmac-system
spec:
publicKey: "00000000-0000-0000-0000-000000000001"
privateKeySecretRef:
name: my-hmac-secrets
key: my-policy-privateKey
algorithms:
contentHash: SHA256 # SHA1 | SHA256 | SHA512
signingHash: HMACSHA256 # HMACSHA1 | HMACSHA256 | HMACSHA512
nonce:
maxAgeInSeconds: 300Signing from a browser or Node
The TypeScript client builds the same signing content as the .NET library, so a request it signs verifies against an HmacManager-protected API.
import { HmacManagerFactory, HashAlgorithm } from "hmac-manager";
const factory = new HmacManagerFactory([{
name: "MyPolicy",
publicKey: "00000000-0000-0000-0000-000000000001",
privateKey: "zvg29s2cQ4idOqbUJWETOw==",
contentHashAlgorithm: HashAlgorithm.SHA256,
signatureHashAlgorithm: HashAlgorithm.SHA256,
schemes: []
}]);
const request = new Request("https://api.example.com/orders");
await factory.create("MyPolicy")!.sign(request); // adds the Hmac headers
const response = await fetch(request);What you get
Each of these has a page in the documentation.
A policy is a key pair, a hash algorithm choice, a replay window and a set of schemes. Register several and verify each request against the one it names, so different callers can hold different keys.
A named set of headers whose values are folded into the signature — so a request cannot be replayed against a different account or tenant. Those headers map to claims automatically.
Every signature carries a nonce and a timestamp, cached for the lifetime of the window so a captured request cannot be sent twice. In-process or Redis-backed.
Policies can be a static singleton, reloaded from configuration as it changes, or resolved per request from a database — without giving up the built-in authentication handler.
Structured ILogger output with stable event ids: every
rejection says which check failed. No message can carry a
private key, and a test asserts it over the whole sign/verify path.
An HmacPolicy CRD and an operator that reconciles it, so
the mesh's policies live in Git beside everything else and keys stay
in Kubernetes Secrets.