Merge pull request #83 from rakenodiax/chrono-example
Add example of using `chrono::DateTime` in claims
This commit is contained in:
commit
d6d0da7ab9
|
@ -0,0 +1,101 @@
|
|||
extern crate jsonwebtoken as jwt;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate chrono;
|
||||
|
||||
use chrono::prelude::*;
|
||||
use jwt::{Header, Validation};
|
||||
|
||||
const SECRET: &str = "some-secret";
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
struct Claims {
|
||||
sub: String,
|
||||
#[serde(with = "jwt_numeric_date")]
|
||||
iat: DateTime<Utc>,
|
||||
#[serde(with = "jwt_numeric_date")]
|
||||
exp: DateTime<Utc>,
|
||||
}
|
||||
|
||||
mod jwt_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 serde::{self, Deserialize, Deserializer, Serializer};
|
||||
|
||||
/// Serializes a DateTime<Utc> to a Unix timestamp (milliseconds since 1970/1/1T00:00:00T)
|
||||
pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let timestamp = date.timestamp();
|
||||
serializer.serialize_i64(timestamp)
|
||||
}
|
||||
|
||||
/// Attempts to deserialize an i64 and use as a Unix timestamp
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Utc.timestamp_opt(i64::deserialize(deserializer)?, 0)
|
||||
.single() // If there are multiple or no valid DateTimes from timestamp, return None
|
||||
.ok_or_else(|| serde::de::Error::custom("invalid Unix timestamp value"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use jwt::{Header, Validation};
|
||||
|
||||
const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gRGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.RTgha0S53MjPC2pMA4e2oMzaBxSY3DMjiYR2qFfV55A";
|
||||
|
||||
use super::super::{Claims, SECRET};
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
let sub = "Custom DateTime ser/de".to_string();
|
||||
let iat = Utc.timestamp(0, 0);
|
||||
let exp = Utc.timestamp(32503680000, 0);
|
||||
|
||||
let claims = Claims { sub: sub.clone(), iat, exp };
|
||||
|
||||
let token = jwt::encode(&Header::default(), &claims, SECRET.as_ref())
|
||||
.expect("Failed to encode claims");
|
||||
|
||||
assert_eq!(&token, EXPECTED_TOKEN);
|
||||
|
||||
let decoded = jwt::decode::<Claims>(&token, SECRET.as_ref(), &Validation::default())
|
||||
.expect("Failed to decode token");
|
||||
|
||||
assert_eq!(decoded.claims, claims);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_invalid_timestamp() {
|
||||
// A token with the expiry of i64::MAX + 1
|
||||
let overflow_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gRGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjkyMjMzNzIwMzY4NTQ3NzYwMDB9.G2PKreA27U8_xOwuIeCYXacFYeR46f9FyENIZfCrvEc";
|
||||
|
||||
let decode_result =
|
||||
jwt::decode::<Claims>(&overflow_token, SECRET.as_ref(), &Validation::default());
|
||||
|
||||
assert!(decode_result.is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sub = "Custom DateTime ser/de".to_string();
|
||||
let iat = Utc::now();
|
||||
let exp = iat + chrono::Duration::days(1);
|
||||
|
||||
let claims = Claims { sub: sub.clone(), iat, exp };
|
||||
|
||||
let token = jwt::encode(&Header::default(), &claims, SECRET.as_ref())?;
|
||||
|
||||
println!("serialized token: {}", &token);
|
||||
|
||||
let token_data = jwt::decode::<Claims>(&token, SECRET.as_ref(), &Validation::default())?;
|
||||
|
||||
println!("token data:\n{:#?}", &token_data);
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue