axum_security_oidc/
claims.rs1use serde::{Deserialize, Deserializer, de};
2
3use crate::error::ClaimsError;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
7pub struct UtcTimestamp(i64);
8
9impl<'de> serde::Deserialize<'de> for UtcTimestamp {
10 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
11 let value = i64::deserialize(deserializer)?;
12
13 #[cfg(feature = "jiff")]
14 jiff::Timestamp::from_second(value).map_err(de::Error::custom)?;
15
16 #[cfg(feature = "chrono")]
17 if chrono::DateTime::<chrono::Utc>::from_timestamp(value, 0).is_none() {
18 return Err(de::Error::custom("timestamp out of range for chrono"));
19 }
20
21 #[cfg(feature = "time")]
22 time::OffsetDateTime::from_unix_timestamp(value).map_err(de::Error::custom)?;
23
24 Ok(UtcTimestamp(value))
25 }
26}
27
28impl UtcTimestamp {
29 pub fn as_secs(&self) -> i64 {
30 self.0
31 }
32
33 #[cfg(feature = "jiff")]
34 pub fn to_jiff(&self) -> jiff::Timestamp {
35 jiff::Timestamp::from_second(self.0).expect("validated during deserialization")
36 }
37
38 #[cfg(feature = "chrono")]
39 pub fn to_chrono(&self) -> chrono::DateTime<chrono::Utc> {
40 chrono::DateTime::from_timestamp(self.0, 0).expect("validated during deserialization")
41 }
42
43 #[cfg(feature = "time")]
44 pub fn to_time(&self) -> time::OffsetDateTime {
45 time::OffsetDateTime::from_unix_timestamp(self.0).expect("validated during deserialization")
46 }
47}
48
49#[derive(Debug, Clone, Deserialize)]
51pub struct OidcAddress<'a> {
52 pub formatted: Option<&'a str>,
53 pub street_address: Option<&'a str>,
54 pub locality: Option<&'a str>,
55 pub region: Option<&'a str>,
56 pub postal_code: Option<&'a str>,
57 pub country: Option<&'a str>,
58}
59
60#[derive(Debug, Clone, Deserialize)]
71pub struct OidcClaims<'a> {
72 #[serde(rename = "iss")]
74 pub issuer: &'a str,
75 #[serde(rename = "aud", deserialize_with = "deserialize_audience")]
76 pub audiences: Vec<&'a str>,
77 #[serde(rename = "sub")]
78 pub subject: &'a str,
79
80 exp: UtcTimestamp,
82 iat: UtcTimestamp,
83 auth_time: Option<UtcTimestamp>,
84 updated_at: Option<UtcTimestamp>,
85
86 pub nonce: Option<&'a str>,
88 pub acr: Option<&'a str>,
89 pub amr: Option<Vec<&'a str>>,
90 pub azp: Option<&'a str>,
91 pub at_hash: Option<&'a str>,
92
93 pub name: Option<&'a str>,
95 pub given_name: Option<&'a str>,
96 pub family_name: Option<&'a str>,
97 pub middle_name: Option<&'a str>,
98 pub nickname: Option<&'a str>,
99 pub preferred_username: Option<&'a str>,
100 pub profile: Option<&'a str>,
101 pub picture: Option<&'a str>,
102 pub website: Option<&'a str>,
103 pub gender: Option<&'a str>,
104 pub birthdate: Option<&'a str>,
105 pub zoneinfo: Option<&'a str>,
106 pub locale: Option<&'a str>,
107
108 pub email: Option<&'a str>,
110 pub email_verified: Option<bool>,
111
112 pub phone_number: Option<&'a str>,
114 pub phone_number_verified: Option<bool>,
115
116 pub address: Option<OidcAddress<'a>>,
118
119 #[serde(flatten)]
121 pub extra: serde_json::Map<String, serde_json::Value>,
122}
123
124impl<'a> OidcClaims<'a> {
125 pub fn from_payload(payload: &'a [u8]) -> Result<OidcClaims<'a>, ClaimsError> {
130 serde_json::from_slice(payload).map_err(ClaimsError::Deserialize)
131 }
132
133 pub fn expiration_secs(&self) -> i64 {
134 self.exp.as_secs()
135 }
136
137 #[cfg(feature = "jiff")]
138 pub fn expiration_jiff(&self) -> jiff::Timestamp {
139 self.exp.to_jiff()
140 }
141
142 #[cfg(feature = "chrono")]
143 pub fn expiration_chrono(&self) -> chrono::DateTime<chrono::Utc> {
144 self.exp.to_chrono()
145 }
146
147 #[cfg(feature = "time")]
148 pub fn expiration_time(&self) -> time::OffsetDateTime {
149 self.exp.to_time()
150 }
151
152 pub fn issued_at(&self) -> &UtcTimestamp {
153 &self.iat
154 }
155
156 pub fn auth_time(&self) -> Option<&UtcTimestamp> {
157 self.auth_time.as_ref()
158 }
159
160 pub fn updated_at(&self) -> Option<&UtcTimestamp> {
161 self.updated_at.as_ref()
162 }
163}
164
165fn deserialize_audience<'de, D>(deserializer: D) -> Result<Vec<&'de str>, D::Error>
167where
168 D: Deserializer<'de>,
169{
170 struct AudienceVisitor;
171
172 impl<'de> de::Visitor<'de> for AudienceVisitor {
173 type Value = Vec<&'de str>;
174
175 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
176 f.write_str("a string or array of strings")
177 }
178
179 fn visit_borrowed_str<E: de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
180 Ok(vec![v])
181 }
182
183 fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
184 let mut values = Vec::with_capacity(seq.size_hint().unwrap_or(1));
185 while let Some(v) = seq.next_element()? {
186 values.push(v);
187 }
188 Ok(values)
189 }
190 }
191
192 deserializer.deserialize_any(AudienceVisitor)
193}