Fix Broken chrono Example (#126)
* Add quickcheck as test dependencie * Use timestamp nanos * Add test property for en/decoding * Fix existing test * Remove quickcheck dependency * Normalize `DateTime` values in claims
This commit is contained in:
parent
fa05ece73c
commit
09aad3ca2f
|
@ -13,6 +13,19 @@ struct Claims {
|
||||||
exp: DateTime<Utc>,
|
exp: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Claims {
|
||||||
|
/// If a token should always be equal to its representation after serializing and deserializing
|
||||||
|
/// again, this function must be used for construction. `DateTime` contains a microsecond field
|
||||||
|
/// but JWT timestamps are defined as UNIX timestamps (seconds). This function normalizes the
|
||||||
|
/// timestamps.
|
||||||
|
pub fn new(sub: String, iat: DateTime<Utc>, exp: DateTime<Utc>) -> Self {
|
||||||
|
// normalize the timestamps by stripping of microseconds
|
||||||
|
let iat = iat.date().and_hms_milli(iat.hour(), iat.minute(), iat.second(), 0);
|
||||||
|
let exp = exp.date().and_hms_milli(exp.hour(), exp.minute(), exp.second(), 0);
|
||||||
|
Self { sub, iat, exp }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod jwt_numeric_date {
|
mod jwt_numeric_date {
|
||||||
//! Custom serialization of DateTime<Utc> to conform with the JWT spec (RFC 7519 section 2, "Numeric Date")
|
//! Custom serialization of DateTime<Utc> to conform with the JWT spec (RFC 7519 section 2, "Numeric Date")
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
@ -42,6 +55,8 @@ mod jwt_numeric_date {
|
||||||
const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gRGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.RTgha0S53MjPC2pMA4e2oMzaBxSY3DMjiYR2qFfV55A";
|
const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gRGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.RTgha0S53MjPC2pMA4e2oMzaBxSY3DMjiYR2qFfV55A";
|
||||||
|
|
||||||
use super::super::{Claims, SECRET};
|
use super::super::{Claims, SECRET};
|
||||||
|
use chrono::{Duration, TimeZone, Utc};
|
||||||
|
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip() {
|
fn round_trip() {
|
||||||
|
@ -49,7 +64,7 @@ mod jwt_numeric_date {
|
||||||
let iat = Utc.timestamp(0, 0);
|
let iat = Utc.timestamp(0, 0);
|
||||||
let exp = Utc.timestamp(32503680000, 0);
|
let exp = Utc.timestamp(32503680000, 0);
|
||||||
|
|
||||||
let claims = Claims { sub: sub.clone(), iat, exp };
|
let claims = Claims::new(sub.clone(), iat, exp);
|
||||||
|
|
||||||
let token =
|
let token =
|
||||||
encode(&Header::default(), &claims, &EncodingKey::from_secret(SECRET.as_ref()))
|
encode(&Header::default(), &claims, &EncodingKey::from_secret(SECRET.as_ref()))
|
||||||
|
@ -72,11 +87,37 @@ mod jwt_numeric_date {
|
||||||
// A token with the expiry of i64::MAX + 1
|
// A token with the expiry of i64::MAX + 1
|
||||||
let overflow_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gRGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjkyMjMzNzIwMzY4NTQ3NzYwMDB9.G2PKreA27U8_xOwuIeCYXacFYeR46f9FyENIZfCrvEc";
|
let overflow_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gRGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjkyMjMzNzIwMzY4NTQ3NzYwMDB9.G2PKreA27U8_xOwuIeCYXacFYeR46f9FyENIZfCrvEc";
|
||||||
|
|
||||||
let decode_result =
|
let decode_result = decode::<Claims>(
|
||||||
decode::<Claims>(&overflow_token, SECRET.as_ref(), &Validation::default());
|
&overflow_token,
|
||||||
|
&DecodingKey::from_secret(SECRET.as_ref()),
|
||||||
|
&Validation::default(),
|
||||||
|
);
|
||||||
|
|
||||||
assert!(decode_result.is_err());
|
assert!(decode_result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_token_and_parse_equals_identity() {
|
||||||
|
let iat = Utc::now();
|
||||||
|
let exp = iat + Duration::days(1);
|
||||||
|
let sub = "Custom DateTime ser/de".to_string();
|
||||||
|
|
||||||
|
let claims = Claims::new(sub.clone(), iat, exp);
|
||||||
|
|
||||||
|
let token =
|
||||||
|
encode(&Header::default(), &claims, &EncodingKey::from_secret(SECRET.as_ref()))
|
||||||
|
.expect("Failed to encode claims");
|
||||||
|
|
||||||
|
let decoded = decode::<Claims>(
|
||||||
|
&token,
|
||||||
|
&DecodingKey::from_secret(SECRET.as_ref()),
|
||||||
|
&Validation::default(),
|
||||||
|
)
|
||||||
|
.expect("Failed to decode token")
|
||||||
|
.claims;
|
||||||
|
|
||||||
|
assert_eq!(claims, decoded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +126,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let iat = Utc::now();
|
let iat = Utc::now();
|
||||||
let exp = iat + chrono::Duration::days(1);
|
let exp = iat + chrono::Duration::days(1);
|
||||||
|
|
||||||
let claims = Claims { sub: sub.clone(), iat, exp };
|
let claims = Claims::new(sub.clone(), iat, exp);
|
||||||
|
|
||||||
let token = jsonwebtoken::encode(
|
let token = jsonwebtoken::encode(
|
||||||
&Header::default(),
|
&Header::default(),
|
||||||
|
|
Loading…
Reference in New Issue