Code Monkey home page Code Monkey logo

jwtinfo's Introduction

jwtinfo

build badge codecov crates.io badge Documentation rustc badge Clippy Linting Result License: MIT OR Apache-2.0 Gitpod Ready-to-Code

A command line tool to get information about JWTs (Json Web Tokens).

Usage

jwtinfo is a command line interface that allows you to inspect a given JWT. The tool currently allows 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_adQssw5c

Which 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_adQssw5c

Which will print:

{"alg":"HS256","typ":"JWT"}

You can combine the tool with other command line utilities, for instance jq:

jwtinfo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c | jq .

Install

You can install the binary in several ways:

Cargo

You can install the binary in your system with cargo:

cargo install jwtinfo

At this point jwtinfo will be available as a binary in your system.

Install script

The following script will download and install precompiled binaries from the latest GitHub release

curl https://raw.githubusercontent.com/lmammino/jwtinfo/main/install.sh | sh

By default it will install the binary in /usr/local/bin. You can customize this by setting the INSTALL_DIRECTORY environment variable before running the script (e.g. INSTALL_DIRECTORY=$HOME will install the binary in $HOME/bin).

If you want to install a specific release you can set the RELEASE_TAG environment variable to point to your target versiong before running the script (e.g. RELESE_TAG=v0.1.7).

Precompiled binaries

Pre-compiled binaries for x64 (Windows, MacOs and Unix) and ARMv7 are available in the Releases page.

Alternatives

If you don't want to install a binary for debugging JWT, a super simple bash alternative called jwtinfo.sh is available.

Programmatic usage

Install with cargo:

[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\"}");

Coverage reports

If you want to run coverage reports locally you can follow this recipe.

First of all you will need Rust Nightly that you can get with rustup

rustup install nightly

You will also need grcov that you can get with cargo:

cargo install grcov

Now 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 test

This will run your tests and generate coverage info in ./target/debug/

Now we 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.

Tarpaulin coverage

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.

Credits

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.

Contributing

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.

License

Licensed under MIT License. © Luciano Mammino & Stefano Abalsamo.

jwtinfo's People

Contributors

lmammino avatar stefanoabalsamo79 avatar whiskeytuesday avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

jwtinfo's Issues

called `Option::unwrap()` on a `None` value

Sys info:

Ubuntu 23.04 x86_64
Kernel: 6.2.0-36-generic
Shell: bash 5.2.15
jwtinfo 0.4.2

Bug

RUST_BACKTRACE=1 jwtinfo
thread 'main' panicked at /home/edoardottt/.cargo/registry/src/index.crates.io-6f17d22bba15001f/jwtinfo-0.4.2/src/cli.rs:32:56:
called `Option::unwrap()` on a `None` value
stack backtrace:
   0: rust_begin_unwind
             at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:595:5
   1: core::panicking::panic_fmt
             at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:67:14
   2: core::panicking::panic
             at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:117:5
   3: jwtinfo::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Allow dumping both header and body

I'd like to add a feature to jwtinfo that allows dumping both the header and the body simultaneously, perhaps a --full flag? @lmammino would you accept a PR for this?

Output:

{
  "header": {
    "kid": "24378254378"
  },
  "claims": {
    "iat": 12345,
    ...,
  }
}

It fails with encrypted JWT tokens

jwtinfo currently fails with encrypted tokens. It should instead print a nicer message mentioning that the token is encrypted and therefore it is not possible to read the body.

An example of this kind of token is what's generated by AWS Cognito as a refresh token.

The header part looks like the following:

{
  "cty": "JWT",
  "enc": "A256GCM",
  "alg": "RSA-OAEP"
}

On possible solution is that the CLI should not try to parse the body as JSON if there is an enc field in the header.

Improve code quality - better error management - remove nested matches

As suggested on Reddit by Doddzilla7, there's an opportunity to do better error management and get rid of nested matched by using a specific Enum type error that wraps more generic errors as in the following example:

pub enum JWTDecodeError {
  MissingSection(String),
  InvalidUtf8(str::Utf8Error),
  InvalidBase64(base64::DecodeError),
  InvalidJSON(serde_json::error::Error)
}

This will allow to use the ? operator in many places and get rid of nested matches.

Track code coverage and increase tests

At the moment we are only testing the happy path. It would be great to test the rest of the codebase as well and to have a way to track and record code coverage (maybe with integration with coveralls).

allow verifying the token against a JWKS

I'd like to add a feature to jwtinfo that allows specifying an optional JWKS URL to verify the token against.

Here's my thought about what it might look like: jwtinfo --jwks=https://mydomain.us.auth0.com/ <token>

There are a couple different ways you could handle output/validation errors:

  • no stdout, print error to stderr and exit with a non-zero code
  • print data to stdout even if the token is invalid, print error to stderr, exit with non-zero code
  • In combination with #37, extend the output format of the "full" output to include extra keys including valid: bool and validationErrors: str[]

@lmammino interested in your thoughts about whether you'd accept a PR, and suggestions for shaping a reasonable CLI surface.

thread 'main' panicked at 'Mismatch between definition and access of `pretty`. Could not downcast to TypeId

On linux. Specifically:

cat /etc/os-release 
NAME="CentOS Stream"
VERSION="8"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="8"
PLATFORM_ID="platform:el8"
PRETTY_NAME="CentOS Stream 8"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:8"
HOME_URL="https://centos.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_SUPPORT_PRODUCT_VERSION="CentOS Stream"

Full error

RUST_BACKTRACE=1 jwtinfo $TOKEN
thread 'main' panicked at 'Mismatch between definition and access of `pretty`. Could not downcast to TypeId { t: 324675245860759320943513204350442872190 }, need to downcast to TypeId { t: 7428646492878894209665195255548636123 }
', /home/luciano.mammino/.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap_builder-4.4.5/src/parser/error.rs:32:9
stack backtrace:
   0: rust_begin_unwind
             at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/std/src/panicking.rs:593:5
   1: core::panicking::panic_fmt
             at /rustc/d5c2e9c342b358556da91d61ed4133f6f50fc0c3/library/core/src/panicking.rs:67:14
   2: clap_builder::parser::matches::arg_matches::ArgMatches::get_flag
   3: jwtinfo::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Refactor tests

in rust unit tests are in the module tested not outside.

@mardiros on Reddit

More comments by mardiros:

Unit tests:

https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html

Most unit tests go into a tests mod with the #[cfg(test)] attribute. Test functions are marked with the #[test] attribute.

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;
}

Integration tests:

https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html

Cargo looks for integration tests in tests directory next to src.

(e.g. not in src).

Add the option --header to allow looking into the header part

jwtinfo prints the body of the token. Sometimes it is convenient to have a look at the header as well (e.g. in openID tokens can have attributes like jti - token id - or jki - token key id - embedded in the header part).

When this feature is implemented, running the following command:

jwtinfo --header "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbWUta2V5In0.eyJoZWxsbyI6ImZyb20gSldUIn0._tBIgntQFTeCCNbTbHnNONfXTDAfuu77DtkkD0RE0I0"

Should print:

{"alg": "HS256","typ": "JWT","kid": "some-key"}

In the stdout

Expose internal jwt parsing library

It would be great to be able to expose the internal JWT parsing library so that other projects can leverage it if needed.

Right now we expose only the binary application.

Some documentation on how this might be achieved:

If we do this, we should also make sure that the crate documentation is generated and published correctly. This document might help with this task

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.