@goplausible/liquid-auth-cloud
Liquid Auth on Cloudflare Workers — passwordless FIDO2/WebAuthn authentication with Algorand wallet binding, deployed to the edge with zero idle cost.
Built by GoPlausible, inspired by the Algorand Foundation's Liquid Auth reference server. A ground-up reimplementation that replaces NestJS + MongoDB + Redis + Docker with Cloudflare Workers + KV + Durable Objects — lighter, more scalable, cloud-native, and serverless.
Live deployment: https://liquidauth.goplausible.xyz
| Project | Package / App | Description |
|---|---|---|
| Liquid Auth Client (TypeScript) | @goplausible/liquid-client |
Browser client — native WebSocket, FIDO2 + WebRTC signaling |
| Liquid Auth Android | Android wallet app | FIDO2 + WebRTC with native OkHttp WebSocket |
| Rocca Wallet | React Native / Expo app | Cross-platform wallet — FIDO2 passkeys + WebRTC |
| WebRTC Payment SDK | @goplausible/webrtc-payment-sdk |
Used in — micropayment-gated WebRTC streams on Algorand |
| Concern | Original (NestJS) | This (Cloudflare Workers) |
|---|---|---|
| Runtime | Node.js + Express + Docker | Cloudflare Worker (V8 isolate) |
| User storage | MongoDB | Workers KV |
| Sessions | MongoDB + connect-mongo | Durable Object with HMAC-SHA256 signing |
| WebSocket | Redis + Socket.io | Durable Objects + native WebSocket |
| Crypto | Node.js crypto | tweetnacl + Node crypto (nodejs_compat) |
| Static assets | Express static | Worker assets + R2 bucket |
| Deployment | Docker / VM | wrangler deploy |
| Scaling | Manual / load balancer | Automatic edge scaling, 300+ PoPs |
| Idle cost | Server always running | Zero (pay per request) |
Key design choices:
- No Socket.io — native WebSocket with
{ event, data }JSON protocol, designed for Durable Object Hibernation API - Room routing via URL —
/ws?requestId=xxxputs both peers in the same DO instance without room-join handshakes - Dual room broadcast — auth events go to both the wallet room and the requestId room so the offer peer always receives them
- Discoverable assertions — supports passwordless sign-in without knowing the credential ID (synced passkeys across devices)
- Proper SHA-512/256 — Algorand address checksums use real SHA-512/256 via Node crypto, not truncated SHA-512
Wallet (FIDO2) Cloudflare Edge Browser/Dapp
============== ========================= ==================
1. Scan QR code ------> Worker receives request
KV stores challenge
Returns attestation options
2. Sign with passkey -----> Worker verifies attestation
KV stores user + credential
DO broadcasts auth event
|
v
WalletRoom Durable Object -------> WebSocket push
(per-requestId signaling room) "link" event
3. WebRTC signaling <-----> DO relays offer/answer <-------> WebRTC signaling
(offer/answer/ICE) candidates between peers (offer/answer/ICE)
4. DataChannel opens <======= encrypted P2P ==================> DataChannel opens
- Node.js >= 18, npm
- Wrangler CLI (
npm install -g wrangler) - A Cloudflare account
# From the monorepo root
npm install
npm run dev:cloud # Starts at http://localhost:8787npm run deploy:cloudUpdate wrangler.toml:
[vars]
RP_NAME = "Your App Name"
HOSTNAME = "auth.yourdomain.com"
ORIGIN = "https://auth.yourdomain.com"
ANDROID_ORIGINS = "android:apk-key-hash:<YOUR_APK_KEY_HASH>"
SESSION_TTL = "300"Set secrets:
wrangler secret put SESSION_SECRET| Route | Description |
|---|---|
GET / |
Demo page — offer side, QR codes, messaging |
GET /wallet?requestId=xxx |
Web wallet — mobile-optimized answer side |
GET /.well-known/assetlinks.json |
Android FIDO2 Digital Asset Links |
GET /download/:key |
Serve files from R2 (APK, assets) |
GET /favicon.ico |
Served from Worker static assets |
| Route | Description |
|---|---|
POST /attestation/request |
Get WebAuthn registration options |
POST /attestation/response |
Submit signed attestation with Liquid extension |
| Route | Description |
|---|---|
POST /assertion/request |
Discoverable assertion (no credId — synced passkeys) |
POST /assertion/request/:credId |
Assertion for a specific credential |
POST /assertion/response |
Submit signed assertion |
| Route | Description |
|---|---|
GET /auth/session |
Current session and user info |
GET /auth/user |
Authenticated user (401 if not logged in) |
DELETE /auth/keys/:credId |
Remove a credential |
GET /auth/logout |
Destroy session, redirect to / |
const ws = new WebSocket(`wss://auth.yourdomain.com/ws?requestId=${requestId}`);
// All messages use { event, data } JSON envelope
ws.send(JSON.stringify({ event: "link", data: { requestId } }));
ws.onmessage = (e) => {
const { event, data } = JSON.parse(e.data);
// event: link | offer-description | offer-candidate | answer-description | answer-candidate
};src/
index.ts Entry point, routing, CORS, session cookies
types.ts Env bindings, StoredUser, StoredSession, events
encoding.ts Base64URL, Base32, SHA-512/256, Algorand address codec
kv.ts KV storage layer (users, credentials)
session.ts Session utilities (HMAC-signed cookies)
session-store.ts SessionStore Durable Object
fido2.ts FIDO2 attestation/assertion, Liquid extension verification
wallet-room.ts WalletRoom Durable Object (WebSocket signaling)
routes.ts HTTP route handlers
demo.ts Demo page HTML
wallet.ts Web wallet page HTML
public/
favicon.ico Static asset
- No private keys on server — all Algorand signing happens on the client device
- FIDO2 user verification required — all operations require biometric/PIN
- Proper SHA-512/256 — Algorand address checksums use the correct algorithm (not truncated SHA-512)
- Rekeyed account support — checks on-chain
auth-addron both mainnet and testnet - Cross-platform origins — FIDO2 origin validation includes both web and Android origins
- HMAC-SHA256 sessions — stored in Durable Object, signed cookies, automatic TTL expiry
- HttpOnly + Secure + SameSite=None cookies
- No workers.dev URL — custom domain only,
workers_devandpreview_urlsdisabled - Secrets encrypted at rest —
SESSION_SECRETset viawrangler secret put
- Architecture — component design, data flow, migration mapping
- Sequence Diagrams — registration, authentication, WebSocket, session flows
- Vision — why edge deployment, principles, design decisions
- Contributing — development setup and guidelines
MIT — see LICENSE.