Giter Site home page Giter Site logo

mike-engel / jwt-cli Goto Github PK

View Code? Open in Web Editor NEW
1.0K 4.0 65.0 441 KB

A super fast CLI tool to decode and encode JWTs built in Rust

License: MIT License

Rust 97.05% PowerShell 0.65% Shell 2.30%
rust jwt jwt-token json-web-token command-line-tool cli jwt-cli json jsonwebtoken

jwt-cli's Introduction

jwt-cli

Dependabot Status

A super fast CLI tool to decode and encode JWTs built in Rust.

Continuous Integration GitHub release

jwt-cli is a command line tool to help you work with JSON Web Tokens (JWTs). Like most JWT command line tools out there, you can decode almost any JWT header and claims body. Unlike any that I've found, however, jwt-cli allows you to encode a new JWT with nearly any piece of data you can think of. Custom header values (some), custom claim bodies (as long as it's JSON, it's game), and using any secret you need.

On top of all that, it's written in Rust so it's fast and portable (windows, macOS, and linux supported right now).

Installation

Install jwt-cli via Homebrew or MacPorts (macOS), Cargo (cross-platform), and FreshPorts (FreeBSD). If you intend to use one of these methods, skip ahead.

You may also install the binary from the release page, if you're unable to use Homebrew or Cargo install methods below.

Only 64bit linux, macOS, and Windows targets are pre-built. Sorry if you're not on one of those! You'll need to build it from the source. See the contributing section on how to install and build the project.

You should install it somewhere in your $PATH. For Linux and macOS, a good place is generally /usr/local/bin. For Windows, there isn't a good place by default :(.

Homebrew

# Install jwt-cli
brew install mike-engel/jwt-cli/jwt-cli

# Ensure it worked ok by running the help command
jwt help

MacPorts

sudo port install jwt-cli

More info here.

Cargo

If your system supports it, you can install via Cargo. Make sure you have Rust and Cargo installed, following these instructions before proceeding.

cargo install jwt-cli

The binary installs to your Cargo bin path (~/.cargo/bin). Make sure your $PATH environment variable includes this path.

FreshPorts

If you're on FreeBSD, you can use the pkg tool to install jwt-cli on your system.

pkg install jwt-cli

Big thanks to Sergey Osokin, the FreeBSD contributor who added jwt-cli to the FreeBSD ports tree!

Scoop

jwt-cli is available on the Scoop main repository for Windows.

scoop install jwt-cli

Arch Linux

jwt-cli is available in the Arch Linux community repository and can be installed via pacman:

pacman -S jwt-cli

Alpine linux

Unfortunately due to static linking problems when compiling this project, jwt-cli can't run on alpine linux natively.

A workaround is to run it inside a rust docker container rather than using sh within alpine.

Usage

For usage info, use the help command.

# top level help
jwt help

# command specific help
jwt help encode

Usage as a pipe

The - argument tells jwt-cli to read from standard input:

jwt encode --secret=fake '{"hello":"world"}' | jwt decode -

It's useful when you're dealing with a chain of shell commands that produce a JWT. Pipe the result through jwt decode - to decode it.

curl <auth API> | jq -r .access_token | jwt decode -

Using elliptic curve keys

Currently the underlying token encoding and decoding library, jsonwebtoken, doesn't support the SEC1 private key format and requires a conversion to the PKCS8 type. You can read more from their own README.

Shell completion

jwt-cli supports shell completion for bash, elvish, fish, powershell, and zsh. To enable it, run the following command:

source <(jwt completion bash)

You may want to add this to your shell profile to have it available every time you open a new shell:

if hash jwt > /dev/null; then
  source <(jwt completion bash)
fi

Contributing

I welcome all issues and pull requests! This is my first project in rust, so this project almost certainly could be better written. All I ask is that you follow the code of conduct and use rustfmt to have a consistent project code style.

To get started you'll need rustc and cargo on your system. If they aren't already installed, I recommend rustup to get both!

Building and running the project

Once you have both installed, I recommend running the tests to make sure all is well from the start.

# run the tests
cargo test

If it built without any errors, you should be able to run the command via cargo.

cargo run -- help

Or, if you prefer a release build:

cargo run --release -- help

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Mike Engel
Mike Engel

πŸ’» πŸ’¬ πŸ“– πŸ€” 🚧 πŸ‘€ ⚠️ πŸ›
Kyle Burton
Kyle Burton

πŸ’»
Aaron Schaef
Aaron Schaef

πŸ’»
hughsimpson
hughsimpson

πŸ’» ⚠️
Mat Kelly
Mat Kelly

πŸ’» πŸ›
Jason
Jason

πŸ›
Ben Berry
Ben Berry

πŸ›
Kevin Lanni
Kevin Lanni

πŸ“–
Kosta Krauth
Kosta Krauth

πŸ’» ⚠️ πŸ“–
codedust
codedust

πŸ’» πŸ€” ⚠️
Liz Frost
Liz Frost

πŸ’»
Carl Harris
Carl Harris

πŸ’» ⚠️
Yusuke Kominami
Yusuke Kominami

πŸ’» πŸ“–

This project follows the all-contributors specification. Contributions of any kind welcome!

jwt-cli's People

Contributors

brian6932 avatar cbenoit avatar ceharris avatar codedust avatar deepu105 avatar dependabot-preview[bot] avatar dependabot[bot] avatar fmotrifork avatar herbygillot avatar hughsimpson avatar jazcarate avatar kkrauth avatar komi1230 avatar kyleburton avatar lewandom avatar lizfeed avatar lloydmeta avatar machawk1 avatar mamacdon avatar mike-engel avatar n-ae avatar orhun avatar paholg avatar shousper avatar therealklanni avatar tonglil avatar vdbulcke avatar weltonrodrigo avatar wkral avatar x3ro 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  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

jwt-cli's Issues

Not working on alpine linux

I tried installing this on a docker container, alpine linux.

docker run -it alpine:3.6

And ran below set of commands.

wget https://github.com/mike-engel/jwt-cli/releases/download/5.0.3/jwt-linux.tar.gz;tar -xvzf jwt-linux.tar.gz;rm -rf jwt-linux.tar.gz;mv jwt /usr/local/bin/

But, when I ran jwt, getting error as not found.
.usr/local/bin is already in system path.

Even from current working directory also same issue.

./jwt
/bin/sh: ./jwt: not found

Support private keys as the secret

Summary

For my application, I create a public and private key, and the public key is stored in my application.
openssl genrsa 3072 > ~/jwt.key.txt
openssl rsa -in ~/jwt.key.txt -pubout > ~/jwt.public.txt

I need to use the private key as the secret, but when I attempt that, I get an invalid signature.

Steps to reproduce

jwt encode -S '@jwt.key.txt' -e $(date -v+1H +%s) -P 'my_app={"uid": 20000006}' -k 20000006-1594139970

jwt encode -S $(cat jwt.key.txt) -e $(date -v+1H +%s) -P 'drupal={"uid": 20000006}' -k 20000006-1594139970
error: Found argument '--BEGIN' which wasn't expected, or isn't valid in this context

Transform timestamp into ISO8601 dates for registered claims.

Hello

First, let me thank you for this great tool!

I have a feature suggestion regarding the output of the decode command.
The registered claims iat, exp and nbf are decoded as timestamps which is fine.

But sometimes, it would be easier to see these dates formatted for human consumption.

Current output:

  "header": {
    "alg": "HS512"
  },
  "payload": {
    "exp": 1606760228,
    "iat": 1606751228
  }
}

So, would a jwt decode --json --iso8601 would make sense to produce something like?:

  "header": {
    "alg": "HS512"
  },
  "payload": {
    "exp": "2020-11-30T18:17:08Z",
    "iat": "2020-11-30T15:47:08Z"
  }
}

Publish to Cargo?

Please cargo publish this package? Looks like the Cargo.toml is good-to-go, just needs a publish.

This would make it possible to install via cargo install jwt-cli on any platform where cargo exists.

After publishing, I'm willing to update the README install section to detail the cargo install option.

`--alg` should be required when performing signature validation

When no algorithm is given during signature validation, HS256 is used by default. This leads to an error message stating that the JWT has a different algorithm than the one the user provided. PR #133 improves on this by showing the algorithm specified in the JWT and the algorithm used for signature validation (HS256 per default) but still, this is not ideal.

I would therefore suggest to require the --alg parameter when a secret is given (-S).

Current behavior:

$ JWT=`cargo run -- encode '{"field":"value"}' --secret 1234567890 --alg 'HS512' --exp '+1 year'`
$ cargo run -- decode -S 1234567890 $JWT 
The JWT provided has a different signing algorithm than the one you provided

Token header
------------
{
  "typ": "JWT",
  "alg": "HS512"
}

Token claims
------------
{
  "exp": 1657552783,
  "field": "value",
  "iat": 1625995831
}

Expected behavior:

