Skip to content

Commit

Permalink
fix!: timestamps can be float (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomsons authored Aug 21, 2023
1 parent 823a5dd commit 5681f56
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 33 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ reqwest = { version = "0.11.4", features = ["json"], optional = true }
tokio = { version = "1.9.0", features = ["sync"], optional = true }
openssl-sys = "0.9.65"
foreign-types = "0.3.2"
serde_with = "3.1.0"

[dev-dependencies]
axum = "0.1.3"
Expand Down
74 changes: 41 additions & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
#![doc = include_str!("../README.md")]
mod some;

pub mod hmac;

pub mod eddsa;

pub mod ecdsa;

pub mod rsa;

pub mod jwk;

use std::{
borrow::Cow,
Expand All @@ -18,13 +7,27 @@ use std::{
string::FromUtf8Error,
time::{Duration, SystemTime, UNIX_EPOCH},
};

use jwk::Jwk;
use openssl::error::ErrorStack;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{Map, Value};
use serde_with::{serde_as, skip_serializing_none};
use smallvec::SmallVec;

use jwk::Jwk;
pub use some::*;

mod some;

pub mod hmac;

pub mod eddsa;

pub mod ecdsa;

pub mod rsa;

pub mod jwk;

/// JWT header.
#[non_exhaustive]
#[derive(Debug, Serialize, Deserialize, Default)]
Expand Down Expand Up @@ -65,23 +68,22 @@ impl<T> Default for OneOrMany<T> {
}

/// JWT Claims.
#[serde_as]
#[skip_serializing_none]
#[non_exhaustive]
#[derive(Debug, Serialize, Default, Deserialize)]
pub struct Claims<ExtraClaims> {
#[serde(skip_serializing_if = "Option::is_none")]
pub exp: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub nbf: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub iat: Option<u64>,
#[serde_as(as = "Option<serde_with::DurationSeconds<f64>>")]
pub exp: Option<Duration>,
#[serde_as(as = "Option<serde_with::DurationSeconds<f64>>")]
pub nbf: Option<Duration>,
#[serde_as(as = "Option<serde_with::DurationSeconds<f64>>")]
pub iat: Option<Duration>,

#[serde(skip_serializing_if = "Option::is_none")]
pub iss: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sub: Option<String>,
#[serde(default, skip_serializing_if = "OneOrMany::is_empty")]
pub aud: OneOrMany<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub jti: Option<String>,

#[serde(flatten)]
Expand Down Expand Up @@ -189,16 +191,15 @@ impl<ExtraClaims> HeaderAndClaims<ExtraClaims> {
self.claims.iat = Some(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs(),
.unwrap(),
);
self
}

/// Check that `iat` is present and is later than `t`.
pub fn iat_is_later_than(&self, t: SystemTime) -> bool {
self.claims.iat.map_or(false, |iat| {
iat > t.duration_since(UNIX_EPOCH).unwrap().as_secs()
iat > t.duration_since(UNIX_EPOCH).unwrap()
})
}

Expand All @@ -207,8 +208,7 @@ impl<ExtraClaims> HeaderAndClaims<ExtraClaims> {
pub fn set_exp_from_now(&mut self, dur: Duration) -> &mut Self {
let t = (SystemTime::now() + dur)
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
.unwrap();
self.claims.exp = Some(t);
self
}
Expand All @@ -218,8 +218,7 @@ impl<ExtraClaims> HeaderAndClaims<ExtraClaims> {
pub fn set_nbf_from_now(&mut self, dur: Duration) -> &mut Self {
let t = (SystemTime::now() + dur)
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
.unwrap();
self.claims.nbf = Some(t);
self
}
Expand All @@ -238,8 +237,6 @@ fn url_safe_trailing_bits() -> base64::Config {
base64::URL_SAFE_NO_PAD.decode_allow_trailing_bits(true)
}

pub use some::*;

/// Encode and sign this header and claims with the signing key.
///
/// The `alg` field in header is automatically set. The `kid` claim is
Expand Down Expand Up @@ -286,13 +283,13 @@ pub fn verify<ExtraClaims: DeserializeOwned>(
// Check exp and nbf.
let now = SystemTime::now();
if let Some(exp) = claims.claims.exp {
let exp = SystemTime::UNIX_EPOCH + Duration::from_secs(exp);
let exp = SystemTime::UNIX_EPOCH + exp;
if now > exp {
return Err(Error::Expired);
}
}
if let Some(nbf) = claims.claims.nbf {
let nbf = SystemTime::UNIX_EPOCH + Duration::from_secs(nbf);
let nbf = SystemTime::UNIX_EPOCH + nbf;
if now < nbf {
return Err(Error::Before);
}
Expand Down Expand Up @@ -532,4 +529,15 @@ mod tests {

Ok(())
}

#[test]
fn claim_deserialization() {
let mut json = r#"eyJpYXQiOjEuNjkyMTkwMTI1RTksImV4cCI6MS42OTIxOTM3MjVFOSwiYW50aUNzcmZUb2tlbiI6bnVsbCwic3ViIjoiYTM5ZmZjNWUtNjc5ZC00YjAzLWI5YmYtYTliZjEzNDk4NGYzIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDozOTk5L2F1dGgiLCJzZXNzaW9uSGFuZGxlIjoiNTAyMWQ2MTQtYzFmNi00ZTZkLWI1NjktZGQxN2Q0N2EyOWI0IiwicGFyZW50UmVmcmVzaFRva2VuSGFzaDEiOm51bGwsInJlZnJlc2hUb2tlbkhhc2gxIjoiNTZiMjcxZDcxNGRlMzg3M2UwMmIyZjAyYTJiZDcyYWJjZDIyZDM0NGZlZjE2YTJkMWJjYmM1NGU2YWUxN2M3OCJ9"#.as_bytes();

let r = base64::read::DecoderReader::new(&mut json, url_safe_trailing_bits());

let claims: Claims<Value> = serde_json::from_reader(r).unwrap();
assert_eq!(claims.iat, Some(Duration::from_secs(1692190125)));
assert_eq!(claims.exp, Some(Duration::from_secs(1692193725)));
}
}

0 comments on commit 5681f56

Please sign in to comment.