//! Provides [`Key`] and functions to encode & decode expiring claims. use time::OffsetDateTime; pub use crate::signed::Key; /// Join a string and an expiry date together into a string. pub fn encode_expiring_claim(claim: &str, expiry_date: OffsetDateTime) -> String { format!("{}:{}", claim, expiry_date.unix_timestamp()) } /// Extract the string, failing if the expiry date is in the past. pub fn decode_expiring_claim(value: &str) -> Result<&str, &'static str> { let mut parts = value.rsplitn(2, ':'); let expiry_date = parts.next().expect("first .rsplitn().next() is expected to return Some"); let claim = parts.next().ok_or("expected colon")?; let expiry_date: i64 = expiry_date.parse().map_err(|_| "failed to parse timestamp")?; if expiry_date > OffsetDateTime::now_utc().unix_timestamp() { Ok(claim) } else { Err("token is expired") } } #[cfg(test)] mod tests { use time::{OffsetDateTime, Duration}; #[test] fn test_expiring_claim() { for claim in vec!["test", "", "foo:bar"] { let encoded_claim = super::encode_expiring_claim(claim, OffsetDateTime::now_utc() + Duration::minutes(1)); assert_eq!(super::decode_expiring_claim(&encoded_claim).unwrap(), claim); let encoded_claim = super::encode_expiring_claim(claim, OffsetDateTime::now_utc() - Duration::minutes(1)); assert!(super::decode_expiring_claim(&encoded_claim).is_err()); } assert!(super::decode_expiring_claim("test".into()).is_err()); } }