An example HashBack session

Let's follow one request from the client's first thought to the server's final nod. Hashbert the Hedgehog will annotate the important bits, because authentication is easier to understand when someone points at the spiky details.

HashBack circular badge icon
Step 01

The client knows where it is calling

The client-side builder starts with the server host and a narrow HTTPS URL where the server can retrieve the verification hash.

var hashbackBuilder = new billpg.HashBackCore.AuthHeaderBuilder();

var auth = hashbackBuilder
    .WithHost("server.example")
    .WithVerify("https://client.example/api/hashback?id=502542886")
    .Build();

Hashbert says: "The Host tells me who you intend to visit. The Verify URL tells me where I may call you back. Keep that URL narrow and HTTPS-secure, please. I have standards, and also spines."

Step 02

The client prepares two useful results

When Build() runs, it creates the authorization value and calculates the matching verification hash. The client sends the header to the server and publishes the hash at its verification URL.

Console.WriteLine("Auth Header: " + auth.AuthHeader);
Console.WriteLine("Verification Hash: " + auth.VerificationHash);

// Send auth.AuthHeader in the HTTP Authorization header.
// Return auth.VerificationHash from the Verify URL.

Hashbert says: "One result travels to the server. One result waits on the client's website. They are a matched pair, like me and a carefully labelled laboratory notebook."

Step 03

The request carries a claim, not a password

The authorization value contains a Base64-encoded JSON claim. In the README's example, the claim identifies the protocol version, destination host, current time, one-use random value, and callback URL.

Authorization: HashBack
 eyJWZXJzaW9uIjoiQklMTFBHX0RSQUZUXzQuMiIsIkhvc3QiOiJzZXJ2ZXIuZXhhbXBsZSIsIk5v
 dyI6NTI5Mjk3MjAwLCJVbnVzIjoiUnBndDRGYzVuTURxMTRMT3BzL2hZUT09IiwiVmVyaWZ5Ijoi
 aHR0cHM6Ly9jbGllbnQuZXhhbXBsZS9hcGkvaGFzaGJhY2s/aWQ9NTAyNTQyODg2In0=

Hashbert says: "This is not a secret being shared forever. It is a fresh, time-aware claim with a one-use value. The random Unus helps stop anyone from trying the same claim twice. Very sensible. Very prickly."

Step 04

The server checks the callback

The receiving service configures a parser with the host it expects and a small clock tolerance, then parses the incoming header.

var hashbackParser = new billpg.HashBackCore.AuthHeaderParser()
    .WithRequiredHost("server.example")
    .WithTimeTolerance(10);

var parseResult = hashbackParser.Parse("eyHeaderTokenGoesHere==");
Console.WriteLine("Verify URL: " + parseResult.VerifyUrl);
Console.WriteLine("Expected Hash: " + parseResult.ExpectedHash);

Hashbert says: "The server checks that the request is meant for it, that the timestamp is fresh, and that the verification URL belongs to a known client. If any of those wobble, the request stays outside the gate."

Step 05

The server compares the two hashes

The server fetches the verification URL over HTTPS and compares the returned text with the expected hash calculated from the claim. A match reassures the server that the caller controls the registered verification URL.

1. ParseRead and validate the claim.
2. FetchRetrieve the client's published hash.
3. CompareAccept only an exact match.

Hashbert says: "That is the callback. The server has not received a long-lived password; it has received proof that the client can publish the right answer from the right HTTPS location. I approve this exchange with a thoughtful nod."