| Crates.io | pwt |
| lib.rs | pwt |
| version | 0.7.0 |
| created_at | 2025-09-09 22:27:41.532607+00 |
| updated_at | 2025-09-10 00:56:40.70951+00 |
| description | Create, verify and decode protobuf web tokens - with protobuf instead of prost |
| homepage | https://github.com/dra11y/pwt |
| repository | https://github.com/dra11y/pwt |
| max_upload_size | |
| id | 1831690 |
| size | 133,301 |
A Rust library for creating, signing, and verifying compact binary web tokens using Protocol Buffers and Ed25519 signatures.
This crate is a fork of protobuf-web-token that replaces the prost dependency with the official protobuf crate.
Traditional JSON Web Tokens (JWTs) use JSON encoding, which is inefficient for data transfer compared to compact binary encodings like Protocol Buffers. PWTs provide:
| Metric | Simple Data | Complex Data |
|---|---|---|
| Speed | PWT 2.5x faster | PWT 2.6x faster |
| Size | PWT 25% smaller | PWT 60% smaller |
Add this to your Cargo.toml:
[dependencies]
pwt = "0.7"
protobuf = "3.7"
Create a .proto file defining your token claims:
syntax = "proto3";
package test;
message UserClaims {
int64 user_id = 1;
string username = 2;
string email = 3;
repeated Role roles = 4;
}
Add to your build.rs:
fn main() -> Result<(), Box<dyn std::error::Error>> {
protobuf_codegen::Codegen::new()
.out_dir("tests/generated")
.include(".")
.input("tests/fixtures/test.proto")
.run()?;
Ok(())
}
use pwt::{Signer, ed25519::SigningKey};
use std::time::Duration;
// Include your generated protobuf code
mod generated {
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/generated/mod.rs"));
}
use generated::test::UserClaims;
// Create a signer with your Ed25519 private key
let signing_key = SigningKey::from_bytes(&[1u8; 32]); // Use your actual key
let signer = Signer::new(signing_key);
// Create your claims using the generated protobuf struct
let claims = UserClaims {
user_id: 12345,
username: "alice".to_string(),
email: "alice@example.com".to_string(),
..Default::default()
};
// Sign the token (valid for 1 hour)
let token = signer.sign(&claims, Duration::from_secs(3600));
// Verify and decode the token
let verifier = signer.as_verifier();
let decoded = verifier.verify::<UserClaims>(&token)?;
println!("User ID: {}", decoded.claims.user_id);
println!("Username: {}", decoded.claims.username);
println!("Valid until: {:?}", decoded.valid_until);
PWT supports two token formats:
{base64_data}.{base64_signature}
Signer::sign] and [Verifier::verify]use pwt::{Signer, ed25519::SigningKey};
use std::time::Duration;
#
// Create a signer
let signing_key = SigningKey::from_bytes(&[1u8; 32]);
let signer = Signer::new(signing_key);
// Create claims using your generated protobuf struct
let claims = Simple {
some_claim: "binary example".to_string(),
..Default::default()
};
// Sign as bytes (most compact format)
let token_bytes = signer.sign_to_bytes(&claims, Duration::from_secs(3600));
let decoded = signer.as_verifier().verify_bytes::<Simple>(&token_bytes)?;
Signer::sign_to_bytes] and [Verifier::verify_bytes]This fork makes several improvements over the original protobuf-web-token:
prost to official protobuf crate for better ecosystem compatibilitytry_* methods for all potentially failing operationsvalid_until timestampPWT is ideal when you:
For maximum interoperability with existing systems, stick with JWT.
The benefit of PWT is obvious if you are using protobuf anyway and want to avoid JSON just for JWT.
I forked the original crate because prost is incompatible with protobuf,
poorly documented, depends on macros in the generated code (making it hard to debug), and my project was already using protobuf.
Credit to original author Andreas Molitor (anmolitor) for protobuf-web-token.
License: BSD-3-Clause