Collaborative approval of JSON statements through threshold signatures with passkey-backed security
This system enables multiple parties to collaboratively approve JSON statements using digital signatures. A statement becomes valid only when it receives a threshold number of signatures (e.g., 2 out of 3 users must sign).
Key Security Feature: Combines passkey security (hardware-backed attestation) with efficient JWT signing for fast, collaborative workflows.
- Creator drafts a JSON statement (e.g., investment terms, contract)
- Multiple users review and sign the statement
- Threshold reached (e.g., 2 signatures) → Statement becomes valid ✅
This system uses two types of keys for optimal security and efficiency:
| Key Type | Purpose | When Used |
|---|---|---|
| Passkey | Attests your JWT signing key | Once during setup |
| JWT Signing Key | Signs statements | Every time you approve a statement |
Why? Passkeys provide hardware-backed security, while JWT signing keys enable fast signing without repeated biometric prompts.
npm install
npm run dev # Starts on http://localhost:3000Visit the homepage and select your role:
- Creator - Draft JSON statements and check approvals
- Investor - Review and sign JSON statements
Before you can sign statements, you complete a one-time setup:
- Choose your role (Creator or Investor)
- Enter your name (e.g., "Jeff", "Alice", "Bob", "Carol")
- Register a passkey - Your device prompts for biometric/PIN
- System generates your JWT signing key - Automatic
- Passkey attests your signing key - Creates cryptographic proof
Result: You're ready to sign! All future approvals happen instantly without passkey prompts.
- Your passkey created a cryptographic proof that your JWT signing key is legitimate
- This proof is stored in the database
- Now you can sign statements instantly using your JWT signing key
- The system knows your signatures are trustworthy because your key was attested by a passkey
- Multiple users can sign the same statement
- Configurable threshold (currently 2-of-3)
- Statement becomes valid only when threshold is reached
- Hardware-backed: Passkeys use secure hardware enclaves (TPM, Secure Enclave...)
- Cryptographic proof: Each signing key is attested by a passkey
- Standard JWTs: Signatures use EdDSA (Ed25519) algorithm
- Threshold-based signature requirement: Statement becomes valid only when the threshold of signatures is reached
- Two-step setup: Passkey registration (once), then attestation of signing key (once)
- Instant signing: No biometric prompts for every signature
Creator (1 user)
- Creates JSON statements
- Can sign own statements
- Views approval status for all statements
Investors (2+ users)
- Reviews statements and their approval status
- Signs statements to approve
Statements have 2 states:
- Pending - Awaiting sufficient signatures
- Approved - Threshold reached (e.g., 2/3 signatures) ✓
Note: In future, can have a rejected state by adding explicit rejection mechanism.
-- Users with passkey credentials
users (user_id, name, role, credential_id, created_at)
passkey_credentials (credential_id, public_key, counter, ...)
-- JWT signing keys attested by passkeys
attested_jwt_keys (
key_id,
user_id,
credential_id,
public_key_jwk,
passkey_attestation, -- Proof that passkey attested this key
created_at
)
-- Statements and their signatures
statements (statement_id, title, content, creator_id, created_at)
statement_signatures (
id,
statement_id,
user_id,
jwt, -- The actual signature (JWT)
signed_at
)Each user signature of a statement is formatted as a JWT:
{
"header": {
"alg": "EdDSA",
"typ": "JWT",
"kid": "user-signing-key-id"
},
"payload": {
"statementId": "stmt-123",
"content": "{...statement JSON...}",
"signer": "user-abc",
"timestamp": 1732147200000
},
"signature": "..."
}Registration (Consolidated)
POST /api/credentials- Get passkey registration optionsPOST /api/register/complete- Complete registration (verify passkey + create user + register JWT key)
Authentication
POST /api/authenticate/options- Get authentication challengePOST /api/authenticate- Verify passkey authentication
User Management
GET /api/users- List all usersGET /api/users/[credentialId]- Get user by credential ID
Statement Management
POST /api/statements/create- Create new statementGET /api/statements- List all statementsGET /api/statements/[id]- Get specific statementPOST /api/statements/[id]/sign- Sign a statement
Key Management
GET /api/jwt-keys/[id]- Get key detailsGET /api/jwt-keys/by-credential/[credentialId]- Get keys by credential
Scenario: Three parties must approve a $1M investment.
- Alice (Creator) drafts the investment terms as JSON
- Alice signs (1/3) - Statement still pending
- Bob (Investor) signs (2/3) - Threshold reached! ✅ Statement valid
- Carol (Investor) signs (3/3) - Optional additional approval
The statement became valid at 2 signatures (the threshold).
- Hardware protection: Private keys never leave secure hardware
- Attestation: Cryptographic proof that a JWT signing key is legitimate
- User presence: Confirms user was present during setup
- Standard signatures: Works with any JWT library
- Fast signing: No hardware prompts
- Portability: Keys can be backed up (if desired)
- Passkey proves your JWT signing key is trustworthy (one-time)
- JWT signing key creates fast, standard signatures (many times)
- Threshold ensures multiple parties must agree
JWT Private Key Storage
- Private keys stored in browser
localStorage - Keys persist across refreshes but lost if browser data cleared
- Vulnerable to XSS - not production-ready
-
Server-side encrypted storage (recommended)
- Store encrypted keys server-side
- Enable cross-device access
- Implement key rotation
-
Non-extractable Web Crypto keys
- Use
extractable: false - Keys can't be stolen via XSS
- Device-bound only
- Use
-
Session-bound ephemeral keys
- Generate new key each session
- Maximum security
- Requires re-attestation each session
- Next.js 15.3.4 - React framework
- jose 5.9.6 - JWT operations
- @simplewebauthn - WebAuthn/Passkey implementation
- better-sqlite3 - Local database
- TypeScript - Type safety
Requires WebAuthn support:
- Chrome/Edge 67+
- Firefox 60+
- Safari 13+
docs/FLOW.md- Detailed process flowdocs/JWT-VERIFICATION-GUIDE.md- How to verify signatures
MIT - Educational proof-of-concept
Note: This demonstrates multi-signature workflows with passkey attestation. For production, implement proper key management, security audits, and infrastructure hardening.