Unify various decode into the validation struct (#199)
This commit is contained in:
parent
6a7eec9030
commit
1dcfda92f0
|
@ -8,6 +8,7 @@
|
|||
- Remove deprecated `dangerous_unsafe_decode`
|
||||
- `Validation::iss` is now a `HashSet` instead of a single value
|
||||
- `decode` will now error if `Validation::algorithms` is empty
|
||||
- Add JWKs types for easy interop with various Oauth providers
|
||||
|
||||
## 7.2.0 (2020-06-30)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use chrono::prelude::*;
|
||||
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation};
|
||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const SECRET: &str = "some-secret";
|
||||
|
@ -56,7 +56,9 @@ mod jwt_numeric_date {
|
|||
|
||||
use super::super::{Claims, SECRET};
|
||||
use chrono::{Duration, TimeZone, Utc};
|
||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
||||
use jsonwebtoken::{
|
||||
decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
|
@ -75,7 +77,7 @@ mod jwt_numeric_date {
|
|||
let decoded = decode::<Claims>(
|
||||
&token,
|
||||
&DecodingKey::from_secret(SECRET.as_ref()),
|
||||
&Validation::default(),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
)
|
||||
.expect("Failed to decode token");
|
||||
|
||||
|
@ -90,7 +92,7 @@ mod jwt_numeric_date {
|
|||
let decode_result = decode::<Claims>(
|
||||
&overflow_token,
|
||||
&DecodingKey::from_secret(SECRET.as_ref()),
|
||||
&Validation::default(),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
);
|
||||
|
||||
assert!(decode_result.is_err());
|
||||
|
@ -111,7 +113,7 @@ mod jwt_numeric_date {
|
|||
let decoded = decode::<Claims>(
|
||||
&token,
|
||||
&DecodingKey::from_secret(SECRET.as_ref()),
|
||||
&Validation::default(),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
)
|
||||
.expect("Failed to decode token")
|
||||
.claims;
|
||||
|
@ -139,7 +141,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let token_data = jsonwebtoken::decode::<Claims>(
|
||||
&token,
|
||||
&DecodingKey::from_secret(SECRET.as_ref()),
|
||||
&Validation::default(),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
)?;
|
||||
|
||||
println!("token data:\n{:#?}", &token_data);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use jsonwebtoken::errors::ErrorKind;
|
||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
||||
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -18,7 +18,8 @@ fn main() {
|
|||
Err(_) => panic!(), // in practice you would return the error
|
||||
};
|
||||
|
||||
let validation = Validation { sub: Some("b@b.com".to_string()), ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.sub = Some("b@b.com".to_string());
|
||||
let token_data = match decode::<Claims>(&token, &DecodingKey::from_secret(key), &validation) {
|
||||
Ok(c) => c,
|
||||
Err(err) => match *err.kind() {
|
||||
|
|
|
@ -140,26 +140,20 @@ impl DecodingKey {
|
|||
/// Verify signature of a JWT, and return header object and raw payload
|
||||
///
|
||||
/// If the token or its signature is invalid, it will return an error.
|
||||
///
|
||||
/// ```rust///
|
||||
/// use jsonwebtoken::{verify_signature, DecodingKey, Validation, Algorithm};
|
||||
///
|
||||
///
|
||||
/// let token = "a.jwt.token".to_string();
|
||||
/// let token_message = verify_signature(&token, &DecodingKey::from_secret("secret".as_ref()), &Validation::new(Algorithm::HS256));
|
||||
/// ```
|
||||
pub fn verify_signature<'a>(
|
||||
fn verify_signature<'a>(
|
||||
token: &'a str,
|
||||
key: &DecodingKey,
|
||||
validation: &Validation,
|
||||
) -> Result<(Header, &'a str)> {
|
||||
if validation.algorithms.is_empty() {
|
||||
if validation.validate_signature && validation.algorithms.is_empty() {
|
||||
return Err(new_error(ErrorKind::MissingAlgorithm));
|
||||
}
|
||||
|
||||
for alg in &validation.algorithms {
|
||||
if key.family != alg.family() {
|
||||
return Err(new_error(ErrorKind::InvalidAlgorithm));
|
||||
if validation.validate_signature {
|
||||
for alg in &validation.algorithms {
|
||||
if key.family != alg.family() {
|
||||
return Err(new_error(ErrorKind::InvalidAlgorithm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,11 +161,11 @@ pub fn verify_signature<'a>(
|
|||
let (payload, header) = expect_two!(message.rsplitn(2, '.'));
|
||||
let header = Header::from_encoded(header)?;
|
||||
|
||||
if !validation.algorithms.contains(&header.alg) {
|
||||
if validation.validate_signature && !validation.algorithms.contains(&header.alg) {
|
||||
return Err(new_error(ErrorKind::InvalidAlgorithm));
|
||||
}
|
||||
|
||||
if !verify(signature, message.as_bytes(), key, header.alg)? {
|
||||
if validation.validate_signature && !verify(signature, message.as_bytes(), key, header.alg)? {
|
||||
return Err(new_error(ErrorKind::InvalidSignature));
|
||||
}
|
||||
|
||||
|
@ -212,72 +206,6 @@ pub fn decode<T: DeserializeOwned>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Decode a JWT without any signature verification/validations.
|
||||
///
|
||||
/// NOTE: Do not use this unless you know what you are doing! If the token's signature is invalid, it will *not* return an error.
|
||||
///
|
||||
/// ```rust
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use jsonwebtoken::{dangerous_insecure_decode, Validation, Algorithm};
|
||||
///
|
||||
/// #[derive(Debug, Serialize, Deserialize)]
|
||||
/// struct Claims {
|
||||
/// sub: String,
|
||||
/// company: String
|
||||
/// }
|
||||
///
|
||||
/// let token = "a.jwt.token".to_string();
|
||||
/// // Claims is a struct that implements Deserialize
|
||||
/// let token_message = dangerous_insecure_decode::<Claims>(&token);
|
||||
/// ```
|
||||
pub fn dangerous_insecure_decode<T: DeserializeOwned>(token: &str) -> Result<TokenData<T>> {
|
||||
let (_, message) = expect_two!(token.rsplitn(2, '.'));
|
||||
let (claims, header) = expect_two!(message.rsplitn(2, '.'));
|
||||
let header = Header::from_encoded(header)?;
|
||||
|
||||
let (decoded_claims, _): (T, _) = from_jwt_part_claims(claims)?;
|
||||
|
||||
Ok(TokenData { header, claims: decoded_claims })
|
||||
}
|
||||
|
||||
/// Decode and validate a JWT without any signature verification.
|
||||
///
|
||||
/// If the token is invalid or the claims fail validation, it will return an error.
|
||||
///
|
||||
/// NOTE: Do not use this unless you know what you are doing! If the token's signature is invalid, it will *not* return an error.
|
||||
///
|
||||
/// ```rust
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use jsonwebtoken::{dangerous_insecure_decode_with_validation, Validation, Algorithm};
|
||||
///
|
||||
/// #[derive(Debug, Serialize, Deserialize)]
|
||||
/// struct Claims {
|
||||
/// sub: String,
|
||||
/// company: String
|
||||
/// }
|
||||
///
|
||||
/// let token = "a.jwt.token";
|
||||
/// // Claims is a struct that implements Deserialize
|
||||
/// let token_message = dangerous_insecure_decode_with_validation::<Claims>(&token, &Validation::new(Algorithm::HS256));
|
||||
/// ```
|
||||
pub fn dangerous_insecure_decode_with_validation<T: DeserializeOwned>(
|
||||
token: &str,
|
||||
validation: &Validation,
|
||||
) -> Result<TokenData<T>> {
|
||||
let (_, message) = expect_two!(token.rsplitn(2, '.'));
|
||||
let (claims, header) = expect_two!(message.rsplitn(2, '.'));
|
||||
let header = Header::from_encoded(header)?;
|
||||
|
||||
if !validation.algorithms.contains(&header.alg) {
|
||||
return Err(new_error(ErrorKind::InvalidAlgorithm));
|
||||
}
|
||||
|
||||
let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?;
|
||||
validate(&claims_map, validation)?;
|
||||
|
||||
Ok(TokenData { header, claims: decoded_claims })
|
||||
}
|
||||
|
||||
/// Decode a JWT without any signature verification/validations and return its [Header](struct.Header.html).
|
||||
///
|
||||
/// If the token has an invalid format (ie 3 parts separated by a `.`), it will return an error.
|
||||
|
|
|
@ -17,10 +17,7 @@ mod serialization;
|
|||
mod validation;
|
||||
|
||||
pub use algorithms::Algorithm;
|
||||
pub use decoding::{
|
||||
dangerous_insecure_decode, dangerous_insecure_decode_with_validation, decode, decode_header,
|
||||
verify_signature, DecodingKey, TokenData,
|
||||
};
|
||||
pub use decoding::{decode, decode_header, DecodingKey, TokenData};
|
||||
pub use encoding::{encode, EncodingKey};
|
||||
pub use header::Header;
|
||||
pub use validation::{get_current_timestamp, Validation};
|
||||
|
|
|
@ -12,18 +12,16 @@ use crate::errors::{new_error, ErrorKind, Result};
|
|||
/// All time validation happen on UTC timestamps as seconds.
|
||||
///
|
||||
/// ```rust
|
||||
/// use jsonwebtoken::Validation;
|
||||
///
|
||||
/// // Default value
|
||||
/// let validation = Validation::default();
|
||||
///
|
||||
/// // Changing one parameter
|
||||
/// let mut validation = Validation {leeway: 60, ..Default::default()};
|
||||
/// use jsonwebtoken::{Validation, Algorithm};
|
||||
///
|
||||
/// let mut validation = Validation::new(Algorithm::HS256);
|
||||
/// validation.leeway = 5;
|
||||
/// // Setting audience
|
||||
/// let mut validation = Validation::default();
|
||||
/// validation.set_audience(&["Me"]); // a single string
|
||||
/// validation.set_audience(&["Me", "You"]); // array of strings
|
||||
/// // or issuer
|
||||
/// validation.set_iss(&["Me"]); // a single string
|
||||
/// validation.set_iss(&["Me", "You"]); // array of strings
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Validation {
|
||||
|
@ -64,12 +62,27 @@ pub struct Validation {
|
|||
///
|
||||
/// Defaults to `vec![Algorithm::HS256]`.
|
||||
pub algorithms: Vec<Algorithm>,
|
||||
|
||||
/// Whether to validate the JWT signature. Very insecure to turn that off
|
||||
pub(crate) validate_signature: bool,
|
||||
}
|
||||
|
||||
impl Validation {
|
||||
/// Create a default validation setup allowing the given alg
|
||||
pub fn new(alg: Algorithm) -> Validation {
|
||||
Validation { algorithms: vec![alg], ..Default::default() }
|
||||
Validation {
|
||||
algorithms: vec![alg],
|
||||
leeway: 0,
|
||||
|
||||
validate_exp: true,
|
||||
validate_nbf: false,
|
||||
|
||||
iss: None,
|
||||
sub: None,
|
||||
aud: None,
|
||||
|
||||
validate_signature: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// `aud` is a collection of one or more acceptable audience members
|
||||
|
@ -81,22 +94,12 @@ impl Validation {
|
|||
pub fn set_iss<T: ToString>(&mut self, items: &[T]) {
|
||||
self.iss = Some(items.iter().map(|x| x.to_string()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Validation {
|
||||
fn default() -> Validation {
|
||||
Validation {
|
||||
leeway: 0,
|
||||
|
||||
validate_exp: true,
|
||||
validate_nbf: false,
|
||||
|
||||
iss: None,
|
||||
sub: None,
|
||||
aud: None,
|
||||
|
||||
algorithms: vec![Algorithm::HS256],
|
||||
}
|
||||
/// Whether to validate the JWT cryptographic signature
|
||||
/// Very insecure to turn that off, only do it if you know what you're doing.
|
||||
/// With this flag turned off, you should not trust any of the values of the claims.
|
||||
pub fn insecure_disable_signature_validation(&mut self) {
|
||||
self.validate_signature = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,12 +193,13 @@ mod tests {
|
|||
use super::{get_current_timestamp, validate, Validation};
|
||||
|
||||
use crate::errors::ErrorKind;
|
||||
use crate::Algorithm;
|
||||
|
||||
#[test]
|
||||
fn exp_in_future_ok() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("exp".to_string(), to_value(get_current_timestamp() + 10000).unwrap());
|
||||
let res = validate(&claims, &Validation::default());
|
||||
let res = validate(&claims, &Validation::new(Algorithm::HS256));
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
|
@ -203,7 +207,7 @@ mod tests {
|
|||
fn exp_in_past_fails() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("exp".to_string(), to_value(get_current_timestamp() - 100000).unwrap());
|
||||
let res = validate(&claims, &Validation::default());
|
||||
let res = validate(&claims, &Validation::new(Algorithm::HS256));
|
||||
assert!(res.is_err());
|
||||
|
||||
match res.unwrap_err().kind() {
|
||||
|
@ -216,7 +220,8 @@ mod tests {
|
|||
fn exp_in_past_but_in_leeway_ok() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("exp".to_string(), to_value(get_current_timestamp() - 500).unwrap());
|
||||
let validation = Validation { leeway: 1000 * 60, ..Default::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.leeway = 1000 * 60;
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
@ -225,7 +230,7 @@ mod tests {
|
|||
#[test]
|
||||
fn validation_called_even_if_field_is_empty() {
|
||||
let claims = Map::new();
|
||||
let res = validate(&claims, &Validation::default());
|
||||
let res = validate(&claims, &Validation::new(Algorithm::HS256));
|
||||
assert!(res.is_err());
|
||||
match res.unwrap_err().kind() {
|
||||
ErrorKind::ExpiredSignature => (),
|
||||
|
@ -237,8 +242,9 @@ mod tests {
|
|||
fn nbf_in_past_ok() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("nbf".to_string(), to_value(get_current_timestamp() - 10000).unwrap());
|
||||
let validation =
|
||||
Validation { validate_exp: false, validate_nbf: true, ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.validate_nbf = true;
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
@ -247,8 +253,9 @@ mod tests {
|
|||
fn nbf_in_future_fails() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("nbf".to_string(), to_value(get_current_timestamp() + 100000).unwrap());
|
||||
let validation =
|
||||
Validation { validate_exp: false, validate_nbf: true, ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.validate_nbf = true;
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
||||
|
@ -262,12 +269,10 @@ mod tests {
|
|||
fn nbf_in_future_but_in_leeway_ok() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("nbf".to_string(), to_value(get_current_timestamp() + 500).unwrap());
|
||||
let validation = Validation {
|
||||
leeway: 1000 * 60,
|
||||
validate_nbf: true,
|
||||
validate_exp: false,
|
||||
..Default::default()
|
||||
};
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.validate_nbf = true;
|
||||
validation.leeway = 1000 * 60;
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
@ -277,10 +282,9 @@ mod tests {
|
|||
let mut claims = Map::new();
|
||||
claims.insert("iss".to_string(), to_value("Keats").unwrap());
|
||||
|
||||
let mut iss = std::collections::HashSet::new();
|
||||
iss.insert("Keats".to_string());
|
||||
|
||||
let validation = Validation { validate_exp: false, iss: Some(iss), ..Default::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_iss(&["Keats"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
@ -290,10 +294,9 @@ mod tests {
|
|||
let mut claims = Map::new();
|
||||
claims.insert("iss".to_string(), to_value("Hacked").unwrap());
|
||||
|
||||
let mut iss = std::collections::HashSet::new();
|
||||
iss.insert("Keats".to_string());
|
||||
|
||||
let validation = Validation { validate_exp: false, iss: Some(iss), ..Default::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_iss(&["Keats"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
||||
|
@ -307,10 +310,9 @@ mod tests {
|
|||
fn iss_missing_fails() {
|
||||
let claims = Map::new();
|
||||
|
||||
let mut iss = std::collections::HashSet::new();
|
||||
iss.insert("Keats".to_string());
|
||||
|
||||
let validation = Validation { validate_exp: false, iss: Some(iss), ..Default::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_iss(&["Keats"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
||||
|
@ -324,11 +326,10 @@ mod tests {
|
|||
fn sub_ok() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("sub".to_string(), to_value("Keats").unwrap());
|
||||
let validation = Validation {
|
||||
validate_exp: false,
|
||||
sub: Some("Keats".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.sub = Some("Keats".to_owned());
|
||||
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
@ -337,11 +338,10 @@ mod tests {
|
|||
fn sub_not_matching_fails() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("sub".to_string(), to_value("Hacked").unwrap());
|
||||
let validation = Validation {
|
||||
validate_exp: false,
|
||||
sub: Some("Keats".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.sub = Some("Keats".to_owned());
|
||||
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
||||
|
@ -354,11 +354,10 @@ mod tests {
|
|||
#[test]
|
||||
fn sub_missing_fails() {
|
||||
let claims = Map::new();
|
||||
let validation = Validation {
|
||||
validate_exp: false,
|
||||
sub: Some("Keats".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.sub = Some("Keats".to_owned());
|
||||
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
||||
|
@ -372,7 +371,8 @@ mod tests {
|
|||
fn aud_string_ok() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("aud".to_string(), to_value(["Everyone"]).unwrap());
|
||||
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_audience(&["Everyone"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_ok());
|
||||
|
@ -382,7 +382,8 @@ mod tests {
|
|||
fn aud_array_of_string_ok() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("aud".to_string(), to_value(["UserA", "UserB"]).unwrap());
|
||||
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_audience(&["UserA", "UserB"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_ok());
|
||||
|
@ -392,7 +393,8 @@ mod tests {
|
|||
fn aud_type_mismatch_fails() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("aud".to_string(), to_value(["Everyone"]).unwrap());
|
||||
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_audience(&["UserA", "UserB"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
@ -407,7 +409,8 @@ mod tests {
|
|||
fn aud_correct_type_not_matching_fails() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("aud".to_string(), to_value(["Everyone"]).unwrap());
|
||||
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_audience(&["None"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
@ -421,7 +424,8 @@ mod tests {
|
|||
#[test]
|
||||
fn aud_missing_fails() {
|
||||
let claims = Map::new();
|
||||
let mut validation = Validation { validate_exp: false, ..Validation::default() };
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_audience(&["None"]);
|
||||
let res = validate(&claims, &validation);
|
||||
assert!(res.is_err());
|
||||
|
@ -434,22 +438,17 @@ mod tests {
|
|||
|
||||
// https://github.com/Keats/jsonwebtoken/issues/51
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn does_validation_in_right_order() {
|
||||
let mut claims = Map::new();
|
||||
claims.insert("exp".to_string(), to_value(get_current_timestamp() + 10000).unwrap());
|
||||
|
||||
let mut iss = std::collections::HashSet::new();
|
||||
iss.insert("iss no check".to_string());
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.leeway = 5;
|
||||
validation.set_iss(&["iss no check"]);
|
||||
validation.set_audience(&["iss no check"]);
|
||||
|
||||
let res = validate(&claims, &validation);
|
||||
|
||||
let v = Validation {
|
||||
leeway: 5,
|
||||
validate_exp: true,
|
||||
iss: Some(iss),
|
||||
sub: Some("sub no check".to_string()),
|
||||
..Validation::default()
|
||||
};
|
||||
let res = validate(&claims, &v);
|
||||
// It errors because it needs to validate iss/sub which are missing
|
||||
assert!(res.is_err());
|
||||
match res.unwrap_err().kind() {
|
||||
|
@ -470,9 +469,10 @@ mod tests {
|
|||
let aud = "my-googleclientid1234.apps.googleusercontent.com".to_string();
|
||||
let mut aud_hashset = std::collections::HashSet::new();
|
||||
aud_hashset.insert(aud);
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.validate_exp = false;
|
||||
validation.set_audience(&["my-googleclientid1234.apps.googleusercontent.com"]);
|
||||
|
||||
let validation =
|
||||
Validation { aud: Some(aud_hashset), validate_exp: false, ..Validation::default() };
|
||||
let res = validate(&claims, &validation);
|
||||
println!("{:?}", res);
|
||||
assert!(res.is_ok());
|
||||
|
|
105
tests/hmac.rs
105
tests/hmac.rs
|
@ -1,9 +1,8 @@
|
|||
use chrono::Utc;
|
||||
use jsonwebtoken::dangerous_insecure_decode_with_validation;
|
||||
use jsonwebtoken::errors::ErrorKind;
|
||||
use jsonwebtoken::{
|
||||
crypto::{sign, verify},
|
||||
dangerous_insecure_decode, decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey,
|
||||
Header, Validation,
|
||||
decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -39,9 +38,12 @@ fn encode_with_custom_header() {
|
|||
};
|
||||
let header = Header { kid: Some("kid".to_string()), ..Default::default() };
|
||||
let token = encode(&header, &my_claims, &EncodingKey::from_secret(b"secret")).unwrap();
|
||||
let token_data =
|
||||
decode::<Claims>(&token, &DecodingKey::from_secret(b"secret"), &Validation::default())
|
||||
.unwrap();
|
||||
let token_data = decode::<Claims>(
|
||||
&token,
|
||||
&DecodingKey::from_secret(b"secret"),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(my_claims, token_data.claims);
|
||||
assert_eq!("kid", token_data.header.kid.unwrap());
|
||||
}
|
||||
|
@ -55,9 +57,12 @@ fn round_trip_claim() {
|
|||
};
|
||||
let token =
|
||||
encode(&Header::default(), &my_claims, &EncodingKey::from_secret(b"secret")).unwrap();
|
||||
let token_data =
|
||||
decode::<Claims>(&token, &DecodingKey::from_secret(b"secret"), &Validation::default())
|
||||
.unwrap();
|
||||
let token_data = decode::<Claims>(
|
||||
&token,
|
||||
&DecodingKey::from_secret(b"secret"),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(my_claims, token_data.claims);
|
||||
assert!(token_data.header.kid.is_none());
|
||||
}
|
||||
|
@ -65,8 +70,11 @@ fn round_trip_claim() {
|
|||
#[test]
|
||||
fn decode_token() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98";
|
||||
let claims =
|
||||
decode::<Claims>(token, &DecodingKey::from_secret(b"secret"), &Validation::default());
|
||||
let claims = decode::<Claims>(
|
||||
token,
|
||||
&DecodingKey::from_secret(b"secret"),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
);
|
||||
println!("{:?}", claims);
|
||||
claims.unwrap();
|
||||
}
|
||||
|
@ -75,8 +83,11 @@ fn decode_token() {
|
|||
#[should_panic(expected = "InvalidToken")]
|
||||
fn decode_token_missing_parts() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
|
||||
let claims =
|
||||
decode::<Claims>(token, &DecodingKey::from_secret(b"secret"), &Validation::default());
|
||||
let claims = decode::<Claims>(
|
||||
token,
|
||||
&DecodingKey::from_secret(b"secret"),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
);
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
|
@ -85,8 +96,11 @@ fn decode_token_missing_parts() {
|
|||
fn decode_token_invalid_signature() {
|
||||
let token =
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong";
|
||||
let claims =
|
||||
decode::<Claims>(token, &DecodingKey::from_secret(b"secret"), &Validation::default());
|
||||
let claims = decode::<Claims>(
|
||||
token,
|
||||
&DecodingKey::from_secret(b"secret"),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
);
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
|
@ -117,8 +131,11 @@ fn encode_wrong_alg_family() {
|
|||
#[test]
|
||||
fn decode_token_with_bytes_secret() {
|
||||
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.Hm0yvKH25TavFPz7J_coST9lZFYH1hQo0tvhvImmaks";
|
||||
let claims =
|
||||
decode::<Claims>(token, &DecodingKey::from_secret(b"\x01\x02\x03"), &Validation::default());
|
||||
let claims = decode::<Claims>(
|
||||
token,
|
||||
&DecodingKey::from_secret(b"\x01\x02\x03"),
|
||||
&Validation::new(Algorithm::HS256),
|
||||
);
|
||||
assert!(claims.is_ok());
|
||||
}
|
||||
|
||||
|
@ -131,60 +148,38 @@ fn decode_header_only() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn dangerous_insecure_decode_token() {
|
||||
fn dangerous_insecure_decode_valid_token() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98";
|
||||
let claims = dangerous_insecure_decode::<Claims>(token);
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "InvalidToken")]
|
||||
fn dangerous_insecure_decode_token_missing_parts() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
|
||||
let claims = dangerous_insecure_decode::<Claims>(token);
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.insecure_disable_signature_validation();
|
||||
let claims = decode::<Claims>(token, &DecodingKey::from_secret(&[]), &validation);
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dangerous_insecure_decode_token_invalid_signature() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.wrong";
|
||||
let claims = dangerous_insecure_decode::<Claims>(token);
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.insecure_disable_signature_validation();
|
||||
let claims = decode::<Claims>(token, &DecodingKey::from_secret(&[]), &validation);
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dangerous_insecure_decode_token_wrong_algorithm() {
|
||||
let token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.fLxey-hxAKX5rNHHIx1_Ch0KmrbiuoakDVbsJjLWrx8fbjKjrPuWMYEJzTU3SBnYgnZokC-wqSdqckXUOunC-g";
|
||||
let claims = dangerous_insecure_decode::<Claims>(token);
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.insecure_disable_signature_validation();
|
||||
let claims = decode::<Claims>(token, &DecodingKey::from_secret(&[]), &validation);
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dangerous_insecure_decode_token_with_validation() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98";
|
||||
let claims = dangerous_insecure_decode_with_validation::<Claims>(token, &Validation::default());
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "InvalidToken")]
|
||||
fn dangerous_insecure_decode_token_with_validation_missing_parts() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
|
||||
let claims = dangerous_insecure_decode_with_validation::<Claims>(token, &Validation::default());
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dangerous_insecure_decode_token_with_validation_invalid_signature() {
|
||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.wrong";
|
||||
let claims = dangerous_insecure_decode_with_validation::<Claims>(token, &Validation::default());
|
||||
claims.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "InvalidAlgorithm")]
|
||||
fn dangerous_insecure_decode_token_with_validation_wrong_algorithm() {
|
||||
let token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.fLxey-hxAKX5rNHHIx1_Ch0KmrbiuoakDVbsJjLWrx8fbjKjrPuWMYEJzTU3SBnYgnZokC-wqSdqckXUOunC-g";
|
||||
let claims = dangerous_insecure_decode_with_validation::<Claims>(token, &Validation::default());
|
||||
claims.unwrap();
|
||||
let token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjk1MzI1MjQ4OX0.ONtEUTtP1QmyksYH9ijtPCaXoHjZVHcHKZGX1DuJyPiSyKlT93Y-oKgrp_OSkHSu4huxCcVObLzwsdwF-xwiAQ";
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.insecure_disable_signature_validation();
|
||||
let claims = decode::<Claims>(token, &DecodingKey::from_secret(&[]), &validation);
|
||||
let err = claims.unwrap_err();
|
||||
assert_eq!(err.kind(), &ErrorKind::ExpiredSignature);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue