{"id":46569043,"url":"https://github.com/sbstjn/jwtiny","last_synced_at":"2026-03-07T08:02:48.856Z","repository":{"id":322160457,"uuid":"1088394413","full_name":"sbstjn/jwtiny","owner":"sbstjn","description":"Minimal, type-safe JSON Web Token (JWT) validation for Rust.","archived":false,"fork":false,"pushed_at":"2025-12-21T11:30:38.000Z","size":876,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-22T18:58:35.177Z","etag":null,"topics":["authentication","jsonwebtoken","jwt","rust","rust-lang"],"latest_commit_sha":null,"homepage":"https://sbstjn.com/blog/jwtiny-minimal-type-safe-jwt-validation-rust/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sbstjn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-02T22:00:04.000Z","updated_at":"2025-12-21T11:26:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sbstjn/jwtiny","commit_stats":null,"previous_names":["sbstjn/jwtiny"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/sbstjn/jwtiny","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbstjn%2Fjwtiny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbstjn%2Fjwtiny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbstjn%2Fjwtiny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbstjn%2Fjwtiny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sbstjn","download_url":"https://codeload.github.com/sbstjn/jwtiny/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sbstjn%2Fjwtiny/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30209796,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T05:23:27.321Z","status":"ssl_error","status_checked_at":"2026-03-07T05:00:17.256Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","jsonwebtoken","jwt","rust","rust-lang"],"created_at":"2026-03-07T08:02:43.674Z","updated_at":"2026-03-07T08:02:48.811Z","avatar_url":"https://github.com/sbstjn.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jwtiny\n\n[![crates.io](https://img.shields.io/crates/v/jwtiny.svg)](https://crates.io/crates/jwtiny)\n[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE.md)\n[![CI](https://github.com/sbstjn/jwtiny/actions/workflows/release.yml/badge.svg)](https://github.com/sbstjn/jwtiny/actions/workflows/ci.yml)\n[![CI](https://github.com/sbstjn/jwtiny/actions/workflows/ci.yml/badge.svg)](https://github.com/sbstjn/jwtiny/actions/workflows/ci.yml)\n\n\u003e Minimal JSON Web Token (JWT) validation for Rust.\n\n**jwtiny** validates JWT tokens efficiently in production Rust applications. The validator follows a reusable pattern: configure it once at application startup, then verify tokens with minimal allocations. The validator can be shared across requests, which reduces memory footprint and improves performance.\n\nThe library supports **RSA** (RS256, RS384, RS512) and **ECDSA** (ES256, ES384, ES512) algorithms with an `aws-lc-rs` backend, and provides **JWKS support** for remote key fetching over HTTPS (`rustls`) with caching. It's been tested with [Axum](examples/axum/), [Poem](examples/poem/), [Rocket](examples/rocket/), and [Warp](examples/warp/).\n\n## Installation\n\nAdd **jwtiny** to your `Cargo.toml`:\n\n```bash\ncargo add jwtiny\n```\n\n## Quick Start\n\n### Static Key Validation\n\nWhen you have a static public key, configure the validator like this:\n\n```rust\nuse std::sync::Arc;\nuse jwtiny::{AlgorithmPolicy, ClaimsValidation, TokenValidator};\n\nlet validator = TokenValidator::new()\n    .algorithms(AlgorithmPolicy::rs512_only())\n    .validate(ClaimsValidation::default())\n    .key(Arc::new(public_key_der));\n\nlet claims = validator.verify(token).await?;\n```\n\nThat's it! The validator's ready to use. You can call `verify()` as many times as you need—the library is designed for reuse.\n\n### JWKS Validation (Remote Key Fetching)\n\nFor production systems, you'll often want to fetch keys from a JWKS endpoint. Here's how the library sets this up:\n\n```rust\nuse jwtiny::{AlgorithmPolicy, ClaimsValidation, TokenValidator};\nuse moka::future::Cache;\nuse std::time::Duration;\n\nlet client = reqwest::Client::new();\nlet cache = Cache::\u003cString, Vec\u003cu8\u003e\u003e::builder()\n    .time_to_live(Duration::from_secs(300))\n    .max_capacity(1000)\n    .build();\n\nlet validator = TokenValidator::new()\n    .algorithms(AlgorithmPolicy::rs512_only())\n    .issuer(|iss| iss == \"https://auth.example.com\")\n    .validate(ClaimsValidation::default().require_audience(\"my-api\"))\n    .jwks(client)\n    .cache(cache);\n\nlet claims = validator.verify(token).await?;\n```\n\nThe cache reduces network requests and improves performance. Set the TTL to match the key rotation schedule of the identity provider.\n\nFor testing, this works fine with [JWKServe](https://github.com/sbstjn/jwkserve) as well.\n\n### Custom Claims\n\nIf you need custom claim structures, use the `#[claims]` macro:\n\n```rust\nuse std::sync::Arc;\nuse jwtiny::{claims, AlgorithmPolicy, ClaimsValidation, TokenValidator};\n\n#[claims]\nstruct MyClaims {\n    pub role: String,\n    pub permissions: Vec\u003cString\u003e,\n}\n\nlet validator = TokenValidator::new()\n    .algorithms(AlgorithmPolicy::rs256_only())\n    .validate(ClaimsValidation::default())\n    .key(Arc::new(public_key_der));\n\nlet claims = validator.verify_with_custom::\u003cMyClaims\u003e(token).await?;\n```\n\nThe macro handles the standard claims (iss, sub, aud, exp, nbf, iat, jti) automatically, so you only need to define your custom fields.\n\n\n## Performance\n\nFor **ECDSA** algorithms, `jwtiny` is particularly efficient — **ES384** performance is over 3x faster than `jsonwebtoken`, while **ES256** shows a solid 8% improvement. \n\nThe **RSA** performance gains scale with key size: you'll see roughly 18–20% improvements with 2048-bit keys, 26–27% with 3072-bit keys, and around 30–31% with 4096-bit keys, regardless of the hash variant.\n\n![jwtiny-jsonwebtoken-performance](https://raw.githubusercontent.com/sbstjn/jwtiny/refs/heads/main/docs/performance.png)\n\nThese improvements become more pronounced as cryptographic operations become computationally expensive, making jwtiny especially beneficial for high-throughput applications or services handling many concurrent token validations.\n\n![jwtiny-jsonwebtoken-performance-token-size](https://raw.githubusercontent.com/sbstjn/jwtiny/refs/heads/main/docs/performance_size.png)\n\nThe throughput of **RS256** degrades ~35% from default token (60,203 ops/s at 550 bytes) to +1000% token size (39,173 ops/s at 7,830 bytes), while **ES384** stays stable with only ~16% degradation (5,696 to 4,807 ops/s) despite a 14x token size increase. \n\nAt +1000% token size, jwtiny’s **RS256** advantage narrows to ~5%, while **ES384** maintains its ~3x advantage, indicating ES384’s validation is less sensitive to payload size than RS256.\n\n## API Reference\n\n### TokenValidator\n\nConfigure the validator once, then reuse it for multiple verifications:\n\n```rust\nuse std::sync::Arc;\n\nlet validator = TokenValidator::new()\n    .algorithms(AlgorithmPolicy::rs512_only())  // See AlgorithmPolicy section below\n    .issuer(|iss| iss == \"https://auth.example.com\")\n    .validate(ClaimsValidation::default().require_audience(\"my-api\"))\n    .key(Arc::new(public_key_der))  // Wrap in Arc for efficient sharing\n    .jwks(client)                   // JWKS (mutually exclusive with key)\n    .cache(cache);                  // Optional: cache JWKS keys\n\n// Verify tokens (reusable)\nlet claims = validator.verify(token_str).await?;\nlet custom = validator.verify_with_custom::\u003cMyClaims\u003e(token_str).await?;\n```\n\n### AlgorithmPolicy\n\nControl which algorithms are accepted:\n\n```rust\nuse jwtiny::{AlgorithmPolicy, AlgorithmType};\n\n// Predefined policies (zero-allocation, use stack arrays)\nAlgorithmPolicy::rs256_only()  // RS256 only\nAlgorithmPolicy::rs384_only()  // RS384 only\nAlgorithmPolicy::rs512_only()  // RS512 only\nAlgorithmPolicy::rsa_all()     // All RSA algorithms\n\nAlgorithmPolicy::es256_only()  // ES256 (P-256) only\nAlgorithmPolicy::es384_only()  // ES384 (P-384) only\nAlgorithmPolicy::es512_only()  // ES512 (P-521) only\nAlgorithmPolicy::ecdsa_all()   // All ECDSA algorithms\n\n// Custom policies (accepts arrays)\nAlgorithmPolicy::allow_only([AlgorithmType::RS256, AlgorithmType::ES256])\n```\n\n**Note**: The default policy is `rs256_only()` for security. Always configure the policy explicitly to match your identity provider's signing algorithm.\n\n### ClaimsValidation\n\nConfigure temporal and audience validation:\n\n```rust\nClaimsValidation::default()\n    .require_audience(\"my-api\")\n    .max_age(3600)\n    .clock_skew(60)\n    .no_exp_validation()\n    .no_nbf_validation()\n    .no_iat_validation()\n```\n\nBy default, the validator checks expiration (`exp`), not-before (`nbf`), and issued-at (`iat`), with a max age of 30 minutes and no clock skew. In distributed systems, adding clock skew tolerance can help handle time synchronisation differences.\n\n## Error Handling\n\nAll validation errors are returned as `jwtiny::Error`:\n\n```rust\nmatch validator.verify(token).await {\n    Ok(claims) =\u003e println!(\"Valid: {:?}\", claims),\n    Err(jwtiny::Error::TokenExpired { .. }) =\u003e eprintln!(\"Token expired\"),\n    Err(jwtiny::Error::SignatureInvalid) =\u003e eprintln!(\"Invalid signature\"),\n    Err(e) =\u003e eprintln!(\"Validation failed: {:?}\", e),\n}\n```\n\n## Examples\n\nComplete working examples for various web frameworks:\n\n- **Axum**: [`examples/axum.rs`](examples/axum.rs)\n- **Poem**: [`examples/poem.rs`](examples/poem.rs)\n- **Rocket**: [`examples/rocket.rs`](examples/rocket.rs)\n- **Warp**: [`examples/warp.rs`](examples/warp.rs)\n\nRun an example:\n\n```bash\n# Run example: axum, poem, rocket, or warp\n$ \u003e cargo run --example axum\n```\n\n## Benchmarks\n\nNaive performance benchmarks are available to measure validation throughput and latency:\n\n- **[RSA Validation](benches/validation/rsa_validation.rs)** — Signature validation performance for RSA 2048 keys with SHA-256, SHA-384, and SHA-512 algorithms\n- **[Token Size](benches/validation/token_size.rs)** — Validation performance across varying JWT payload sizes (baseline to +2000%)\n- **[JWKS Validation](benches/remote/jwks_validation.rs)** — Remote JWKS endpoint validation with and without caching for SHA-256, SHA-384, and SHA-512\n- **[Rocket Integration](benches/remote/rocket_integration.rs)** — End-to-end validation performance through Rocket middleware with and without caching\n\nRun benchmarks:\n\n```bash\n# Run all benchmarks\n$ \u003e cargo bench\n\n# Run a specific benchmark\n$ \u003e cargo bench --bench rsa_validation\n$ \u003e cargo bench --bench token_size\n$ \u003e cargo bench --bench jwks_validation\n$ \u003e cargo bench --bench rocket_integration\n```\n\nBenchmark results are exported to `./reports/` as plain text files for analysis and charting.\n\n## License\n\nMIT\n\n## References\n\n- [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515) — JSON Web Signature (JWS)\n- [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) — JSON Web Token (JWT)\n- [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725) — JSON Web Signature Best Practices","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbstjn%2Fjwtiny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsbstjn%2Fjwtiny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsbstjn%2Fjwtiny/lists"}