Validate signed JSON Web Tokens (JWTs) inside the zkVM to create zero-knowledge proofs of token authenticity and integrity.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/risc0/risc0/llms.txt
Use this file to discover all available pages before exploring further.
What You’ll Learn
- Verifying JWT signatures with RS256
- Using the jwt-compact crate in the zkVM
- Building Bonsai applications for offchain verification
- Connecting offchain identities to onchain addresses
Overview
This example demonstrates JWT verification using:- RS256 algorithm: RSA signature with SHA-256
- Public key verification: Validate tokens without the private key
- Claim extraction: Commit specific JWT claims to the journal
- Zero-knowledge: Prove token validity without revealing the full token
How It Works
use jwt_core::Validator;
use risc0_zkvm::guest::env;
static PUBLIC_KEY: &str = r#"
{
"alg": "RS256",
"e": "AQAB",
"key_ops": ["verify"],
"kty": "RSA",
"n": "zcQwXx3EevOSkfH0VSWqtfmWTL4c2oIzW6u83qKO1W7X...",
"use": "sig",
"kid": "6ab0e8e4bc121fc287e35d3e5e0efb8a"
}
"#;
fn main() {
// Read the token input
let token: String = env::read();
// Create validator from public key
let validator = PUBLIC_KEY
.parse::<Validator>()
.expect("failed to create validator from key");
// Validate token integrity
let valid_token = validator
.validate_token_integrity(token.as_str())
.expect("token integrity check failed");
// Commit the subject claim
env::commit(&valid_token.claims().custom.subject);
}
The public key is embedded in the guest program, making it part of the Image ID. This ensures token verification uses the correct key.
use jwt_core::{issue_token, Claims};
use jwt_validator_methods::{JWT_VALIDATOR_ELF, JWT_VALIDATOR_ID};
use risc0_zkvm::{ExecutorEnv, default_prover};
fn main() {
// Issue a JWT with a private key
let claims = Claims {
subject: "user@example.com".to_string(),
// ... other claims
};
let token = issue_token(&private_key, &claims).unwrap();
// Prove the JWT is valid
let env = ExecutorEnv::builder()
.write(&token)
.unwrap()
.build()
.unwrap();
let receipt = default_prover()
.prove(env, JWT_VALIDATOR_ELF)
.unwrap()
.receipt;
// Verify the receipt
receipt.verify(JWT_VALIDATOR_ID).unwrap();
// Extract the subject from the journal
let subject: String = receipt.journal.decode().unwrap();
println!("Validated JWT for subject: {}", subject);
}
Running the Example
What Gets Proven?
The receipt proves:- Signature Validity: The JWT signature is valid for the embedded public key
- Token Integrity: The token hasn’t been tampered with
- Claim Binding: The subject claim matches what’s committed to the journal
- Key Binding: Verification used the specific public key (via Image ID)
JWT Structure
A JWT consists of three parts:Header
Specifies the algorithm and key ID:Payload (Claims)
Contains the data being attested:Signature
RSA signature over header and payload:Use Cases
JWT validation in zkVM enables privacy-preserving identity verification across trust boundaries.
Onchain Identity Verification
Connect offchain OAuth accounts to onchain addresses:- User signs in with Google OAuth
- Identity provider issues JWT
- zkVM validates JWT and links to wallet address
- Smart contract verifies the receipt onchain
API Authentication
Prove you have a valid API token without exposing it:Session Management
Validate session tokens with zero-knowledge:Credential Delegation
Prove you have credentials without revealing them:Bonsai Integration
Use as a Bonsai application for:- Offchain computation: Generate proofs off-chain
- Onchain verification: Verify cheaply on blockchain
- Scalability: Handle high-volume JWT validation
- Privacy: Keep tokens offchain
Architecture
Security Considerations
Public Key Management
The public key is embedded in the guest:- Key is part of the Image ID
- Changing the key requires recompiling
- Ensures consistent verification
Token Expiration
This example doesn’t check expiration. Production systems should validate:Algorithm Confusion
The validator enforces RS256:Using the jwt-compact Crate
The example uses jwt-compact for JWT operations:- Uses pure Rust implementation
- Avoids unsupported system calls
- Supports no_std compilation
Performance
JWT validation with RS256:- Cycles: ~50-100M depending on key size
- Proving time: 10-30 seconds locally
- Verification time: Milliseconds onchain
- Receipt size: ~128 KB
Next Steps
- Build a Bonsai application
- Explore digital signatures
- Learn about Bonsai as a zk coprocessor
- Check out Bonsai Pay demo