Executive Summary
Almost every consumer-facing product that uses WebAuthn passkeys today uses them for one purpose: logging into a web session. The passkey unlocks an app, and the app - not the passkey - is what actually authorizes a transaction using a separately-held private key. JIL Sovereign's ledger service instead treats the raw WebAuthn P-256 assertion as the transaction-authorization primitive itself: the assertion is verified directly by validator node code, and the thing being signed is a canonical encoding of the transaction's own fields, not a generic login challenge.
Because the verification logic depends only on data already present in the transaction and the account's registered public key, it is deterministic and safe to execute inside consensus-critical validator code - every validator reaches the same accept/reject answer without calling out to an authentication service, an identity provider, or Apple/Google/Microsoft's own servers.
Problem Statement
Self-custody blockchain wallets have historically forced a choice between security and accessibility: a raw private key or BIP-39 seed phrase gives full self-custody but is a single point of catastrophic, unrecoverable loss for a mainstream user, while custodial wallets remove that burden by giving up self-custody entirely. Passkeys solve the usability half of this problem for web login, but most blockchain integrations of passkeys stop at the login boundary - the passkey unlocks a session, and a conventional private key, held somewhere else in the stack, is what actually signs the transaction. That leaves the private-key-loss problem exactly where it was.
Why Passkey-Gated Wallets Fall Short
- Passkey-gated session login: the passkey authenticates a user into an app; a separately-stored private key still performs the actual transaction signature.
- Hardware wallets with PIN entry: device-bound and phishing-resistant, but not natively verifiable by a validator without an external attestation service.
- Custodial exchange accounts: remove the seed-phrase burden entirely by taking custody, the opposite tradeoff.
- Generic FIDO2 authentication middleware: designed for HTTP session authentication, not for producing a signature a consensus-critical validator can verify deterministically against transaction-specific fields.
Technical Architecture
The Canonical Transaction Challenge
Before a transaction is signed, the wallet client and the validator both independently compute the same canonical string from the transaction's own fields: network identifier, sender, recipient, asset, amount, fee, fee collector, and nonce, joined with a fixed delimiter. That string is hashed (SHA-256) and the resulting digest, base64url-encoded, becomes the WebAuthn challenge value presented to the authenticator. Binding fee and fee collector into the challenge means a relayer submitting the transaction on the user's behalf cannot inflate the fee or redirect it to a different destination - either change produces a different challenge and invalidates the signature.
Deterministic Validator-Side Verification
| Check | What Is Verified |
|---|---|
| authenticatorData length | Rejected outright if shorter than 37 bytes, before any further parsing |
| rpIdHash | First 32 bytes of authenticatorData must equal SHA-256 of the expected relying-party identifier |
| User-presence flag | Must be set in the authenticatorData flags byte |
| User-verification flag (optional) | When required, must also be set in the flags byte |
| clientDataJSON.type | Must equal webauthn.get |
| clientDataJSON.challenge | Must equal the transaction-derived challenge computed independently by the validator |
| Signature | P-256 ECDSA signature over authenticatorData || SHA-256(clientDataJSON), verified against the account's registered public key |
All seven checks run against data already carried in the assertion and the transaction; none require a network call. That determinism is what allows the check to run safely inside validator node code rather than an off-chain authentication tier - every validator computing the same expected challenge and the same rpIdHash will reach the same verification result.
Fee and Relayer Tamper Binding
Many account-abstraction and meta-transaction designs let a relayer submit a transaction on the user's behalf while charging a fee for doing so - and a common weakness is that the fee amount or destination is decided by the relayer at submission time, outside anything the user actually signed. Because fee and fee_collector are explicit fields in JIL Sovereign's canonical challenge string, they are locked at the moment of signing: an empty fee collector produces one canonical string, a non-empty one produces a different string entirely, and any relayer-side modification after signing changes the digest the validator recomputes, causing signature verification to fail. The user's signature is authorization for one specific fee, to one specific collector - not a blank check to the relaying infrastructure.
Prior Art Differentiation
| Approach | What the Passkey Actually Authorizes | Verified By | Fee/Destination Tamper-Bound? |
|---|---|---|---|
| Passkey-gated web session | App login only | Application backend / identity provider | No |
| Hardware wallet + PIN | Transaction signing, device-local | Device firmware | Depends on client implementation |
| Raw private-key / seed-phrase wallet | Transaction signing | Client software | Depends on client implementation |
| JIL Sovereign passkey-as-L1-authorization | The exact transaction (network/parties/asset/amount/fee/fee-collector/nonce) | Validator node, deterministically, in-consensus | Yes - fee and collector are hashed into the signed challenge |