A command line tool to get information about JWTs (JSON Web Tokens).
- Decode JWT tokens without verification - quickly inspect header and claims
- Multiple display modes: view body only (default), header only (
--header), or both (--full) - Pretty printing with
--prettyflag for readable JSON output - Stdin support - pipe tokens directly or use as command argument
- JWE token detection - gracefully handles encrypted JWT tokens with clear messaging
- Composable - works seamlessly with tools like
jqfor advanced JSON processing
- Simple parsing API -
jwt::parse()function for easy token decoding - Type-safe access - header and body exposed as
serde_json::Value - FromStr implementation - parse tokens using
.parse::<jwt::Token>() - No verification - focused on inspection and debugging, not validation
- JWE support - detects encrypted tokens and handles them appropriately
jwtinfo is a command line interface that allows you to inspect a given JWT.
The tool currently allows you to see the body of the token in JSON format. It
accepts a single command line argument which should be a valid JWT.
Here's an example:
jwtinfo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cWhich will print:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }If you want to visualize the token header (rather than the body), you can do
that by passing the --header flag:
jwtinfo --header eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cWhich will print:
{ "alg": "HS256", "typ": "JWT" }If you want to see both the header and the claims at the same time, you can use
the --full flag:
jwtinfo --full eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cWhich will print:
{"header":{"alg":"HS256","typ":"JWT"},"claims":{"sub":"1234567890","name":"John Doe","iat":1516239022}}For better readability, you can combine --full with the --pretty flag to
get formatted output:
jwtinfo --full --pretty eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cWhich will print:
{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"claims": {
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
}You can combine the tool with other command line utilities, for instance
jq:
jwtinfo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c | jq .Note
Encrypted JWE Tokens: If you provide an encrypted JWE token (JSON Web Encryption), the tool will detect it by checking for the enc field in the header. Since JWE tokens are encrypted, the claims/body cannot be read without decryption. In this case, jwtinfo will display the special placeholder string "<encrypted JWE body>" instead of the actual claims. The header can still be inspected normally using the --header flag.
You can install the binary in several ways:
Install via npm (Node.js package manager):
npm install -g jwtinfoOr use npx to run without installing:
npx jwtinfo <token>Install via Homebrew (macOS and Linux):
# Add the tap
brew tap lmammino/tap
# Install jwtinfo
brew install jwtinfoOr install directly in one command:
brew install lmammino/tap/jwtinfoDownload and install precompiled binaries with a single command:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/lmammino/jwtinfo/releases/latest/download/jwtinfo-installer.sh | shDownload and install precompiled binaries with PowerShell:
irm https://github.com/lmammino/jwtinfo/releases/latest/download/jwtinfo-installer.ps1 | iexYou can install the binary in your system with
cargo:
cargo install jwtinfoPre-compiled binaries for multiple platforms are available in the Releases page.
If you are using Nix, you can install the jwtinfo binary
with the following command:
nix profile install github:lmammino/jwtinfoOr, if you prefer to use a configuration file, you can add the following to your flake:
jwtinfo = {
url = "github:lmammino/jwtinfo";
inputs.nixpkgs.follows = "nixpkgs";
};
# ... with home.nix
home.packages = [ inputs.jwtinfo.packages."x86_64-linux".default ];
# ... with configuration.nix
environment.systemPackages = [ inputs.jwtinfo.packages."x86_64-linux".default ];Make sure to replace "x86_64-linux" with your target platform.
You can also just try it out in a Nix shell with:
nix shell github:lmammino/jwtinfo -c jwtinfo <some_jwt_token>Finally, for development purposes, you can clone this repo and then run:
nix developIf you don't want to install a binary for debugging JWT, a super simple bash
alternative called
jwtinfo.sh
is available.
Add to your Cargo.toml:
[dependencies]
jwtinfo = "*"Then use it in your code:
use jwtinfo::{jwt};
let token_str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
let token = jwt::parse(token_str).unwrap();
assert_eq!(token.header.to_string(), "{\"alg\":\"HS256\",\"typ\":\"JWT\"}");
assert_eq!(token.body.to_string(), "{\"iat\":1516239022,\"name\":\"John Doe\",\"sub\":\"1234567890\"}");Since jwt::Token implements str::FromStr, you can also do the following:
use jwtinfo::{jwt};
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c".parse::<jwt::Token>().unwrap();
assert_eq!(token.header.to_string(), "{\"alg\":\"HS256\",\"typ\":\"JWT\"}");
assert_eq!(token.body.to_string(), "{\"iat\":1516239022,\"name\":\"John Doe\",\"sub\":\"1234567890\"}");If you want to run coverage reports locally you can follow this recipe.
First, you will need Rust Nightly that you can get with rustup
rustup install nightlyYou will also need grcov that you can get with cargo:
cargo install grcovNow you can run the tests in profile mode:
export CARGO_INCREMENTAL=0
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads"
cargo +nightly testThis will run the tests and generate coverage info in ./target/debug/
Now you can run grcov:
grcov ./target/debug/ -s . -t html --llvm --branch --ignore-not-existing -o ./target/debug/coverage/Finally, you will have your browsable coverage report at
./target/debug/coverage/index.html.
Since grcov tends to be somewhat inaccurate at times, you can also get a
coverage report by running tarpaulin
using docker:
docker run --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin:develop-nightly bash -c 'cargo build && cargo tarpaulin -o Html'Your coverage report will be available as tarpaulin-report.html in the root of
the project.
A special thank you goes to the Rust Reddit community for providing a lot of useful suggestions on how to improve this project. A special thanks goes to: mardiros, matthieum, steveklabnik1, ESBDB, Dushistov, Doddzilla7. Another huge thank you goes to the Rust stackoverflow community, especially to Denys Séguret.
Big thanks also go to Tim McNamara for conducting a live code review of this codebase.
Everyone is very welcome to contribute to this project. You can contribute just by submitting bugs or suggesting improvements by opening an issue on GitHub.
Licensed under MIT License. © Luciano Mammino & Stefano Abalsamo.