$ JWT=`cargo run -- encode '{"field":"value"}' --secret 1234567890 --alg 'HS512' --exp '+1 year'`
$ cargo run -- decode -S 1234567890 $JWT 
error: The following required arguments were not provided:
    --alg <algorithm>

Even better, it would be nice to allow for specifying multiple valid algorithms like this:

$ cargo run -- decode -S 1234567890 --algs HS256,HS384,HS512 $JWT 

HS256 signature fails validation using python-jose

Summary

I'm using jwt-cli to generate a JWT using the default HS256 algorithm, but the token is failing validation using the python-jose module. I'm not sure whether it's the way I'm using the --secret parameter, or the relationship between the --secret value and the JWK's "k" value, or a bug in either code base.

Steps to reproduce

$ token=$(jwt encode --iss self --sub foo --exp "+1Y" --secret secret)

# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTM3MTMxMTgsImlhdCI6MTYyMjE1NjE2NiwiaXNzIjoic2VsZiIsInN1YiI6ImZvbyJ9.fAMgjQScd7xsfOV0-3XvD5K3P7Toc1zLS1ECoeNTEPg

$ jwt decode "$token" --secret secret

Token header
------------
{
  "typ": "JWT",
  "alg": "HS256"
}

Token claims
------------
{
  "exp": 1653713118,
  "iat": 1622156166,
  "iss": "self",
  "sub": "foo"
}

In Python REPL using python-jose following the JWK example:

# Python 3.9.5 (default, May  4 2021, 03:33:11)
# [Clang 12.0.0 (clang-1200.0.32.29)] on darwin
# python-jose==3.2.0

>>> from jose import jwk
>>> from jose.utils import base64url_decode, base64url_encode

>>> token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTM3MTMxMTgsImlhdCI6MTYyMjE1NjE2NiwiaXNzIjoic2VsZiIsInN1YiI6ImZvbyJ9.fAMgjQScd7xsfOV0-3XvD5K3P7Toc1zLS1ECoeNTEPg'

>>> message, encoded_sig = token.rsplit('.', 1)
# message = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTM3MTMxMTgsImlhdCI6MTYyMjE1NjE2NiwiaXNzIjoic2VsZiIsInN1YiI6ImZvbyJ9'
# encoded_sig = 'fAMgjQScd7xsfOV0-3XvD5K3P7Toc1zLS1ECoeNTEPg'

>>> hmac_key = {'alg':'HS256', 'kty':'oct', 'k':'secret'}
>>> key = jwk.construct(hmac_key)

>>> decoded_sig = base64url_decode(encoded_sig.encode())
>>> key.verify(message.encode(), decoded_sig)
# NOT WHAT I EXPECTED!
False

>>> base64url_encode(key.sign(message.encode())).decode()
# jwt-cli produced "fAMgjQScd7xsfOV0-3XvD5K3P7Toc1zLS1ECoeNTEPg"
'6QZYk7mXWP2_Tvoubc1fAytPWfx08UHPaOJZrMWLUdI'

Expected behavior

JWT produced by jwt-cli can be validated by python-jose.

Environment & version

OS: macOS 10.15.7
jwt-cli: 4.0.0

Absent expiry is treated as expired when decoding with secret validation

Summary

When decoding a token with no expiry, and with secret validation on, an error message "The token has expired" is displayed, even though there is no expiration. This message is correctly not displayed when passing no --secret.

(RFC 7519 specifies exp as optional)

Steps to reproduce

  1. Generate a JWT with another tool with no expiration (as jwt does not seem to support absent expirations)
  2. jwt decode --secret THE_SECRET THE_TOKEN
  3. jwt reports something like
    The token has expired
    
    Token header
    ------------
    {
      "typ": "JWT",
      "alg": "HS256"
    }
    
    Token claims
    ------------
    {
      "fresh": false,
      "iat": 1588198314,
      "identity": 1,
      "jti": "c43853a6-a4f8-49ae-a8dc-5b708b833514",
      "nbf": 1588198314,
      "type": "access"
    }
    

Expected behavior

I expect jwt to behave just like it does when calling it with jwt decode THE_TOKEN (no secret validation):


Token header
------------
{
  "typ": "JWT",
  "alg": "HS256"
}

Token claims
------------
{
  "fresh": false,
  "iat": 1588198314,
  "identity": 1,
  "jti": "c43853a6-a4f8-49ae-a8dc-5b708b833514",
  "nbf": 1588198314,
  "type": "access"
}

Optional plain JSON result

Summary

