1use base64ct::{Base64UrlUnpadded, Encoding};
2use serde::{
3 Deserialize, Serialize,
4 de::{self, IntoDeserializer, value::SeqDeserializer},
5};
6use std::{borrow::Cow, fmt};
7
8mod encoded_token;
9mod signature;
10
11use encoded_token::EncodedToken;
12use signature::Signature;
13
14use crate::error::Error;
15use crate::ser::Serializer;
16
17#[cfg_attr(feature = "debug", derive(Debug))]
19pub struct Jws {
20 encoded_token: EncodedToken,
21 signature: Signature,
22}
23
24impl Jws {
25 #[cfg_attr(feature = "debug", tracing::instrument)]
26 pub fn new(encoded_token: &str, signature: &[u8]) -> Self {
27 let encoded_token = EncodedToken::from(encoded_token);
28 let signature = Signature::from(signature);
29
30 Self {
31 encoded_token,
32 signature,
33 }
34 }
35
36 pub fn encoded_token(&self) -> Cow<'_, str> {
40 let ptr: &str = &self.encoded_token;
41
42 Cow::from(ptr)
43 }
44
45 pub fn signature(&self) -> Cow<'_, [u8]> {
47 let ptr: &[u8] = self.signature.as_ref();
48
49 Cow::from(ptr)
50 }
51
52 pub fn to_string(&self) -> Result<String, Error> {
53 let mut serializer = Serializer {
54 output: String::new(),
55 };
56
57 self.serialize(&mut serializer)?;
58
59 Ok(serializer.output)
60 }
61}
62
63impl<'de> Deserialize<'de> for Jws {
64 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
65 where
66 D: de::Deserializer<'de>,
67 {
68 struct Visitor;
69
70 impl<'de> de::Visitor<'de> for Visitor {
71 type Value = Jws;
72
73 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
74 write!(
75 f,
76 "<header (json via base64)>.<claims (json via base64)>.<signature (base64)>"
77 )
78 }
79
80 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
81 where
82 A: de::SeqAccess<'de>,
83 {
84 let signature: Signature = seq
85 .next_element()?
86 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
87
88 let encoded_token: EncodedToken = seq
89 .next_element()?
90 .ok_or_else(|| de::Error::invalid_length(1, &self))?;
91
92 Ok(Self::Value {
93 encoded_token,
94 signature,
95 })
96 }
97
98 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
99 where
100 E: de::Error,
101 {
102 let parts = v.rsplitn(2, '.');
103
104 let de = SeqDeserializer::new(parts);
105
106 self.visit_seq(de)
107 }
108 }
109
110 deserializer.deserialize_str(Visitor)
111 }
112}
113
114impl Serialize for Jws {
115 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116 where
117 S: serde::Serializer,
118 {
119 let enc_token: Cow<str> = self.encoded_token();
120
121 let signature: String = {
122 let signature: Cow<[u8]> = self.signature();
123
124 Base64UrlUnpadded::encode_string(&signature)
125 };
126
127 serializer.collect_str(&format_args!("{}.{}", enc_token, signature))
128 }
129}
130
131impl TryFrom<&str> for Jws {
132 type Error = Error;
133
134 fn try_from(value: &str) -> Result<Self, Self::Error> {
135 let de = value.into_deserializer();
136
137 Self::deserialize(de)
138 }
139}
140
141#[cfg(test)]
142mod test {
143 use crate::prelude::*;
144 use hmac::Mac;
145 use serde::{Deserialize, Serialize};
146 use serde_json::json;
147 use sha2;
148
149 type Hmac256 = hmac::Hmac<sha2::Sha256>;
150
151 #[derive(Deserialize, Serialize)]
152 struct TestData {
153 pub token: Jws,
154 }
155
156 #[test]
157 fn test_from_str() {
158 let valid_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30";
159
160 let input = json!({
161 "token": valid_token
162 })
163 .to_string();
164
165 let data: TestData = serde_json::from_str(&input).unwrap();
166 let jws: &Jws = &data.token;
167
168 assert_eq!(input, serde_json::to_string(&data).unwrap());
169
170 assert_eq!(
171 "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0".as_bytes(),
172 jws.encoded_token().as_bytes()
173 );
174
175 assert_eq!(
176 [
177 40, 197, 5, 176, 128, 211, 156, 89, 178, 27, 121, 204, 136, 99, 58, 31, 209, 77,
178 21, 68, 78, 127, 124, 33, 237, 41, 170, 38, 159, 144, 87, 125
179 ],
180 jws.signature().as_ref()
181 );
182 }
183
184 #[test]
185 fn test_signature_validation() {
186 let signed_message = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30";
187
188 let secret = "a-string-secret-at-least-256-bits-long";
189
190 let input = json!({
191 "token": signed_message
192 })
193 .to_string();
194
195 let data: TestData = serde_json::from_str(&input).unwrap();
196 let jws: Jws = data.token;
197
198 let mut mac = Hmac256::new_from_slice(secret.as_bytes()).unwrap();
199
200 mac.update(jws.encoded_token().as_bytes());
201
202 assert!(mac.verify_slice(&jws.signature()).is_ok())
203 }
204}