this repo has no description
1use super::{Claims, Header, TokenData, UnsafeClaims}; 2use super::token::{TOKEN_TYPE_ACCESS, TOKEN_TYPE_REFRESH, SCOPE_ACCESS, SCOPE_REFRESH, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED}; 3use anyhow::{Context, Result, anyhow}; 4use base64::Engine as _; 5use base64::engine::general_purpose::URL_SAFE_NO_PAD; 6use chrono::Utc; 7use hmac::{Hmac, Mac}; 8use k256::ecdsa::{Signature, SigningKey, VerifyingKey, signature::Verifier}; 9use sha2::Sha256; 10use subtle::ConstantTimeEq; 11 12type HmacSha256 = Hmac<Sha256>; 13 14pub fn get_did_from_token(token: &str) -> Result<String, String> { 15 let parts: Vec<&str> = token.split('.').collect(); 16 if parts.len() != 3 { 17 return Err("Invalid token format".to_string()); 18 } 19 20 let payload_bytes = URL_SAFE_NO_PAD 21 .decode(parts[1]) 22 .map_err(|e| format!("Base64 decode failed: {}", e))?; 23 24 let claims: UnsafeClaims = 25 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 26 27 Ok(claims.sub.unwrap_or(claims.iss)) 28} 29 30pub fn get_jti_from_token(token: &str) -> Result<String, String> { 31 let parts: Vec<&str> = token.split('.').collect(); 32 if parts.len() != 3 { 33 return Err("Invalid token format".to_string()); 34 } 35 36 let payload_bytes = URL_SAFE_NO_PAD 37 .decode(parts[1]) 38 .map_err(|e| format!("Base64 decode failed: {}", e))?; 39 40 let claims: serde_json::Value = 41 serde_json::from_slice(&payload_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 42 43 claims.get("jti") 44 .and_then(|j| j.as_str()) 45 .map(|s| s.to_string()) 46 .ok_or_else(|| "No jti claim in token".to_string()) 47} 48 49pub fn verify_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 50 verify_token_internal(token, key_bytes, None, None) 51} 52 53pub fn verify_access_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 54 verify_token_internal( 55 token, 56 key_bytes, 57 Some(TOKEN_TYPE_ACCESS), 58 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]), 59 ) 60} 61 62pub fn verify_refresh_token(token: &str, key_bytes: &[u8]) -> Result<TokenData<Claims>> { 63 verify_token_internal( 64 token, 65 key_bytes, 66 Some(TOKEN_TYPE_REFRESH), 67 Some(&[SCOPE_REFRESH]), 68 ) 69} 70 71pub fn verify_access_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> { 72 verify_token_hs256_internal( 73 token, 74 secret, 75 Some(TOKEN_TYPE_ACCESS), 76 Some(&[SCOPE_ACCESS, SCOPE_APP_PASS, SCOPE_APP_PASS_PRIVILEGED]), 77 ) 78} 79 80pub fn verify_refresh_token_hs256(token: &str, secret: &[u8]) -> Result<TokenData<Claims>> { 81 verify_token_hs256_internal( 82 token, 83 secret, 84 Some(TOKEN_TYPE_REFRESH), 85 Some(&[SCOPE_REFRESH]), 86 ) 87} 88 89fn verify_token_internal( 90 token: &str, 91 key_bytes: &[u8], 92 expected_typ: Option<&str>, 93 allowed_scopes: Option<&[&str]>, 94) -> Result<TokenData<Claims>> { 95 let parts: Vec<&str> = token.split('.').collect(); 96 if parts.len() != 3 { 97 return Err(anyhow!("Invalid token format")); 98 } 99 100 let header_b64 = parts[0]; 101 let claims_b64 = parts[1]; 102 let signature_b64 = parts[2]; 103 104 let header_bytes = URL_SAFE_NO_PAD 105 .decode(header_b64) 106 .context("Base64 decode of header failed")?; 107 108 let header: Header = 109 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; 110 111 if let Some(expected) = expected_typ { 112 if header.typ != expected { 113 return Err(anyhow!("Invalid token type: expected {}, got {}", expected, header.typ)); 114 } 115 } 116 117 let signature_bytes = URL_SAFE_NO_PAD 118 .decode(signature_b64) 119 .context("Base64 decode of signature failed")?; 120 121 let signature = Signature::from_slice(&signature_bytes) 122 .map_err(|e| anyhow!("Invalid signature format: {}", e))?; 123 124 let signing_key = SigningKey::from_slice(key_bytes)?; 125 let verifying_key = VerifyingKey::from(&signing_key); 126 127 let message = format!("{}.{}", header_b64, claims_b64); 128 verifying_key 129 .verify(message.as_bytes(), &signature) 130 .map_err(|e| anyhow!("Signature verification failed: {}", e))?; 131 132 let claims_bytes = URL_SAFE_NO_PAD 133 .decode(claims_b64) 134 .context("Base64 decode of claims failed")?; 135 136 let claims: Claims = 137 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 138 139 let now = Utc::now().timestamp() as usize; 140 if claims.exp < now { 141 return Err(anyhow!("Token expired")); 142 } 143 144 if let Some(scopes) = allowed_scopes { 145 let token_scope = claims.scope.as_deref().unwrap_or(""); 146 if !scopes.contains(&token_scope) { 147 return Err(anyhow!("Invalid token scope: {}", token_scope)); 148 } 149 } 150 151 Ok(TokenData { claims }) 152} 153 154fn verify_token_hs256_internal( 155 token: &str, 156 secret: &[u8], 157 expected_typ: Option<&str>, 158 allowed_scopes: Option<&[&str]>, 159) -> Result<TokenData<Claims>> { 160 let parts: Vec<&str> = token.split('.').collect(); 161 if parts.len() != 3 { 162 return Err(anyhow!("Invalid token format")); 163 } 164 165 let header_b64 = parts[0]; 166 let claims_b64 = parts[1]; 167 let signature_b64 = parts[2]; 168 169 let header_bytes = URL_SAFE_NO_PAD 170 .decode(header_b64) 171 .context("Base64 decode of header failed")?; 172 173 let header: Header = 174 serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; 175 176 if header.alg != "HS256" { 177 return Err(anyhow!("Expected HS256 algorithm, got {}", header.alg)); 178 } 179 180 if let Some(expected) = expected_typ { 181 if header.typ != expected { 182 return Err(anyhow!("Invalid token type: expected {}, got {}", expected, header.typ)); 183 } 184 } 185 186 let signature_bytes = URL_SAFE_NO_PAD 187 .decode(signature_b64) 188 .context("Base64 decode of signature failed")?; 189 190 let message = format!("{}.{}", header_b64, claims_b64); 191 192 let mut mac = HmacSha256::new_from_slice(secret) 193 .map_err(|e| anyhow!("Invalid secret: {}", e))?; 194 mac.update(message.as_bytes()); 195 196 let expected_signature = mac.finalize().into_bytes(); 197 let is_valid: bool = signature_bytes.ct_eq(&expected_signature).into(); 198 199 if !is_valid { 200 return Err(anyhow!("Signature verification failed")); 201 } 202 203 let claims_bytes = URL_SAFE_NO_PAD 204 .decode(claims_b64) 205 .context("Base64 decode of claims failed")?; 206 207 let claims: Claims = 208 serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; 209 210 let now = Utc::now().timestamp() as usize; 211 if claims.exp < now { 212 return Err(anyhow!("Token expired")); 213 } 214 215 if let Some(scopes) = allowed_scopes { 216 let token_scope = claims.scope.as_deref().unwrap_or(""); 217 if !scopes.contains(&token_scope) { 218 return Err(anyhow!("Invalid token scope: {}", token_scope)); 219 } 220 } 221 222 Ok(TokenData { claims }) 223} 224 225pub fn get_algorithm_from_token(token: &str) -> Result<String, String> { 226 let parts: Vec<&str> = token.split('.').collect(); 227 if parts.len() != 3 { 228 return Err("Invalid token format".to_string()); 229 } 230 231 let header_bytes = URL_SAFE_NO_PAD 232 .decode(parts[0]) 233 .map_err(|e| format!("Base64 decode failed: {}", e))?; 234 235 let header: Header = 236 serde_json::from_slice(&header_bytes).map_err(|e| format!("JSON decode failed: {}", e))?; 237 238 Ok(header.alg) 239}