pynezz.dev
login

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, role and session use 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.

Menu
Observatory A+ 120