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