A better Rust ATProto crate

Compare changes

Choose any two refs to compare.

Changed files
+30 -30
crates
jacquard
src
jacquard-axum
jacquard-oauth
+1 -4
crates/jacquard/src/client/bff_session.rs
··· 107 107 })?; 108 108 Ok(Some(data)) 109 109 } 110 - Err(gloo_storage::errors::StorageError::KeyNotFound(err)) => { 111 - tracing::debug!("gloo error: {}", err); 112 - Ok(None) 113 - } 110 + Err(gloo_storage::errors::StorageError::KeyNotFound(_)) => Ok(None), 114 111 Err(e) => Err(SessionStoreError::Other( 115 112 format!("SessionStorage error: {}", e).into(), 116 113 )),
+1
crates/jacquard-axum/Cargo.toml
··· 39 39 [features] 40 40 default = ["service-auth"] 41 41 service-auth = ["jacquard-common/service-auth", "dep:jacquard-identity", "dep:multibase"] 42 + tracing = [] 42 43 43 44 [dev-dependencies] 44 45 axum-macros = "0.5.0"
+17 -3
crates/jacquard-axum/src/service_auth.rs
··· 572 572 573 573 match codec { 574 574 // p256-pub (0x1200) 575 - [0x80, 0x24] => PublicKey::from_p256_bytes(key_material).ok(), 575 + [0x80, 0x24] => PublicKey::from_p256_bytes(key_material) 576 + .inspect_err(|_e| { 577 + #[cfg(feature = "tracing")] 578 + tracing::error!("Failed to parse p256 public key: {}", _e); 579 + }) 580 + .ok(), 576 581 // secp256k1-pub (0xe7) 577 - [0xe7, 0x01] => PublicKey::from_k256_bytes(key_material).ok(), 578 - _ => None, 582 + [0xe7, 0x01] => PublicKey::from_k256_bytes(key_material) 583 + .inspect_err(|_e| { 584 + #[cfg(feature = "tracing")] 585 + tracing::error!("Failed to parse secp256k1 public key: {}", _e); 586 + }) 587 + .ok(), 588 + _ => { 589 + #[cfg(feature = "tracing")] 590 + tracing::error!("Unsupported public key multicodec: {:?}", codec); 591 + None 592 + } 579 593 } 580 594 } 581 595
+1 -5
crates/jacquard-oauth/src/atproto.rs
··· 242 242 redirect_uris, 243 243 application_type, 244 244 token_endpoint_auth_method: Some(auth_method.into()), 245 - grant_types: if keyset.is_some() { 246 - Some(metadata.grant_types.into_iter().map(|v| v.into()).collect()) 247 - } else { 248 - None 249 - }, 245 + grant_types: Some(metadata.grant_types.into_iter().map(|v| v.into()).collect()), 250 246 response_types: vec!["code".to_cowstr()], 251 247 scope: Some(Scope::serialize_multiple(metadata.scopes.as_slice())), 252 248 dpop_bound_access_tokens: Some(true),
+1 -3
crates/jacquard-oauth/src/client.rs
··· 280 280 token_set, 281 281 }; 282 282 283 - dbg!(&client_data); 284 - 285 283 self.create_session(client_data).await 286 284 } 287 285 Err(e) => Err(e.into()), ··· 298 296 } 299 297 300 298 pub async fn restore(&self, did: &Did<'_>, session_id: &str) -> Result<OAuthSession<T, S>> { 301 - self.create_session(self.registry.get(did, session_id, false).await?) 299 + self.create_session(self.registry.get(did, session_id, true).await?) 302 300 .await 303 301 } 304 302
+5 -5
crates/jacquard-oauth/src/dpop.rs
··· 150 150 /// Extract authorization hash from request headers 151 151 fn extract_ath(headers: &http::HeaderMap) -> Option<CowStr<'static>> { 152 152 headers 153 - .get("Authorization") 153 + .get("authorization") 154 154 .filter(|v| v.to_str().is_ok_and(|s| s.starts_with("DPoP "))) 155 155 .map(|auth| { 156 156 URL_SAFE_NO_PAD ··· 212 212 213 213 let next_nonce = response 214 214 .headers() 215 - .get("DPoP-Nonce") 215 + .get("dpop-nonce") 216 216 .and_then(|v| v.to_str().ok()) 217 - .map(|c| CowStr::from(c.to_string())); 217 + .map(|c| CowStr::copy_from_str(c)); 218 218 match &next_nonce { 219 219 Some(s) if next_nonce != init_nonce => { 220 220 store_nonce(data_source, is_to_auth_server, s.clone()); ··· 380 380 } 381 381 if !is_to_auth_server && status == 401 { 382 382 if let Some(www_auth) = headers 383 - .get("WWW-Authenticate") 383 + .get("www-authenticate") 384 384 .and_then(|v| v.to_str().ok()) 385 385 { 386 386 return www_auth.starts_with("DPoP") && www_auth.contains(r#"error="use_dpop_nonce""#); ··· 404 404 else if response.status() == 401 { 405 405 if let Some(www_auth) = response 406 406 .headers() 407 - .get("WWW-Authenticate") 407 + .get("www-authenticate") 408 408 .and_then(|v| v.to_str().ok()) 409 409 { 410 410 return www_auth.starts_with("DPoP") && www_auth.contains(r#"error="use_dpop_nonce""#);
+4 -10
crates/jacquard-oauth/src/request.rs
··· 311 311 pub fn is_permanent(&self) -> bool { 312 312 match &self.kind { 313 313 RequestErrorKind::NoRefreshToken => true, 314 - RequestErrorKind::HttpStatusWithBody { body, .. } => { 315 - body.get("error") 316 - .and_then(|e| e.as_str()) 317 - .is_some_and(|e| matches!(e, "invalid_grant" | "access_denied")) 318 - } 314 + RequestErrorKind::HttpStatusWithBody { body, .. } => body 315 + .get("error") 316 + .and_then(|e| e.as_str()) 317 + .is_some_and(|e| matches!(e, "invalid_grant" | "access_denied")), 319 318 _ => false, 320 319 } 321 320 } ··· 518 517 prompt: prompt.map(CowStr::from), 519 518 }; 520 519 521 - #[cfg(feature = "tracing")] 522 - tracing::debug!( 523 - parameters = ?parameters, 524 - "par:" 525 - ); 526 520 if metadata 527 521 .server_metadata 528 522 .pushed_authorization_request_endpoint