1use std::{fmt, time::Duration};
6
7pub use base64::engine::general_purpose::URL_SAFE_NO_PAD;
8use serde::{
9 Deserialize, Deserializer, Serialize, Serializer,
10 de::{self, Visitor},
11};
12
13pub const TYPE_JWT: &str = "JWT";
17
18pub const ALGORITHM_HS256: &str = "HS256";
22
23pub const HS256_MIN_KEY_BYTES: usize = 32;
25
26#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
31pub struct NumericDate(Duration);
32
33impl From<u64> for NumericDate {
34 fn from(seconds: u64) -> Self {
35 Self(Duration::from_secs(seconds))
36 }
37}
38
39impl From<Duration> for NumericDate {
40 fn from(duration: Duration) -> Self {
41 Self(duration)
42 }
43}
44
45impl Serialize for NumericDate {
46 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47 where
48 S: Serializer,
49 {
50 if self.0.subsec_nanos() == 0 {
51 serializer.serialize_u64(self.0.as_secs())
52 } else {
53 serializer.serialize_f64(self.0.as_secs_f64())
54 }
55 }
56}
57
58impl<'de> Deserialize<'de> for NumericDate {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60 where
61 D: Deserializer<'de>,
62 {
63 deserializer.deserialize_any(NumericDateVisitor)
64 }
65}
66
67struct NumericDateVisitor;
68
69impl Visitor<'_> for NumericDateVisitor {
70 type Value = NumericDate;
71
72 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
73 formatter.write_str("a nonnegative RFC 7519 NumericDate")
74 }
75
76 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
77 where
78 E: de::Error,
79 {
80 Ok(NumericDate::from(value))
81 }
82
83 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
84 where
85 E: de::Error,
86 {
87 Duration::try_from_secs_f64(value)
88 .map(NumericDate)
89 .map_err(|_error| E::custom("NumericDate must be nonnegative, finite and in range"))
90 }
91}
92
93#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
95pub struct RegisteredJwtClaims {
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub exp: Option<NumericDate>,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub iat: Option<NumericDate>,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub nbf: Option<NumericDate>,
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub iss: Option<String>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub sub: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub aud: Option<JwtAudience>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub jti: Option<String>,
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(untagged)]
125pub enum JwtAudience {
126 Single(String),
127 Multiple(Vec<String>),
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct JwtHeader {
135 pub alg: String,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub typ: Option<String>,
138}
139
140#[cfg(test)]
141#[path = "TESTS/jwt.rs"]
142mod tests;