Skip to content

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.

ASP.NET Core library
dotnet add package HmacManager
Kubernetes / Istio
helm repo add zills https://jzills.github.io/hmac-manager
helm install hmac-manager zills/hmac-manager
JavaScript / TypeScript client
npm install hmac-manager

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");

The .NET documentation →

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.

ClientIstio gatewayor ambient waypointYour serviceext-authz checkhmac-managerverifies the signature200 — forwarded upstream, unchanged403 — rejected before it reaches your service

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: 300

The Kubernetes documentation →

Signing 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);

The client documentation →

What you get

Each of these has a page in the documentation.

Named policies

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.

Schemes

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.

Replay protection

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.

Dynamic policies

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.

Diagnosable rejections

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.

Policies as resources

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.