1use core::fmt;
5use core::str::FromStr;
6
7use chrono::{DateTime, Utc};
8use serde::de::Error as _;
9use serde::{Deserialize, Deserializer, Serialize};
10
11#[cfg(feature = "sd-jwt")]
12use crate::jose::numeric_date;
13use crate::jose::{CompactJws, JwsError, JwsHeader, JwtClaims, Signer, encode_compact};
14
15#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
35#[serde(transparent)]
36pub struct Jwt(String);
37
38impl Jwt {
39 pub fn new(token: impl Into<String>) -> Result<Self, JwsError> {
45 let token = token.into();
46 CompactJws::decode(&token)?;
47 Ok(Self(token))
48 }
49
50 pub async fn sign(
59 header: &JwsHeader,
60 claims: &JwtClaims,
61 signer: &dyn Signer,
62 ) -> Result<Self, JwsError> {
63 if let Some(name) = registered_in_additional(claims) {
64 return Err(JwsError::InvalidPayload(format!(
65 "registered claim `{name}` is set under `additional`; set its field instead"
66 )));
67 }
68 let payload = serde_json::to_vec(claims).expect("a claims set serializes");
69 encode_compact(header, &payload, signer).await.map(Self)
70 }
71
72 pub fn as_str(&self) -> &str {
74 &self.0
75 }
76
77 pub fn into_string(self) -> String {
79 self.0
80 }
81
82 pub fn decode(&self) -> Result<CompactJws, JwsError> {
88 CompactJws::decode(&self.0)
89 }
90}
91
92impl fmt::Debug for Jwt {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.debug_tuple("Jwt").field(&self.0).finish()
95 }
96}
97
98impl fmt::Display for Jwt {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.write_str(&self.0)
101 }
102}
103
104impl AsRef<str> for Jwt {
105 fn as_ref(&self) -> &str {
106 &self.0
107 }
108}
109
110impl FromStr for Jwt {
111 type Err = JwsError;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 Self::new(s)
115 }
116}
117
118impl TryFrom<String> for Jwt {
119 type Error = JwsError;
120
121 fn try_from(token: String) -> Result<Self, Self::Error> {
122 Self::new(token)
123 }
124}
125
126impl<'de> Deserialize<'de> for Jwt {
127 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
128 Self::new(String::deserialize(deserializer)?).map_err(D::Error::custom)
129 }
130}
131
132fn registered_in_additional(claims: &JwtClaims) -> Option<&'static str> {
134 JwtClaims::REGISTERED
135 .into_iter()
136 .find(|name| claims.additional.contains_key(*name))
137}
138
139#[cfg(feature = "sd-jwt")]
141pub(crate) fn required_numeric_date<'de, D: Deserializer<'de>>(
142 deserializer: D,
143) -> Result<i64, D::Error> {
144 numeric_date(deserializer)?.ok_or_else(|| serde::de::Error::custom("a NumericDate is required"))
145}
146
147#[cfg_attr(docsrs, doc(cfg(feature = "vc")))]
150impl JwtClaims {
151 pub fn numeric_date(time: DateTime<Utc>) -> i64 {
153 time.timestamp()
154 }
155
156 pub fn timestamp(numeric_date: i64) -> Option<DateTime<Utc>> {
158 DateTime::from_timestamp(numeric_date, 0)
159 }
160
161 #[must_use]
163 pub fn with_exp(mut self, time: DateTime<Utc>) -> Self {
164 self.exp = Some(Self::numeric_date(time));
165 self
166 }
167
168 #[must_use]
170 pub fn with_nbf(mut self, time: DateTime<Utc>) -> Self {
171 self.nbf = Some(Self::numeric_date(time));
172 self
173 }
174
175 #[must_use]
177 pub fn with_iat(mut self, time: DateTime<Utc>) -> Self {
178 self.iat = Some(Self::numeric_date(time));
179 self
180 }
181}