Pennyauth is an open-source captcha alternative that uses micropayments.

Try the Demo

Use foobar / password.



This is somewhat of an experiment. Lots of people find captchas painful and obnoxious. Maybe a penny is worthy tradeoff?

Do I have to enter my credit-card every time?

Nope, just once, during your first payment. Pennyauth remembers who you are, so all future payments are one-click.

Doesn't this disenfranchise the underpriveleged, and further stratify society?

Yikes. Pennyauth can optionally fallback to a regular captcha. Crosswalks ain't so bad. NBD.

How does it work?

Pennyauth uses QUID to process micropayments. When a captcha is required, the browser makes a QUID payment request and validates the receipt on the Pennyauth server. After validation, it creates a unique signed login token that can be verified by your application.

How do I use it?

Step 1. Get your API keys

Use the command below to get Pennyauth API keys for your website. Pennyauth only works with HTTPS origins. $ echo | bash <(curl -s { "apiKey":"k-0e062e46670a19ae26e0a56f366c0685", "apiSecret":"s-f8ab0a1650bbedb657157735f85139a0", "originKey":"fVrkl1A5OVIaHM=" }

Step 2. Add it to your website

Load the Pennyauth script into your webpage and connect your login handler to it. The Pennyauth widget will be installed into the DOM element specified in el. <script src=""/> <script> const client = new pennyauth.Client({ el: document.getElementByID('pennywall-div'), apiKey: 'YOUR_API_KEY', originKey: 'YOUR_ORGIN_KEY', onTokenReady: () => { enableLoginButton() } }); function onLoginClicked() { // Get token and send along with your login API call client.ask((token) => { processLogin(username, password, token); }); } </script>

Step 3. Validate the captcha on the server

Use the SHA256 hash of your API secret to construct a HMAC of the token payload (concatenated into a comma-separated string.) If the HMAC matches the signature, then the token is valid.

To protect from forged tokens, make sure to also validate that the token's apiKey and origin fields match yours, and isn't more than a minute old. Use the following NodeJS code as an example.

function loginHandler(req, res) { // Get token from request const token = req.params.token; // Create a SHA256 hash of the API secret const hashedSecret = crypto .createHash('SHA256') .update(API_SECRET) .digest('hex'); // Get HMAC of token payload const payload = [, token.unixTime, token.origin, token.apiKey].join(','); const sig = crypto .createHmac('SHA256', hashedSecret) .update(payload).digest('base64'); // Verify HMAC signature if (sig !== req.sig) { throw new Error("Invalid token") } // Make sure this token is yours if (token.apiKey !== 'MY_API_KEY' || token.origin !== '') { throw new Error("Invalid token scope") } // Make sure this token isn't more than a minute old if (token.unixTime < (Math.floor( / 1000) - 60)) { throw new Error("Token expired") } // All good: validate username, password and log user in. // ... }

Where do the pennies go?

We keep the pennies :-) It keeps this service free and operational.

But I want my money!

Lucky for you, Pennyauth is open-source. Fork it, host it, and watch the pennies roll in.

I hate you for doing this. How do I reach you?

Tweet me your hate at @11111110b. Even better, send pull requests to 0xfe@github.
Fork me on GitHub