1use ct_codecs::{Base64UrlSafeNoPadding, Decoder, Encoder};
2use serde::{de::DeserializeOwned, Serialize};
3
4use crate::claims::*;
5use crate::common::*;
6use crate::error::*;
7use crate::jwt_header::*;
8
9pub const MAX_HEADER_LENGTH: usize = 8192;
10
11pub struct Token;
13
14#[derive(Debug, Clone, Default)]
16pub struct TokenMetadata {
17 pub(crate) jwt_header: JWTHeader,
18}
19
20impl TokenMetadata {
21 pub fn algorithm(&self) -> &str {
26 &self.jwt_header.algorithm
27 }
28
29 pub fn content_type(&self) -> Option<&str> {
31 self.jwt_header.content_type.as_deref()
32 }
33
34 pub fn key_id(&self) -> Option<&str> {
36 self.jwt_header.key_id.as_deref()
37 }
38
39 pub fn signature_type(&self) -> Option<&str> {
41 self.jwt_header.signature_type.as_deref()
42 }
43
44 pub fn critical(&self) -> Option<&[String]> {
46 self.jwt_header.critical.as_deref()
47 }
48
49 pub fn certificate_chain(&self) -> Option<&[String]> {
53 self.jwt_header.certificate_chain.as_deref()
54 }
55
56 pub fn key_set_url(&self) -> Option<&str> {
61 self.jwt_header.key_set_url.as_deref()
62 }
63
64 pub fn public_key(&self) -> Option<&str> {
69 self.jwt_header.public_key.as_deref()
70 }
71
72 pub fn certificate_url(&self) -> Option<&str> {
77 self.jwt_header.certificate_url.as_deref()
78 }
79
80 pub fn certificate_sha1_thumbprint(&self) -> Option<&str> {
85 self.jwt_header.certificate_sha1_thumbprint.as_deref()
86 }
87
88 pub fn certificate_sha256_thumbprint(&self) -> Option<&str> {
93 self.jwt_header.certificate_sha256_thumbprint.as_deref()
94 }
95}
96
97impl Token {
98 pub(crate) fn build<AuthenticationOrSignatureFn, CustomClaims: Serialize + DeserializeOwned>(
99 jwt_header: &JWTHeader,
100 claims: JWTClaims<CustomClaims>,
101 authentication_or_signature_fn: AuthenticationOrSignatureFn,
102 ) -> Result<String, Error>
103 where
104 AuthenticationOrSignatureFn: FnOnce(&str) -> Result<Vec<u8>, Error>,
105 {
106 let jwt_header_json = serde_json::to_string(&jwt_header)?;
107 let claims_json = serde_json::to_string(&claims)?;
108 let authenticated = format!(
109 "{}.{}",
110 Base64UrlSafeNoPadding::encode_to_string(jwt_header_json)?,
111 Base64UrlSafeNoPadding::encode_to_string(claims_json)?
112 );
113 let authentication_tag_or_signature = authentication_or_signature_fn(&authenticated)?;
114 let mut token = authenticated;
115 token.push('.');
116 token.push_str(&Base64UrlSafeNoPadding::encode_to_string(
117 authentication_tag_or_signature,
118 )?);
119 Ok(token)
120 }
121
122 pub(crate) fn verify<AuthenticationOrSignatureFn, CustomClaims: Serialize + DeserializeOwned>(
123 jwt_alg_name: &'static str,
124 token: &str,
125 options: Option<VerificationOptions>,
126 authentication_or_signature_fn: AuthenticationOrSignatureFn,
127 ) -> Result<JWTClaims<CustomClaims>, Error>
128 where
129 AuthenticationOrSignatureFn: FnOnce(&str, &[u8]) -> Result<(), Error>,
130 {
131 let options = options.unwrap_or_default();
132
133 if let Some(max_token_length) = options.max_token_length {
134 ensure!(token.len() <= max_token_length, JWTError::TokenTooLong);
135 }
136
137 let mut parts = token.split('.');
138 let jwt_header_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
139 ensure!(
140 jwt_header_b64.len() <= options.max_header_length.unwrap_or(MAX_HEADER_LENGTH),
141 JWTError::HeaderTooLarge
142 );
143 let claims_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
144 let authentication_tag_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
145 ensure!(parts.next().is_none(), JWTError::CompactEncodingError);
146 let jwt_header: JWTHeader = serde_json::from_slice(
147 &Base64UrlSafeNoPadding::decode_to_vec(jwt_header_b64, None)?,
148 )?;
149 if let Some(signature_type) = &jwt_header.signature_type {
150 let signature_type_uc = signature_type.to_uppercase();
151 ensure!(
152 signature_type_uc == "JWT" || signature_type_uc.ends_with("+JWT"),
153 JWTError::NotJWT
154 );
155 }
156 ensure!(
157 jwt_header.algorithm == jwt_alg_name,
158 JWTError::AlgorithmMismatch
159 );
160 if let Some(required_key_id) = &options.required_key_id {
161 if let Some(key_id) = &jwt_header.key_id {
162 ensure!(key_id == required_key_id, JWTError::KeyIdentifierMismatch);
163 } else {
164 bail!(JWTError::MissingJWTKeyIdentifier)
165 }
166 }
167 let authentication_tag =
168 Base64UrlSafeNoPadding::decode_to_vec(authentication_tag_b64, None)?;
169 let authenticated = &token[..jwt_header_b64.len() + 1 + claims_b64.len()];
170 authentication_or_signature_fn(authenticated, &authentication_tag)?;
171 let claims: JWTClaims<CustomClaims> =
172 serde_json::from_slice(&Base64UrlSafeNoPadding::decode_to_vec(claims_b64, None)?)?;
173 claims.validate(&options)?;
174 Ok(claims)
175 }
176
177 pub fn decode_metadata(token: &str) -> Result<TokenMetadata, Error> {
180 let mut parts = token.split('.');
181 let jwt_header_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
182 ensure!(
183 jwt_header_b64.len() <= MAX_HEADER_LENGTH,
184 JWTError::HeaderTooLarge
185 );
186 let jwt_header: JWTHeader = serde_json::from_slice(
187 &Base64UrlSafeNoPadding::decode_to_vec(jwt_header_b64, None)?,
188 )?;
189 Ok(TokenMetadata { jwt_header })
190 }
191}
192
193#[test]
194fn should_verify_token() {
195 use crate::prelude::*;
196
197 let key = HS256Key::generate();
198
199 let issuer = "issuer";
200 let audience = "recipient";
201 let mut claims = Claims::create(Duration::from_mins(10))
202 .with_issuer(issuer)
203 .with_audience(audience);
204 let nonce = claims.create_nonce();
205 let token = key.authenticate(claims).unwrap();
206
207 let options = VerificationOptions {
208 required_nonce: Some(nonce),
209 allowed_issuers: Some(HashSet::from_strings(&[issuer])),
210 allowed_audiences: Some(HashSet::from_strings(&[audience])),
211 ..Default::default()
212 };
213 key.verify_token::<NoCustomClaims>(&token, Some(options))
214 .unwrap();
215}
216
217#[test]
218fn multiple_audiences() {
219 use std::collections::HashSet;
220
221 use crate::prelude::*;
222
223 let key = HS256Key::generate();
224
225 let mut audiences = HashSet::new();
226 audiences.insert("audience 1");
227 audiences.insert("audience 2");
228 audiences.insert("audience 3");
229 let claims = Claims::create(Duration::from_mins(10)).with_audiences(audiences);
230 let token = key.authenticate(claims).unwrap();
231
232 let options = VerificationOptions {
233 allowed_audiences: Some(HashSet::from_strings(&["audience 1"])),
234 ..Default::default()
235 };
236 key.verify_token::<NoCustomClaims>(&token, Some(options))
237 .unwrap();
238}
239
240#[test]
241fn explicitly_empty_audiences() {
242 use std::collections::HashSet;
243
244 use crate::prelude::*;
245
246 let key = HS256Key::generate();
247
248 let audiences: HashSet<&str> = HashSet::new();
249 let claims = Claims::create(Duration::from_mins(10)).with_audiences(audiences);
250 let token = key.authenticate(claims).unwrap();
251 let decoded = key.verify_token::<NoCustomClaims>(&token, None).unwrap();
252 assert!(decoded.audiences.is_some());
253
254 let claims = Claims::create(Duration::from_mins(10)).with_audience("");
255 let token = key.authenticate(claims).unwrap();
256 let decoded = key.verify_token::<NoCustomClaims>(&token, None).unwrap();
257 assert!(decoded.audiences.is_some());
258
259 let claims = Claims::create(Duration::from_mins(10));
260 let token = key.authenticate(claims).unwrap();
261 let decoded = key.verify_token::<NoCustomClaims>(&token, None).unwrap();
262 assert!(decoded.audiences.is_none());
263}