I've had a hard time to read the output of jwt in my programming tool in spite of the -j option, because in error-case it doesn't return plain json as expected:
οΏ½[0mοΏ½[1mοΏ½[31mThe JWT provided is invalid because Error(Base64(InvalidByte(85, 61)))
{
"header": {
"typ": "JWT",
"alg": "ES256",
[...]
Especially the weird color-information in the error should be deactivatable by a new option (like --plaintext oslt).

Steps to reproduce

Run jwt -j with an expired token for instance

Expected behavior

Return plain JSON wrapping the current one with something like that without color codes:
{
"result: {
"error": "The JWT provided is invalid because Error(Base64(InvalidByte(85, 61)))"
}
"header": {
"typ": "JWT",
"alg": "ES256",
[...]

"invalid RSA key" when using RS256

Summary

When I try to encode a JWT using RS256 I get the following error:

Something went awry creating the jwt

invalid RSA key

Steps to reproduce

I've run the following command on a Linux machine (NixOS):

jwt encode --alg RS256 --secret key.pem '{"iss": "ISSUER", "aud": "AUDIENCE"}'

The same command with default algorithm of HS256 is working:

jwt encode --secret key.pem '{"iss": "ISSUER", "aud": "AUDIENCE"}'

I've tried it both with an RSA PEM key using pkcs1 (BEGIN RSA PRIVATE KEY) and pkcs8 (BEGIN PRIVATE KEY):

Expected behavior

JWT is successfully created using RS256 algorithm.

Fix CI/CD

  • Pack stages are wrong; the binaries are jwt not jwt-cli
  • Research how cross builds are done in other projects
  • Try to test locally
  • Add cargo publish step

Feature request: read JWT from stdin

Summary

Please provide an option like - that allows one to to decode jwt from stdin. For example following usecase will be helpful

curl https://some-login-call | jq .token | jwt decode -

Support encoding JWT using JWKS string

Summary

This is related to use cases described in both #119 and #128.

I'd like to run jwt encode by providing my own JWKS instead of a plaintext --secret value. For example:

# jwt encode ... [--secret|--jwks]

$ MY_JWKS='{"keys":[{"alg":"HS256","k":"hS1H1jxz1fgBlXZGvuPxJHOnZxKsoAlvTncmFD-qEkc","key_ops":["sign","verify"],"kid":"MY_KEY_ID","kty":"oct"}]}'

$ jwt encode --kid MY_KEY_ID ... --jwks "$MY_JWKS"

where MY_JWKS can be retrieved from some HTTPS endpoint or generated locally using the jose CLI:

# https://github.com/latchset/jose

$ jose jwk gen -s -i '{"alg":"HS256","kid":"MY_KEY_ID"}'

{"keys":[{"alg":"HS256","k":"hS1H1jxz1fgBlXZGvuPxJHOnZxKsoAlvTncmFD-qEkc","key_ops":["sign","verify"],"kid":"MY_KEY_ID","kty":"oct"}]}

and jwt-cli would use the JWK in the provided keyset by matching the mandatory --kid value.

Besides just adding support for the JWKS standard, it allows using 256-bit (or whatever size) base64-encoded secrets that is in the JWKS k value. The --secret parameter is currently unable to use a non-Unicode string value, throwing the following error if given a binary string:

$ jwt encode --kid MY_KEY_ID ... --secret "$(base64 -d <<< "hS1H1jxz1fgBlXZGvuPxJHOnZxKsoAlvTncmFD-qEkc")"

thread 'main' panicked at 'unexpected invalid UTF-8 code point', /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-2.33.3/src/args/arg_matches.rs:118:40
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Support for piped input would be nice

Summary

I'm finding this utility helpful for debugging JWTs that I'm getting via a pipeline starting with curl, going though jq to extract the JWT, and then sending to jwt-cli. Right now I have to stick xargs in there to provide jwt-cli with the JWT as an argument. It would be nice if it could just accept piped input.

Steps to reproduce

$ curl <curl stuff to auth API> | jq -r .access_token | jwt decode
error: The following required arguments were not provided:
   <jwt>

Instead I have to do:

$ curl <curl stuff to auth API> | jq -r .access_token | xargs jwt decode

Expected behavior:

jwt-cli detects JWT input via STDIN, like in the first case above, and handles it

Claims are in a nested field

Summary

Claims provided to the tool are somehow nested in a _field0 object in the payload

Steps to reproduce

run the tool with payloads provided and preview the JWT at jwt.io

Expected behavior

custom claims are nested at the root level of the JWT's payload object, rather than in a nested field

fails to read secret file with absolute path

Steps to reproduce

Step #1: + ls -lash /workspace/bazel-bot-github-key.pem
Step #1: 4.0K -rw-r--r-- 1 root root 1.7K Feb  1 18:22 /workspace/bazel-bot-github-key.pem
Step #1: ++ jwt encode --secret /workspace/bazel-bot-github-key.pem --iss 97063 --exp '+10 min' --alg RS256
Step #1: thread 'main' panicked at 'Unable to read file workspace/bazel-bot-github-key.pem', src/main.rs:321:44
Step #1: stack backtrace:
Step #1:    0: rust_begin_unwind
Step #1:              at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/std/src/panicking.rs:495:5
Step #1:    1: std::panicking::begin_panic_fmt
Step #1:              at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/std/src/panicking.rs:437:5
Step #1:    2: jwt::slurp_file::{{closure}}
Step #1:    3: jwt::main
Step #1: note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

I suspect the .skip(1) is the cause,

let secret = slurp_file(&secret_string.chars().skip(1).collect::<String>());

but the .skip(1) must be doing something useful that I don't understand.

Cannot use systemd.time strings in the past (for nbf)

Summary

I needed to set the nbf on my JWT a couple seconds in the past to account for clock drift, and I couldn't achieve it through the native jwt command line. I had to resort to arithmetic in bash (e.g. jwt --nbf "$(( $(date +%s) - 3 ))").

Steps to reproduce

$ jwt --version
jwt 5.0.0

$ # Notice that the printed date matches iat, and nbf == iat+5, not iat-5.
$ date +%s; jwt encode --secret foo --nbf='5 seconds ago' | jwt decode -
1641934457

Token header
------------
{
  "typ": "JWT",
  "alg": "HS256"
}

Token claims
------------
{
  "iat": 1641934457,
  "nbf": 1641934462
}

$ jwt encode --secret foo --nbf='-5s'
error: Invalid value for '--nbf <not_before>': must be a UNIX timestamp or systemd.time string

Expected behavior

I thought -5s would be accepted and calculated properly. When that wasn't, then I tried 5 seconds ago, which was accepted, but calculated incorrectly.

I believe man 7 systemd.time says that either of those formats should be accepted:

When parsing, systemd will also accept relative time
specifications. A time span (see above) that is prefixed with "+"
is evaluated to the current time plus the specified time span.
Correspondingly, a time span that is prefixed with "-" is
evaluated to the current time minus the specified time span.
Instead of prefixing the time span with "+" or "-", it may also
be suffixed with a space and the word "left" or "ago".

Array for values in payload

Hi,

It would be greate if arrays of values could be passed. E.g. aud can be an array.
The same should apply for custom payload attributes.

Regards,
Florain

Support for EdDSA (or don't validate the algorithm)

Summary

EdDSA is currently not a supported algorithm, it would be great if it could be, however I know that the algorithm support comes from a dependency. Since that is mostly outside the purview of this library, I will also propose a feature request to add a flag to ignore the algorithm (--ignore-alg or similar). My main use case is to just decode the JWT's payloadβ€”which of course doesn't require validation of the algorithm or the headers at all. It would certainly be nice to have, but I'm wondering if it makes sense to block the ability to decode the payload.

Steps to reproduce

jwt decode "jwt.encoded.with.EdDSA" outputs the following:

The JWT provided is invalid because Error(Json(Error("unknown variant `EdDSA`, expected one of `HS256`, `HS384`, `HS512`, `ES256`, `ES384`, `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`", line: 1, column: 26)))

Expected behavior

The decoded JWT perhaps with a warning instead that the algorithm was not supported OR the ability to add a flag, e.g. jwt decode --ignore-alg "jwt.encoded.with.EdDSA", to decode and validate the payload.

Remove "--prn" option or make it optional on encode

Summary

According to the latest JWT spec (https://tools.ietf.org/html/rfc7519#page-9), the "prn" claim is not even mentioned, and according to an earlier JWT draft spec (https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-05#page-9), the "prn" claim is optional.

Steps to reproduce

On encode, I get an error that the "--prn" required argument was not provided:

error: The following required arguments were not provided:
    --prn <principal>

USAGE:
    jwt encode --alg <algorithm> --aud <audience> --exp <expires> --iss <issuer> --payload <payload>... --prn <principal> --secret <secret> --sub <subject>

Expected behavior

The "--prn" option should either be removed entirely or at least be made optional.

@ prefix to read a binary file cannot be used with HMAC algorithms

Summary

The help text claims that a value starting with @ can be provided to the -S/--secret parameter in order to read the secret from a binary file. However, this is not implemented for the HMAC algorithms (like HS256) - instead, the string starting with @ provided on the command line is used verbatim as the secret, leading to confusing signature validation issues.

This unfortunately leads this tool to encourage the exclusive use of human-readable strings as JWT secrets when using the HMAC modes, which considerably reduces the size of the keyspace.

Steps to reproduce

jwt encode -S @secret.key -a fake -i fake -p fake -s fake

Expected behavior

The bytes contained in the file secret.key are used as the JWT secret.

Actual behavior

The bytes of the string @secret.key are used as the JWT secret.

Using openssl generated ECDSA key

Summary

Hi, I (unsuccessfully) tried to encode a JWT using a ECDSA private key generated with openssl.

Steps to reproduce

openssl ecparam -name secp256r1 -genkey -outform der > key.der
jwt encode -A ES256 -S @key.der
Something went awry creating the jwt

InvalidEcdsaKey

Same with PEM encoding:

openssl ecparam -name secp256r1 -genkey -outform pem > key.pem
jwt encode -A ES256 -S @key.pem
Something went awry creating the jwt

InvalidEcdsaKey

Expected behavior

jwt-cli generates a JWT.

Doesn't properly encode nested records

Summary

Using nested records doesn't get encoded properly.

Steps to reproduce

Run jwt encode -e 1539178753 -n 0 -S "fake" -P admin=false, test:{stuff=things,s=t}

Actual Behavior

Token claims
------------
{
  "admin": "false,",
  "exp": 1539178753,
  "iat": 1539179624,
  "nbf": 0,
  "test:s": "t",
  "test:stuff": "things"
}

Expected behavior

Token claims
------------
{
  "admin": "false,",
  "exp": 1539178753,
  "iat": 1539179624,
  "nbf": 0,
  "test": {"s": "t", "stuff": "things"}
}

Support ed25519 alg

Summary

NATS.io use JWT, as described here https://docs.nats.io/using-nats/developer/tutorials/jwt. The algorithm used is ed25519, sometime written as ed25519-nkey in JWT themselves. Hence jwt goes boom

Steps to reproduce

Here's trying to decode a NATS JWT found in the doc here

~ ᐅ jwt decode eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJPRFhJSVI2Wlg1Q1AzMlFJTFczWFBENEtTSDYzUFNNSEZHUkpaT05DR1RLVVBISlRLQ0JBIiwiaWF0IjoxNTU2NjU1Njk0LCJpc3MiOiJPRFdaSjJLQVBGNzZXT1dNUENKRjZCWTRRSVBMVFVJWTRKSUJMVTRLM1lERzNHSElXQlZXQkhVWiIsIm5hbWUiOiJBIiwic3ViIjoiQUNTVTNRNkxUTEJWTEdBUVVPTkFHWEpIVk5XR1NLS0FVQTdJWTVUQjRaN1BMRUtTUjVPNkpUR1IiLCJ0eXBlIjoiYWNjb3VudCIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJjb25uIjotMSwibGVhZiI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ3aWxkY2FyZHMiOnRydWV9fX0._WW5C1triCh8a4jhyBxEZZP8RJ17pINS8qLzz-01o6zbz1uZfTOJGvwSTS6Yv2_849B9iUXSd-8kp1iMXHdoBA

The JWT provided is invalid because Error(Json(Error("unknown variant `ed25519`, expected one of `HS256`, `HS384`, `HS512`, `ES256`, `ES384`, `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`", line: 1, column: 28)))

Reminder, this one uses ed25519 but instead of ed25519-nkey, but couldn't find an example jwt with the latest in the doc

Expected behavior

I don't mind jwt being unable to understand ed25519-nkey == ed25519, but I wish I could -A ed25519. A tool such as https://www.jstoolset.com/jwt do work, though I prefer a cli like jwt.

New release ? :)

Hello,

Thank you for your JWT CLI tool it's very helpful !
Just a quick note to ask if you could release a new version?

Installing via HomeBrew we still get the 4.0.0 version which misses all the amazing improvements done since last February.
Or should we install it some other way to have the 'latest' build ?

Regards,

Olivier

Remove trailing newline during decode

Summary

When piping the output of jwt encode to jwt decode it fails signature verification due to the newline

Steps to reproduce

jwt encode β€”secret foo β€˜{ β€œbar” : 10 }’ | jwt decode β€”secret foo

Expected behavior

It should pass signature verification

If I store the output of jwt encode in an environment variable and pipe it with β€œecho -n” to jwt decode the verification is fine

Panic with command 'jwt decode help' command

Summary

When invalid order of parameters are supplied: jwt decode help the program panics with error

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(InvalidToken)', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Obviously not a huge issue but maybe not the expected behavior.

Steps to reproduce

See summary

Expected behavior

A more helpful error message be printed.

Add "--jti" option to the encode command

Summary

The "jti" claim is a registered claim name in the latest JWT spec (https://tools.ietf.org/html/rfc7519#page-10), and is used often enough that there should be an option for it in the encode command.

Steps to reproduce

Missing "--jti" option in jwt help encode.

Expected behavior

While it's possible to treat it as a custom claim with --payload "jti=some_id", it should be supported as a first-class option.

Automatically set `iat` and `exp`

Summary

iat should automatically be set to the current time, and exp should be set to a default of 30 minutes.

Steps to reproduce

Expected behavior

superfluous output on stdout doesn't help programmatic usage

Summary

Having displayed

Success! Here's your token                                                                                                                                                  

on stdout seems superfluous. Maybe it shouldn't be displayed at all (or stderr at least), and should return 0 or 1 in case of success or failure respectively, thus following more the UNIX philosophy.

That would allow me to do stuff like:

TOKEN=$( jwt encode -S 'secret' -P role=todo_user)

Steps to reproduce

jwt encode -S 'secret' -P role=todo_user
Success! Here's your token
                                                                                                                                              
<SOME-TOKEN-OUTPUT>

Expected behavior

jwt encode -S 'secret' -P role=todo_user
<SOME-TOKEN-OUTPUT>

Thanks for this CLI! awesome.

Symlink not create by homebrew

Summary

When trying to install via homebrew it seems like the symlink is not created properly.

Steps to reproduce

  1. Install via homebrew
  2. Try jwt help -> command not found

Expected behavior

Symlink is properly created

Unsuccessful with the Windows release

Summary

I am new to JWT so it might be that I am just not using the utility in a proper way. Anyhow, I downloaded the latest Windows release and did not have success so far.

Steps to reproduce

I run the command:
jwt encode --alg RS256 --payload '{"userName":"foobar"}' -S @decrypted-private.pem

It gave error message:
error: Invalid value for '--payload ...': payloads must have a key and value in the form key=value

This is odd, as the JSON should be with colon?

Then I run it like this:
jwt encode --alg RS256 --payload '{"userName"="foobar"}' -S @decrypted-private.pem

And it says:
Something went awry creating the jwt
Invalid RSA key

The decrypted-private.pem is a decrypted version of the 2048 bit RSA private key I generated with OpenSSL.

Expected behavior

Would be happy to be able to encode the jwt.

Support ES256

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(Json(Error("unknown variant `ES256`, expected one of `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`", line: 1, column: 14)))', src/libcore/result.rs:997:5
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:39
   1: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:70
   2: std::panicking::default_hook::{{closure}}
             at src/libstd/sys_common/backtrace.rs:58
             at src/libstd/panicking.rs:200
   3: std::panicking::default_hook
             at src/libstd/panicking.rs:215
   4: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:478
   5: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:385
   6: rust_begin_unwind
             at src/libstd/panicking.rs:312
   7: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
   8: core::result::unwrap_failed
   9: jwt::main
  10: std::rt::lang_start::{{closure}}
  11: std::panicking::try::do_call
             at src/libstd/rt.rs:49
             at src/libstd/panicking.rs:297
  12: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:87
  13: std::rt::lang_start_internal
             at src/libstd/panicking.rs:276
             at src/libstd/panic.rs:388
             at src/libstd/rt.rs:48
  14: main
  15: __libc_start_main
  16: <unknown>

I'm not very well versed in the realm of tokens, but I'm guessing missing support for ECDSA? Would this be difficult to add?

Thanks!

Unintuitive syntax / error message for RSA secret

It was not immediately clear to me that the RSA key had to be read from file and then why it stripped the leading character from the path (regardless which character).

I think it would help to have the "if first character is @" and display an error message in the else clause. This ensures that only '@' is eligible to strip.

let secret = slurp_file(&secret_string.chars().skip(1).collect::<String>());

Thanks for a great tool!

Feature request: `-j` flag for encoding using the output of `decode -j`

decode allows supplying the -j (--json) flag to output JSON in the following format:

{
  "header": {
    "typ": "JWT",
    "alg": "HS256"
  },
  "payload": {
    "exp": 1835241013,
    "iat": 1635240113,
    "sub": "2e5c4600-9fb6-47b4-aff7-f490d23c6074",
    "email": "[email protected]"
  }
}

I propose adding a -j (--json) flag to encode as well which accepts JSON in the same format. That means, options such as typ, alg etc. should be inferred from the header object, as well as payload and so on.

Ideally, this should enable the following workflow:

echo $JWT | jwt decode -j - | modify-token | jwt encode -j -S secret-key -

... where modify-token can be replaced with whatever utility that modifies the JSON.

For (asymmetric) signature algorithms, verification requires a public key, not a secret

Summary

For RSA-/ECC-based signatures, a public key is required for signature verification. Currently, this public key needs to be specified via the --secret parameter which is not intuitive. For RSA-/ECC-based signatures, this parameter should be renamed to --publicKey (or similar).

Steps to reproduce

$ RS256=`cargo run -- encode --alg 'RS256' -S '@./tests/private_rsa_key.der'`
$ cargo run -- decode --alg 'RS256' --secret '@./tests/public_rsa_key.der' $RS256 # <- public key is given via option named `--secret`
...

Expected behavior

# specify public key via `--publicKey` option (for RSA- and ECC-based algorithms)
$ RS256=`cargo run -- encode --alg 'RS256' -S '@./tests/private_rsa_key.der'`
$ cargo run -- decode --alg 'RS256' --publicKey '@./tests/public_rsa_key.der' $RS256
...

# specify secret via `--secret` option (for HMAC-based algorithms)
$ HS256=`cargo run -- encode --alg 'HS256' --secret '1234567890`
$ cargo run -- decode --alg 'HS256' --secret '1234567890` $HS256
...

Provide option to allow --secret argument be interpreted as base-64 encoded bytes

Summary

To simplify use in scripted applications, it would be helpful if there was an option to interpret the --secret argument as a string containing base-64 encoded bytes. Perhaps an additional --base64 or -B option could be used to specify this behavior.

The same outcome can be achieved by writing the bytes of the key to a file and using --secret @filename, but directly supporting base-64 would avoid the need to write the key to a file.

Example

jwt --base64 --secret "CFP7qWsQP/oHNj+i+mxpEf/iYzmlweFHwonE4nlUCtA="

Parsing fails on token with no `typ` attribute in header.

Summary

With a token with the alg specified in the header but no typ, I get the following error:

thread 'main' panicked at 'called Result::unwrap()on anErrvalue: Error(Json(ErrorImpl { code: Message("missing fieldtyp"), line: 1, column: 15 }), State { next_error: None })', src/libcore/result.rs:859

Steps to reproduce

jwt decode eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.SEQijh6tEuOOAAKpHPuKxgFqEvlTNP1jj4FUNoBwXaM

Expected behavior

It should decode and display.

AUR Package

Summary

I have created an AUR package for jwt-cli which can be installed with:

yay -Sy jwt-cli-bin

Can't encode token with .pem secret on Windows

Summary

I am trying to encode a token on Windows using the following command:

jwt encode --alg RS256 --exp "+60 sec" --iss <issuer> --secret=@<key_name>.pem

I am getting the errors:

Something went awry creating the jwt

InvalidKeyFormat

The same key in .pem format works with the same command when I run it on Mac. Any ideas why there is an issue on Windows?

Steps to reproduce

Expected behavior

Equivalent .pem secret works the same on Windows as it does on Mac.

Make exp and iap optional?

Summary

The current behavior of jwt adds exp and iap claims, e.g.

shell>  echo '{"foo": "bar"}'| jwt encode --secret='super-secret-foo' - | jwt decode -                                                                                                  
Token header
------------
{
  "typ": "JWT",
  "alg": "HS256"
}

Token claims
------------
{
  "exp": 1612965158,
  "foo": "bar",
  "iat": 1612963358
}

Steps to reproduce

See above.

Expected behavior

Each of exp and iat is "OPTIONAL" in the JWT spec. I feel the current behavior (addition of exp and iat) is counterintuitive and that it would be more intuitive if exp and iat were only added upon request of the user.

This is, I believe, the most intuitive behavior for the jwt cli:

exp in payload exp not in payload
--exp provided overwrite exp with provided value set exp with provided value
--exp not provided keep exp from payload leave exp empty

I think the same behavior for other optional registered claim names (like iat) would be more intuitive than the current behavior.

(Thanks for this fantastic tool! 🀘)

Incorrect version when upgrading to v3.0.0

Summary

after upgrading via cargo install jwt-cli and giving the command ./jwt -V in ~/.cargo/bin the version is still v2.5.1 instead of the expected v3.0.0

Steps to reproduce

see summary. Platform is Fedora 31 amd64.

Expected behavior

see summary.

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.