From 1411506d8cd5bc4e3173b12ef373e3e2ba13c830 Mon Sep 17 00:00:00 2001 From: Lewis Date: Sat, 11 Jul 2026 14:45:39 +0300 Subject: [PATCH] auth: typed TokenVerifyError from es256k verifier Lewis: May this revision serve well! --- crates/tranquil-auth/src/lib.rs | 4 +- crates/tranquil-auth/src/verify.rs | 130 +++++----------------------- crates/tranquil-pds/src/auth/mod.rs | 8 +- 3 files changed, 28 insertions(+), 114 deletions(-) diff --git a/crates/tranquil-auth/src/lib.rs b/crates/tranquil-auth/src/lib.rs index d213dc9..1f9b8c6 100644 --- a/crates/tranquil-auth/src/lib.rs +++ b/crates/tranquil-auth/src/lib.rs @@ -25,6 +25,6 @@ pub use types::{ pub use verify::{ get_algorithm_from_token, get_did_from_token, get_jti_from_token, verify_access_token, - verify_access_token_hs256, verify_access_token_typed, verify_refresh_token, - verify_refresh_token_hs256, verify_token, + verify_access_token_hs256, verify_refresh_token, verify_refresh_token_hs256, verify_token, + verify_token_es256k, }; diff --git a/crates/tranquil-auth/src/verify.rs b/crates/tranquil-auth/src/verify.rs index 71d09fc..b004372 100644 --- a/crates/tranquil-auth/src/verify.rs +++ b/crates/tranquil-auth/src/verify.rs @@ -66,11 +66,11 @@ pub fn get_algorithm_from_token(token: &str) -> Result Result> { - verify_token_internal(token, key_bytes, None, None) + verify_token_es256k(token, key_bytes, None, None).map_err(anyhow::Error::from) } pub fn verify_access_token(token: &str, key_bytes: &[u8]) -> Result> { - verify_token_internal( + verify_token_es256k( token, key_bytes, Some(TokenType::Access), @@ -80,15 +80,17 @@ pub fn verify_access_token(token: &str, key_bytes: &[u8]) -> Result Result> { - verify_token_internal( + verify_token_es256k( token, key_bytes, Some(TokenType::Refresh), Some(&[TokenScope::Refresh]), ) + .map_err(anyhow::Error::from) } pub fn verify_access_token_hs256(token: &str, secret: &[u8]) -> Result> { @@ -113,15 +115,15 @@ pub fn verify_refresh_token_hs256(token: &str, secret: &[u8]) -> Result, allowed_scopes: Option<&[TokenScope]>, -) -> Result> { +) -> Result, TokenVerifyError> { let parts: Vec<&str> = token.split('.').collect(); if parts.len() != 3 { - return Err(anyhow!("Invalid token format")); + return Err(TokenVerifyError::Invalid("Invalid token format")); } let header_b64 = parts[0]; @@ -130,46 +132,43 @@ fn verify_token_internal( let header_bytes = URL_SAFE_NO_PAD .decode(header_b64) - .context("Base64 decode of header failed")?; + .map_err(|_| TokenVerifyError::Invalid("Base64 decode of header failed"))?; - let header: Header = - serde_json::from_slice(&header_bytes).context("JSON decode of header failed")?; + let header: Header = serde_json::from_slice(&header_bytes) + .map_err(|_| TokenVerifyError::Invalid("JSON decode of header failed"))?; if let Some(expected) = expected_typ && header.typ != expected { - return Err(anyhow!( - "Invalid token type: expected {}, got {}", - expected, - header.typ - )); + return Err(TokenVerifyError::Invalid("Invalid token type")); } let signature_bytes = URL_SAFE_NO_PAD .decode(signature_b64) - .context("Base64 decode of signature failed")?; + .map_err(|_| TokenVerifyError::Invalid("Base64 decode of signature failed"))?; let signature = Signature::from_slice(&signature_bytes) - .map_err(|e| anyhow!("Invalid signature format: {}", e))?; + .map_err(|_| TokenVerifyError::Invalid("Invalid signature format"))?; - let signing_key = SigningKey::from_slice(key_bytes)?; + let signing_key = SigningKey::from_slice(key_bytes) + .map_err(|_| TokenVerifyError::Invalid("Invalid signing key"))?; let verifying_key = VerifyingKey::from(&signing_key); let message = format!("{}.{}", header_b64, claims_b64); verifying_key .verify(message.as_bytes(), &signature) - .map_err(|e| anyhow!("Signature verification failed: {}", e))?; + .map_err(|_| TokenVerifyError::Invalid("Signature verification failed"))?; let claims_bytes = URL_SAFE_NO_PAD .decode(claims_b64) - .context("Base64 decode of claims failed")?; + .map_err(|_| TokenVerifyError::Invalid("Base64 decode of claims failed"))?; - let claims: Claims = - serde_json::from_slice(&claims_bytes).context("JSON decode of claims failed")?; + let claims: Claims = serde_json::from_slice(&claims_bytes) + .map_err(|_| TokenVerifyError::Invalid("JSON decode of claims failed"))?; let now = Utc::now().timestamp(); if claims.exp < now { - return Err(anyhow!("Token expired")); + return Err(TokenVerifyError::Expired); } if let Some(scopes) = allowed_scopes { @@ -180,7 +179,7 @@ fn verify_token_internal( .parse() .unwrap_or_else(|e| match e {}); if !scopes.contains(&token_scope) { - return Err(anyhow!("Invalid token scope: {}", token_scope)); + return Err(TokenVerifyError::Invalid("Invalid token scope")); } } @@ -266,88 +265,3 @@ fn verify_token_hs256_internal( Ok(TokenData { claims }) } - -pub fn verify_access_token_typed( - token: &str, - key_bytes: &[u8], -) -> Result, TokenVerifyError> { - verify_token_typed_internal(token, key_bytes, Some(TokenType::Access), None) -} - -fn verify_token_typed_internal( - token: &str, - key_bytes: &[u8], - expected_typ: Option, - allowed_scopes: Option<&[TokenScope]>, -) -> Result, TokenVerifyError> { - let parts: Vec<&str> = token.split('.').collect(); - if parts.len() != 3 { - return Err(TokenVerifyError::Invalid); - } - - let header_b64 = parts[0]; - let claims_b64 = parts[1]; - let signature_b64 = parts[2]; - - let Ok(header_bytes) = URL_SAFE_NO_PAD.decode(header_b64) else { - return Err(TokenVerifyError::Invalid); - }; - - let Ok(header) = serde_json::from_slice::
(&header_bytes) else { - return Err(TokenVerifyError::Invalid); - }; - - if let Some(expected) = expected_typ - && header.typ != expected - { - return Err(TokenVerifyError::Invalid); - } - - let Ok(signature_bytes) = URL_SAFE_NO_PAD.decode(signature_b64) else { - return Err(TokenVerifyError::Invalid); - }; - - let Ok(signature) = Signature::from_slice(&signature_bytes) else { - return Err(TokenVerifyError::Invalid); - }; - - let Ok(signing_key) = SigningKey::from_slice(key_bytes) else { - return Err(TokenVerifyError::Invalid); - }; - let verifying_key = VerifyingKey::from(&signing_key); - - let message = format!("{}.{}", header_b64, claims_b64); - if verifying_key - .verify(message.as_bytes(), &signature) - .is_err() - { - return Err(TokenVerifyError::Invalid); - } - - let Ok(claims_bytes) = URL_SAFE_NO_PAD.decode(claims_b64) else { - return Err(TokenVerifyError::Invalid); - }; - - let Ok(claims) = serde_json::from_slice::(&claims_bytes) else { - return Err(TokenVerifyError::Invalid); - }; - - let now = Utc::now().timestamp(); - if claims.exp < now { - return Err(TokenVerifyError::Expired); - } - - if let Some(scopes) = allowed_scopes { - let token_scope: TokenScope = claims - .scope - .as_deref() - .unwrap_or("") - .parse() - .unwrap_or_else(|e| match e {}); - if !scopes.contains(&token_scope) { - return Err(TokenVerifyError::Invalid); - } - } - - Ok(TokenData { claims }) -} diff --git a/crates/tranquil-pds/src/auth/mod.rs b/crates/tranquil-pds/src/auth/mod.rs index 51f98c0..1e8ac56 100644 --- a/crates/tranquil-pds/src/auth/mod.rs +++ b/crates/tranquil-pds/src/auth/mod.rs @@ -53,8 +53,8 @@ pub use tranquil_auth::{ create_service_token_hs256, generate_backup_codes, generate_qr_png_base64, generate_totp_secret, generate_totp_uri, get_algorithm_from_token, get_did_from_token, get_jti_from_token, hash_backup_code, is_backup_code_format, verify_access_token, - verify_access_token_hs256, verify_access_token_typed, verify_backup_code, verify_refresh_token, - verify_refresh_token_hs256, verify_token, verify_totp_code, + verify_access_token_hs256, verify_backup_code, verify_refresh_token, + verify_refresh_token_hs256, verify_token, verify_token_es256k, verify_totp_code, }; pub fn lxm_permits(lxm: &str, expected: &str) -> bool { @@ -408,7 +408,7 @@ async fn validate_bearer_token_with_options_internal( return Err(TokenValidationError::AccountTakedown); } - match verify_access_token_typed(token, &decrypted_key) { + match verify_token_es256k(token, &decrypted_key, Some(TokenType::Access), None) { Ok(token_data) => { let jti = &token_data.claims.jti; let session_cache_key = crate::cache_keys::session_key(&did, jti); @@ -473,7 +473,7 @@ async fn validate_bearer_token_with_options_internal( Err(TokenVerifyError::Expired) => { return Err(TokenValidationError::TokenExpired); } - Err(TokenVerifyError::Invalid) => {} + Err(TokenVerifyError::Invalid(_)) => {} } } }