Show HN: Free and Anonymous Age Verification Demo
by jwally | 2025-09-19 23:50:51+00:00
========================

What is this?

========================

A short demo showing how a merchant can outsource age verification to a trusted institution (bank/KYC provider) with minimal cost and minimal data leakage. The user never hands the merchant their identity — only signed attestations like over_18: true. It’s a pragmatic build: WebAuthn proves token ownership; ECDSA signatures from the bank prove attestation integrity.

========================

Why Does It Matter?

========================

Age verification is usually either privacy-destroying (share DOB/SSN) or expensive. Banks already do KYC for many customers. If they can safely issue attestations that protect privacy and are cryptographically verifiable, merchants get compliance without storing PII and users keep their data. Age Verification becomes less useful in de facto outlawing free speech.

========================

How does it work (user-flow)?

========================

1) Go to https://app.hornpub.click

2) Click Generate PassKey

3) Copy the PassKey (there's a button for you)

4) Click on The Iron Bank link

5) Click on "Age Verification"

6) Make up some credentials to log in with - (un: xxx, pw: xxx, 2fa: 111111)

7) Paste your token into the TextArea

8) Copy the bank's token

9) Go back to HornPub (click button or whatever)

10) Paste the token into the "Complete Verification" text-area

11) Click the "Complete Verification" button

12) Complete the PassKey flow, and access the site

========================

How does it work (merchant-flow)?

========================

1) The user creates a PassKey, and you extract the key's ID and PublicKey

2) Base64 encode the {id, PublicKey} and let the user copy it

3) The user goes to their Bank or KYC provider.

4) The user provides the Bank's token

5) Have the user perform a WebAuthn Authentication by extracting the credential's Id and Public key from the bank's data. Use the entire bank's payload as your WebAuthn Challenge

6) Send the Authentication payload to the server

7) Verify the WebAuthn Authentication - this proves the user didn't create a token and sell it

8) Verify the bank's signature. You control what banks you accept. This proves it wasn't tampered with

9) Verify a salted-hash of the user's IP address.

========================

How does it work (bank-flow)?

========================

1) Publish your ECDSA signing key at /.well-known/keys

2) Accept a string payload from your user that you will sign. You do not have to verify or look at it.

3) Look the user up and add age attestations of `over_18:boolean` and `over_21:boolean`

4) Create a nonce, and provide it and sha256(nonce:ip) in your signature target

5) Sign base64Encode({over_18, over_21, nonce, ipHash, userPayload })

6) Provide the signature and signature-target to the user for them to copy back to the merchant site:

========================

Security Notes & Trade Offs

========================

WebAuthn proves the holder of the PassKey created the credential — prevents a user from inventing a credential and selling it.

ECDSA signatures from the bank prove the attestation came from an accepted bank and wasn’t tampered with. Merchant picks which banks to trust.

Nonce + IP hash minimize replay/copy-paste attacks. They do not make the system 100% subpoena-proof — logs and warrants can still correlate events.

Privacy: merchant receives boolean attestations only, not DOB or identity. Banks don't know where the user will use the token.