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