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 return Err(anyhow!( 118 "Invalid token type: expected {}, got {}", 119 expected, 120 header.typ 121 )); 122 } 123 124 let signature_bytes = URL_SAFE_NO_PAD 125 .decode(signature_b64) 126 .context("Base64 decode of signature failed")?; 127 128 let signature = Signature::from_slice(&signature_bytes) 129 .map_err(|e| anyhow!("Invalid signature format: {}", e))?; 130 131 let signing_key = SigningKey::from_slice(key_bytes)?; 132 let verifying_key = VerifyingKey::from(&signing_key); 133 134 let message = format!("{}.{}", header_b64, claims_b64); 135 verifying_key 136 .verify(message.as_bytes(), &signature) 137 .map_err(|e| anyhow!("Signature verification failed: {}", e))?; 138 139 let claims_bytes = URL_SAFE_NO_PAD 140 .decode(claims_b64) 141 .context("Base64 decode of claims failed")?; 142 143 let claims: Claims = 144 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 145 146 let now = Utc::now().timestamp() as usize; 147 if claims.exp < now { 148 return Err(anyhow!("Token expired")); 149 } 150 151 if let Some(scopes) = allowed_scopes { 152 let token_scope = claims.scope.as_deref().unwrap_or(""); 153 if !scopes.contains(&token_scope) { 154 return Err(anyhow!("Invalid token scope: {}", token_scope)); 155 } 156 } 157 158 Ok(TokenData { claims }) 159} 160 161fn verify_token_hs256_internal( 162 token: &str, 163 secret: &[u8], 164 expected_typ: Option<&str>, 165 allowed_scopes: Option<&[&str]>, 166) -> Result<TokenData<Claims>> { 167 let parts: Vec<&str> = token.split('.').collect(); 168 if parts.len() != 3 { 169 return Err(anyhow!("Invalid token format")); 170 } 171 172 let header_b64 = parts[0]; 173 let claims_b64 = parts[1]; 174 let signature_b64 = parts[2]; 175 176 let header_bytes = URL_SAFE_NO_PAD 177 .decode(header_b64) 178 .context("Base64 decode of header failed")?; 179 180 let header: Header = 181 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; 182 183 if header.alg != "HS256" { 184 return Err(anyhow!("Expected HS256 algorithm, got {}", header.alg)); 185 } 186 187 if let Some(expected) = expected_typ 188 && header.typ != expected { 189 return Err(anyhow!( 190 "Invalid token type: expected {}, got {}", 191 expected, 192 header.typ 193 )); 194 } 195 196 let signature_bytes = URL_SAFE_NO_PAD 197 .decode(signature_b64) 198 .context("Base64 decode of signature failed")?; 199 200 let message = format!("{}.{}", header_b64, claims_b64); 201 202 let mut mac = 203 HmacSha256::new_from_slice(secret).map_err(|e| anyhow!("Invalid secret: {}", e))?; 204 mac.update(message.as_bytes()); 205 206 let expected_signature = mac.finalize().into_bytes(); 207 let is_valid: bool = signature_bytes.ct_eq(&expected_signature).into(); 208 209 if !is_valid { 210 return Err(anyhow!("Signature verification failed")); 211 } 212 213 let claims_bytes = URL_SAFE_NO_PAD 214 .decode(claims_b64) 215 .context("Base64 decode of claims failed")?; 216 217 let claims: Claims = 218 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 219 220 let now = Utc::now().timestamp() as usize; 221 if claims.exp < now { 222 return Err(anyhow!("Token expired")); 223 } 224 225 if let Some(scopes) = allowed_scopes { 226 let token_scope = claims.scope.as_deref().unwrap_or(""); 227 if !scopes.contains(&token_scope) { 228 return Err(anyhow!("Invalid token scope: {}", token_scope)); 229 } 230 } 231 232 Ok(TokenData { claims }) 233} 234 235pub fn get_algorithm_from_token(token: &str) -> Result<String, String> { 236 let parts: Vec<&str> = token.split('.').collect(); 237 if parts.len() != 3 { 238 return Err("Invalid token format".to_string()); 239 } 240 241 let header_bytes = URL_SAFE_NO_PAD 242 .decode(parts[0]) 243 .map_err(|e| format!("Base64 decode failed: {}", e))?; 244 245 let header: Header = 246 serde_json::from_slice(&header_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 247 248 Ok(header.alg) 249}