Skip to content

Commit

Permalink
feat: Improved error messages/warnings
Browse files Browse the repository at this point in the history
New: If no secret/publicKey is provided by the user, no signature check
is performed. Rather then silently skipping the signature check, this
commit introduces a warning in this case.

New: If the `exp` claim is not set and the token validation check is
skipped, a warning is shown.

New: After a successful signature validation a success message is shown.

Changed: The `InvalidAlgorithm` error message now shows the selected
algorithm and the algorithm specified in the JWT header.

New: If no signature validation is performed, the return code is now
`2`.

All tests have been updated accordingly.
  • Loading branch information
codedust committed Jul 31, 2021
1 parent 783c91a commit 084ffe4
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 49 deletions.
45 changes: 30 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ fn encode_token(matches: &ArgMatches) -> JWTResult<String> {
fn decode_token(
matches: &ArgMatches,
) -> (
JWTResult<TokenData<Payload>>,
Option<JWTResult<TokenData<Payload>>>,
JWTResult<TokenData<Payload>>,
OutputFormat,
) {
Expand Down Expand Up @@ -508,8 +508,12 @@ fn decode_token(

(
match secret {
Some(secret_key) => decode::<Payload>(&jwt, &secret_key.unwrap(), &secret_validator),
None => dangerous_insecure_decode::<Payload>(&jwt),
Some(secret_key) => Some(decode::<Payload>(
&jwt,
&secret_key.unwrap(),
&secret_validator,
)),
None => None, // unable to safely decode token => validated_token is set to None
},
token_data,
if matches.is_present("json") {
Expand Down Expand Up @@ -539,12 +543,13 @@ fn print_encoded_token(token: JWTResult<String>) {
}

fn print_decoded_token(
validated_token: JWTResult<TokenData<Payload>>,
validated_token: Option<JWTResult<TokenData<Payload>>>,
token_data: JWTResult<TokenData<Payload>>,
options_algorithm: Algorithm,
format: OutputFormat,
) {
if let Err(err) = &validated_token {
match err.kind() {
match validated_token {
Some(Err(ref err)) => match err.kind() {
ErrorKind::InvalidToken => {
bunt::println!("{$red+bold}The JWT provided is invalid{/$}")
}
Expand Down Expand Up @@ -572,15 +577,20 @@ fn print_decoded_token(
ErrorKind::ImmatureSignature => bunt::eprintln!(
"{$red+bold}The `nbf` claim is in the future which isn't allowed{/$}"
),
ErrorKind::InvalidAlgorithm => bunt::eprintln!(
"{$red+bold}The JWT provided has a different signing algorithm than the one you \
provided{/$}",
),
ErrorKind::InvalidAlgorithm => {
let jwt_algorithm = match token_data {
Ok(ref token) => token.header.alg,
Err(_) => panic!("Error: Invalid token data."),
};
bunt::eprintln!("{$red+bold}Error: Invalid Signature! The JWT provided has a different signing algorithm ({:?}) than the one selected for validation ({:?}){/$}",jwt_algorithm, options_algorithm)
}
_ => bunt::eprintln!(
"{$red+bold}The JWT provided is invalid because{/$} {:?}",
"{$red+bold}The JWT provided is invalid because {:?}{/$}",
err
),
};
},
Some(Ok(_)) => bunt::eprintln!("{$green+bold}Success! JWT signature is valid!{/$}"),
None => bunt::eprintln!("{$red+bold}Warning! JWT signature has not been validated!{/$}"),
}

match (format, token_data) {
Expand All @@ -597,8 +607,9 @@ fn print_decoded_token(
}

exit(match validated_token {
Err(_) => 1,
Ok(_) => 0,
Some(Ok(_)) => 0, // successful signature check
Some(Err(_)) => 1, // token validation error
None => 2, // no signature check performed
})
}

Expand All @@ -616,7 +627,11 @@ fn main() {
("decode", Some(decode_matches)) => {
let (validated_token, token_data, format) = decode_token(&decode_matches);

print_decoded_token(validated_token, token_data, format);
let options_algorithm = translate_algorithm(SupportedAlgorithms::from_string(
decode_matches.value_of("algorithm").unwrap(),
));

print_decoded_token(validated_token, token_data, options_algorithm, format);
}
_ => (),
}
Expand Down
71 changes: 37 additions & 34 deletions tests/jwt-cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header } = decoded_token.unwrap();
let TokenData { claims, header } = decoded_token.unwrap().unwrap();

assert_eq!(header.alg, Algorithm::HS256);
assert_eq!(header.kid, Some("1234".to_string()));
Expand Down Expand Up @@ -262,9 +262,9 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header: _ } = decoded_token.unwrap();
let TokenData { claims, header: _ } = decoded_token.unwrap().unwrap();
let iat = from_value::<i64>(claims.0["iat"].clone());

assert!(iat.is_ok());
Expand All @@ -284,7 +284,7 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, token_data, _) = decode_token(&decode_matches);

assert!(decoded_token.is_err());
assert!(decoded_token.as_ref().unwrap().is_err());

let TokenData { claims, header: _ } = token_data.unwrap();

Expand All @@ -304,9 +304,9 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header: _ } = decoded_token.unwrap();
let TokenData { claims, header: _ } = decoded_token.unwrap().unwrap();
let exp = from_value::<i64>(claims.0["exp"].clone());

assert!(exp.is_ok());
Expand All @@ -333,9 +333,9 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header: _ } = decoded_token.unwrap();
let TokenData { claims, header: _ } = decoded_token.unwrap().unwrap();

assert!(claims.0.get("iat").is_none());
}
Expand All @@ -361,9 +361,9 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header: _ } = decoded_token.unwrap();
let TokenData { claims, header: _ } = decoded_token.unwrap().unwrap();
let exp_claim = from_value::<i64>(claims.0["exp"].clone());

assert!(exp_claim.is_ok());
Expand All @@ -383,7 +383,7 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_err());
assert!(decoded_token.as_ref().unwrap().is_err());
}

#[test]
Expand All @@ -406,7 +406,7 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());
}

#[test]
Expand All @@ -429,9 +429,9 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header: _ } = decoded_token.unwrap();
let TokenData { claims, header: _ } = decoded_token.unwrap().unwrap();
let exp_claim = from_value::<i64>(claims.0["exp"].clone());
let iat_claim = from_value::<i64>(claims.0["iat"].clone());

Expand Down Expand Up @@ -465,9 +465,9 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, _, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header: _ } = decoded_token.unwrap();
let TokenData { claims, header: _ } = decoded_token.unwrap().unwrap();
let nbf_claim = from_value::<i64>(claims.0["nbf"].clone());
let iat_claim = from_value::<i64>(claims.0["iat"].clone());

Expand Down Expand Up @@ -496,7 +496,7 @@ mod tests {
let decode_matches = matches.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);

assert!(result.is_ok());
assert!(result.unwrap().is_ok());
}

#[test]
Expand All @@ -510,9 +510,10 @@ mod tests {
])
.unwrap();
let decode_matches = matches.subcommand_matches("decode").unwrap();
let (result, _, format) = decode_token(&decode_matches);
let (validated_token, token_data, format) = decode_token(&decode_matches);

assert!(result.is_ok());
assert!(validated_token.is_none()); // no signature validation
assert!(token_data.is_ok());
assert!(format == OutputFormat::Json);
}

Expand All @@ -532,7 +533,7 @@ mod tests {
let decode_matches = matches.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);

assert!(result.is_err());
assert!(result.unwrap().is_err());
}

#[test]
Expand All @@ -547,9 +548,10 @@ mod tests {
])
.unwrap();
let decode_matches = matches.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);
let (validated_token, token_data, _) = decode_token(&decode_matches);

assert!(result.is_ok());
assert!(validated_token.is_none()); // no signature validation
assert!(token_data.is_ok());
}

#[test]
Expand All @@ -562,9 +564,10 @@ mod tests {
])
.unwrap();
let decode_matches = matches.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);
let (validated_token, token_data, _) = decode_token(&decode_matches);

assert!(result.is_ok());
assert!(validated_token.is_none()); // no signature validation
assert!(token_data.is_ok());
}

#[test]
Expand All @@ -577,9 +580,10 @@ mod tests {
])
.unwrap();
let decode_matches = matches.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);
let (validated_token, token_data, _) = decode_token(&decode_matches);

assert!(result.is_ok());
assert!(validated_token.is_none()); // no signature validation
assert!(token_data.is_ok());
}

#[test]
Expand All @@ -592,9 +596,10 @@ mod tests {
])
.unwrap();
let decode_matches = matches.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);
let (validated_token, token_data, _) = decode_token(&decode_matches);

assert!(result.is_ok());
assert!(validated_token.is_none()); // no signature validation
assert!(token_data.is_ok());
}

#[test]
Expand Down Expand Up @@ -628,7 +633,7 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);

assert!(result.is_ok());
assert!(result.unwrap().is_ok());
}

#[test]
Expand Down Expand Up @@ -676,9 +681,7 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (result, _, _) = decode_token(&decode_matches);

dbg!(&result);

assert!(result.is_ok());
assert!(result.unwrap().is_ok());
}

#[test]
Expand Down Expand Up @@ -712,7 +715,7 @@ mod tests {
let decode_matches = decode_matcher.subcommand_matches("decode").unwrap();
let (decoded_token, token_data, _) = decode_token(&decode_matches);

assert!(decoded_token.is_ok());
assert!(decoded_token.as_ref().unwrap().is_ok());

let TokenData { claims, header: _ } = token_data.unwrap();

Expand Down

0 comments on commit 084ffe4

Please sign in to comment.