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