Two years later, the auth question gets answered
Two years later, the auth question gets answered
The first attempt at this site left one design question open and labeled it, literally, "undecided": password + JWT, WebAuthn, or public-key crypto. I shipped a bit of all three and settled none of it.
pynezz-hdauth is the answer, two years late: public-key crypto, specifically BIP32-style
hierarchical deterministic key derivation over secp256k1 - the same derivation scheme wallets use,
repurposed for login instead of holding funds.
Why HD derivation for auth
The interesting property isn't the signature scheme, it's the tree:
- A 256-bit seed lives only on whatever device I trust to authenticate from - never on the server, never in a browser.
m/purpose'/service'is derived with hardened derivation, which requires the private key. That's the boundary a leaked public key can never cross: a compromised service-level xpub can't produce siblings for another service, let alone the master seed.- Below that boundary,
roleandsessionuse normal derivation, which works from the public key alone. The server stores one xpub per user and independently re-derives the exact public key it expects for a given role and session index - it never has to trust a client-presented key, only check it against what it computes itself.
Proof of possession comes from a signature over a server-issued, single-use, 60-second nonce. Replay a captured signature and the nonce is already spent; nothing to brute-force, because there's no password in this system at all.
The bridge problem nobody warns you about
The part that took longer than the cryptography: the private key lives wherever the CLI runs, and
the thing that needs a session is a browser, which are usually not the same device. hdauth's
/auth/verify mints a short-lived, single-use proof token alongside its success response - the
CLI prints it, you paste it into the site's /login form within two minutes, and the site trades
it for its own session cookie via /auth/redeem. The browser never sees a nonce, a signature, or
a private key; it only ever sees a token that's already been spent by the time anyone else could
reuse it.
There's a /vault page on this site now gated behind exactly that flow, mostly to prove the whole
chain actually works end to end rather than looking correct on paper.
What's still open
Session proofs currently ride on a server-generated secret that's regenerated every process restart, which invalidates all sessions on deploy - fine for a portfolio site, not fine for anything that needs sessions to survive a restart. That's the next "undecided" section, and I'm hoping it doesn't take two years to close this time.