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 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 verify_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 54 verify_token_internal(token, key_bytes, None, None) 55} 56 57pub fn verify_access_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 58 verify_token_internal( 59 token, 60 key_bytes, 61 Some(TOKEN_TYPE_ACCESS), 62 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]), 63 ) 64} 65 66pub fn verify_refresh_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 67 verify_token_internal( 68 token, 69 key_bytes, 70 Some(TOKEN_TYPE_REFRESH), 71 Some(&[SCOPE_REFRESH]), 72 ) 73} 74 75pub fn verify_access_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> { 76 verify_token_hs256_internal( 77 token, 78 secret, 79 Some(TOKEN_TYPE_ACCESS), 80 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]), 81 ) 82} 83 84pub fn verify_refresh_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> { 85 verify_token_hs256_internal( 86 token, 87 secret, 88 Some(TOKEN_TYPE_REFRESH), 89 Some(&[SCOPE_REFRESH]), 90 ) 91} 92 93fn verify_token_internal( 94 token: &str, 95 key_bytes: &[u8], 96 expected_typ: Option<&str>, 97 allowed_scopes: Option<&[&str]>, 98) -> Result<TokenData<Claims>> { 99 let parts: Vec<&str> = token.split('.').collect(); 100 if parts.len() != 3 { 101 return Err(anyhow!("Invalid token format")); 102 } 103 104 let header_b64 = parts[0]; 105 let claims_b64 = parts[1]; 106 let signature_b64 = parts[2]; 107 108 let header_bytes = URL_SAFE_NO_PAD 109 .decode(header_b64) 110 .context("Base64 decode of header failed")?; 111 112 let header: Header = 113 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; 114 115 if let Some(expected) = expected_typ 116 && header.typ != expected 117 { 118 return Err(anyhow!( 119 "Invalid token type: expected {}, got {}", 120 expected, 121 header.typ 122 )); 123 } 124 125 let signature_bytes = URL_SAFE_NO_PAD 126 .decode(signature_b64) 127 .context("Base64 decode of signature failed")?; 128 129 let signature = Signature::from_slice(&signature_bytes) 130 .map_err(|e| anyhow!("Invalid signature format: {}", e))?; 131 132 let signing_key = SigningKey::from_slice(key_bytes)?; 133 let verifying_key = VerifyingKey::from(&signing_key); 134 135 let message = format!("{}.{}", header_b64, claims_b64); 136 verifying_key 137 .verify(message.as_bytes(), &signature) 138 .map_err(|e| anyhow!("Signature verification failed: {}", e))?; 139 140 let claims_bytes = URL_SAFE_NO_PAD 141 .decode(claims_b64) 142 .context("Base64 decode of claims failed")?; 143 144 let claims: Claims = 145 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 146 147 let now = Utc::now().timestamp() as usize; 148 if claims.exp < now { 149 return Err(anyhow!("Token expired")); 150 } 151 152 if let Some(scopes) = allowed_scopes { 153 let token_scope = claims.scope.as_deref().unwrap_or(""); 154 if !scopes.contains(&token_scope) { 155 return Err(anyhow!("Invalid token scope: {}", token_scope)); 156 } 157 } 158 159 Ok(TokenData { claims }) 160} 161 162fn verify_token_hs256_internal( 163 token: &str, 164 secret: &[u8], 165 expected_typ: Option<&str>, 166 allowed_scopes: Option<&[&str]>, 167) -> Result<TokenData<Claims>> { 168 let parts: Vec<&str> = token.split('.').collect(); 169 if parts.len() != 3 { 170 return Err(anyhow!("Invalid token format")); 171 } 172 173 let header_b64 = parts[0]; 174 let claims_b64 = parts[1]; 175 let signature_b64 = parts[2]; 176 177 let header_bytes = URL_SAFE_NO_PAD 178 .decode(header_b64) 179 .context("Base64 decode of header failed")?; 180 181 let header: Header = 182 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; 183 184 if header.alg != "HS256" { 185 return Err(anyhow!("Expected HS256 algorithm, got {}", header.alg)); 186 } 187 188 if let Some(expected) = expected_typ 189 && header.typ != expected 190 { 191 return Err(anyhow!( 192 "Invalid token type: expected {}, got {}", 193 expected, 194 header.typ 195 )); 196 } 197 198 let signature_bytes = URL_SAFE_NO_PAD 199 .decode(signature_b64) 200 .context("Base64 decode of signature failed")?; 201 202 let message = format!("{}.{}", header_b64, claims_b64); 203 204 let mut mac = 205 HmacSha256::new_from_slice(secret).map_err(|e| anyhow!("Invalid secret: {}", e))?; 206 mac.update(message.as_bytes()); 207 208 let expected_signature = mac.finalize().into_bytes(); 209 let is_valid: bool = signature_bytes.ct_eq(&expected_signature).into(); 210 211 if !is_valid { 212 return Err(anyhow!("Signature verification failed")); 213 } 214 215 let claims_bytes = URL_SAFE_NO_PAD 216 .decode(claims_b64) 217 .context("Base64 decode of claims failed")?; 218 219 let claims: Claims = 220 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 221 222 let now = Utc::now().timestamp() as usize; 223 if claims.exp < now { 224 return Err(anyhow!("Token expired")); 225 } 226 227 if let Some(scopes) = allowed_scopes { 228 let token_scope = claims.scope.as_deref().unwrap_or(""); 229 if !scopes.contains(&token_scope) { 230 return Err(anyhow!("Invalid token scope: {}", token_scope)); 231 } 232 } 233 234 Ok(TokenData { claims }) 235} 236 237pub fn get_algorithm_from_token(token: &str) -> Result<String, String> { 238 let parts: Vec<&str> = token.split('.').collect(); 239 if parts.len() != 3 { 240 return Err("Invalid token format".to_string()); 241 } 242 243 let header_bytes = URL_SAFE_NO_PAD 244 .decode(parts[0]) 245 .map_err(|e| format!("Base64 decode failed: {}", e))?; 246 247 let header: Header = 248 serde_json::from_slice(&header_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 249 250 Ok(header.alg) 251}