this repo has no description
1use super::token::{ 2 SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED, SCOPE_REFRESH, TOKEN_TYPE_ACCESS, 3 TOKEN_TYPE_REFRESH, 4}; 5use super::{Claims, Header, TokenData, UnsafeClaims}; 6use anyhow::{Context, Result, anyhow}; 7use base64::Engine as _; 8use base64::engine::general_purpose::URL_SAFE_NO_PAD; 9use chrono::Utc; 10use hmac::{Hmac, Mac}; 11use k256::ecdsa::{Signature, SigningKey, VerifyingKey, signature::Verifier}; 12use sha2::Sha256; 13use std::fmt; 14use subtle::ConstantTimeEq; 15 16type HmacSha256 = Hmac<Sha256>; 17 18#[derive(Debug, Clone, Copy, PartialEq, Eq)] 19pub enum TokenVerifyError { 20 Expired, 21 Invalid, 22} 23 24impl fmt::Display for TokenVerifyError { 25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 match self { 27 Self::Expired => write!(f, "Token expired"), 28 Self::Invalid => write!(f, "Token invalid"), 29 } 30 } 31} 32 33impl std::error::Error for TokenVerifyError {} 34 35pub fn get_did_from_token(token: &str) -> Result<String, String> { 36 let parts: Vec<&str> = token.split('.').collect(); 37 if parts.len() != 3 { 38 return Err("Invalid token format".to_string()); 39 } 40 41 let payload_bytes = URL_SAFE_NO_PAD 42 .decode(parts[1]) 43 .map_err(|e| format!("Base64 decode failed: {}", e))?; 44 45 let claims: UnsafeClaims = 46 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 47 48 Ok(claims.sub.unwrap_or(claims.iss)) 49} 50 51pub fn get_jti_from_token(token: &str) -> Result<String, String> { 52 let parts: Vec<&str> = token.split('.').collect(); 53 if parts.len() != 3 { 54 return Err("Invalid token format".to_string()); 55 } 56 57 let payload_bytes = URL_SAFE_NO_PAD 58 .decode(parts[1]) 59 .map_err(|e| format!("Base64 decode failed: {}", e))?; 60 61 let claims: serde_json::Value = 62 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 63 64 claims 65 .get("jti") 66 .and_then(|j| j.as_str()) 67 .map(|s| s.to_string()) 68 .ok_or_else(|| "No jti claim in token".to_string()) 69} 70 71pub fn verify_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 72 verify_token_internal(token, key_bytes, None, None) 73} 74 75pub fn verify_access_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 76 verify_token_internal( 77 token, 78 key_bytes, 79 Some(TOKEN_TYPE_ACCESS), 80 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]), 81 ) 82} 83 84pub fn verify_refresh_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 85 verify_token_internal( 86 token, 87 key_bytes, 88 Some(TOKEN_TYPE_REFRESH), 89 Some(&[SCOPE_REFRESH]), 90 ) 91} 92 93pub fn verify_access_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> { 94 verify_token_hs256_internal( 95 token, 96 secret, 97 Some(TOKEN_TYPE_ACCESS), 98 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]), 99 ) 100} 101 102pub fn verify_refresh_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> { 103 verify_token_hs256_internal( 104 token, 105 secret, 106 Some(TOKEN_TYPE_REFRESH), 107 Some(&[SCOPE_REFRESH]), 108 ) 109} 110 111fn verify_token_internal( 112 token: &str, 113 key_bytes: &[u8], 114 expected_typ: Option<&str>, 115 allowed_scopes: Option<&[&str]>, 116) -> Result<TokenData<Claims>> { 117 let parts: Vec<&str> = token.split('.').collect(); 118 if parts.len() != 3 { 119 return Err(anyhow!("Invalid token format")); 120 } 121 122 let header_b64 = parts[0]; 123 let claims_b64 = parts[1]; 124 let signature_b64 = parts[2]; 125 126 let header_bytes = URL_SAFE_NO_PAD 127 .decode(header_b64) 128 .context("Base64 decode of header failed")?; 129 130 let header: Header = 131 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; 132 133 if let Some(expected) = expected_typ 134 && header.typ != expected 135 { 136 return Err(anyhow!( 137 "Invalid token type: expected {}, got {}", 138 expected, 139 header.typ 140 )); 141 } 142 143 let signature_bytes = URL_SAFE_NO_PAD 144 .decode(signature_b64) 145 .context("Base64 decode of signature failed")?; 146 147 let signature = Signature::from_slice(&signature_bytes) 148 .map_err(|e| anyhow!("Invalid signature format: {}", e))?; 149 150 let signing_key = SigningKey::from_slice(key_bytes)?; 151 let verifying_key = VerifyingKey::from(&signing_key); 152 153 let message = format!("{}.{}", header_b64, claims_b64); 154 verifying_key 155 .verify(message.as_bytes(), &signature) 156 .map_err(|e| anyhow!("Signature verification failed: {}", e))?; 157 158 let claims_bytes = URL_SAFE_NO_PAD 159 .decode(claims_b64) 160 .context("Base64 decode of claims failed")?; 161 162 let claims: Claims = 163 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 164 165 let now = Utc::now().timestamp() as usize; 166 if claims.exp < now { 167 return Err(anyhow!("Token expired")); 168 } 169 170 if let Some(scopes) = allowed_scopes { 171 let token_scope = claims.scope.as_deref().unwrap_or(""); 172 if !scopes.contains(&token_scope) { 173 return Err(anyhow!("Invalid token scope: {}", token_scope)); 174 } 175 } 176 177 Ok(TokenData { claims }) 178} 179 180fn verify_token_hs256_internal( 181 token: &str, 182 secret: &[u8], 183 expected_typ: Option<&str>, 184 allowed_scopes: Option<&[&str]>, 185) -> Result<TokenData<Claims>> { 186 let parts: Vec<&str> = token.split('.').collect(); 187 if parts.len() != 3 { 188 return Err(anyhow!("Invalid token format")); 189 } 190 191 let header_b64 = parts[0]; 192 let claims_b64 = parts[1]; 193 let signature_b64 = parts[2]; 194 195 let header_bytes = URL_SAFE_NO_PAD 196 .decode(header_b64) 197 .context("Base64 decode of header failed")?; 198 199 let header: Header = 200 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; 201 202 if header.alg != "HS256" { 203 return Err(anyhow!("Expected HS256 algorithm, got {}", header.alg)); 204 } 205 206 if let Some(expected) = expected_typ 207 && header.typ != expected 208 { 209 return Err(anyhow!( 210 "Invalid token type: expected {}, got {}", 211 expected, 212 header.typ 213 )); 214 } 215 216 let signature_bytes = URL_SAFE_NO_PAD 217 .decode(signature_b64) 218 .context("Base64 decode of signature failed")?; 219 220 let message = format!("{}.{}", header_b64, claims_b64); 221 222 let mut mac = 223 HmacSha256::new_from_slice(secret).map_err(|e| anyhow!("Invalid secret: {}", e))?; 224 mac.update(message.as_bytes()); 225 226 let expected_signature = mac.finalize().into_bytes(); 227 let is_valid: bool = signature_bytes.ct_eq(&expected_signature).into(); 228 229 if !is_valid { 230 return Err(anyhow!("Signature verification failed")); 231 } 232 233 let claims_bytes = URL_SAFE_NO_PAD 234 .decode(claims_b64) 235 .context("Base64 decode of claims failed")?; 236 237 let claims: Claims = 238 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 239 240 let now = Utc::now().timestamp() as usize; 241 if claims.exp < now { 242 return Err(anyhow!("Token expired")); 243 } 244 245 if let Some(scopes) = allowed_scopes { 246 let token_scope = claims.scope.as_deref().unwrap_or(""); 247 if !scopes.contains(&token_scope) { 248 return Err(anyhow!("Invalid token scope: {}", token_scope)); 249 } 250 } 251 252 Ok(TokenData { claims }) 253} 254 255pub fn verify_access_token_typed( 256 token: &str, 257 key_bytes: &[u8], 258) -> Result<TokenData<Claims>, TokenVerifyError> { 259 verify_token_typed_internal( 260 token, 261 key_bytes, 262 Some(TOKEN_TYPE_ACCESS), 263 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]), 264 ) 265} 266 267fn verify_token_typed_internal( 268 token: &str, 269 key_bytes: &[u8], 270 expected_typ: Option<&str>, 271 allowed_scopes: Option<&[&str]>, 272) -> Result<TokenData<Claims>, TokenVerifyError> { 273 let parts: Vec<&str> = token.split('.').collect(); 274 if parts.len() != 3 { 275 return Err(TokenVerifyError::Invalid); 276 } 277 278 let header_b64 = parts[0]; 279 let claims_b64 = parts[1]; 280 let signature_b64 = parts[2]; 281 282 let Ok(header_bytes) = URL_SAFE_NO_PAD.decode(header_b64) else { 283 return Err(TokenVerifyError::Invalid); 284 }; 285 286 let Ok(header) = serde_json::from_slice::<Header>(&header_bytes) else { 287 return Err(TokenVerifyError::Invalid); 288 }; 289 290 if let Some(expected) = expected_typ 291 && header.typ != expected 292 { 293 return Err(TokenVerifyError::Invalid); 294 } 295 296 let Ok(signature_bytes) = URL_SAFE_NO_PAD.decode(signature_b64) else { 297 return Err(TokenVerifyError::Invalid); 298 }; 299 300 let Ok(signature) = Signature::from_slice(&signature_bytes) else { 301 return Err(TokenVerifyError::Invalid); 302 }; 303 304 let Ok(signing_key) = SigningKey::from_slice(key_bytes) else { 305 return Err(TokenVerifyError::Invalid); 306 }; 307 let verifying_key = VerifyingKey::from(&signing_key); 308 309 let message = format!("{}.{}", header_b64, claims_b64); 310 if verifying_key.verify(message.as_bytes(), &signature).is_err() { 311 return Err(TokenVerifyError::Invalid); 312 } 313 314 let Ok(claims_bytes) = URL_SAFE_NO_PAD.decode(claims_b64) else { 315 return Err(TokenVerifyError::Invalid); 316 }; 317 318 let Ok(claims) = serde_json::from_slice::<Claims>(&claims_bytes) else { 319 return Err(TokenVerifyError::Invalid); 320 }; 321 322 let now = Utc::now().timestamp() as usize; 323 if claims.exp < now { 324 return Err(TokenVerifyError::Expired); 325 } 326 327 if let Some(scopes) = allowed_scopes { 328 let token_scope = claims.scope.as_deref().unwrap_or(""); 329 if !scopes.contains(&token_scope) { 330 return Err(TokenVerifyError::Invalid); 331 } 332 } 333 334 Ok(TokenData { claims }) 335} 336 337pub fn get_algorithm_from_token(token: &str) -> Result<String, String> { 338 let parts: Vec<&str> = token.split('.').collect(); 339 if parts.len() != 3 { 340 return Err("Invalid token format".to_string()); 341 } 342 343 let header_bytes = URL_SAFE_NO_PAD 344 .decode(parts[0]) 345 .map_err(|e| format!("Base64 decode failed: {}", e))?; 346 347 let header: Header = 348 serde_json::from_slice(&header_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 349 350 Ok(header.alg) 351}