A better Rust ATProto crate

oauth url nonsense

Changed files
+23 -19
crates
jacquard
src
client
jacquard-oauth
+3 -1
crates/jacquard-oauth/src/client.rs
··· 264 264 let client_data = ClientSessionData { 265 265 account_did: token_set.sub.clone(), 266 266 session_id: auth_req_info.state, 267 - host_url: token_set.iss.clone(), 267 + host_url: token_set.aud.clone(), 268 268 authserver_url: auth_req_info.authserver_url.to_cowstr(), 269 269 authserver_token_endpoint: auth_req_info.authserver_token_endpoint, 270 270 authserver_revocation_endpoint: auth_req_info.authserver_revocation_endpoint, ··· 279 279 }, 280 280 token_set, 281 281 }; 282 + 283 + dbg!(&client_data); 282 284 283 285 self.create_session(client_data).await 284 286 }
+1 -2
crates/jacquard-oauth/src/request.rs
··· 528 528 }; 529 529 let auth_req_data = AuthRequestData { 530 530 state, 531 - authserver_url: url::Url::parse(&metadata.server_metadata.issuer) 532 - .expect("Failed to parse issuer URL"), 531 + authserver_url: metadata.server_metadata.issuer.clone(), 533 532 account_did: None, 534 533 scopes, 535 534 request_uri: par_response.request_uri.to_cowstr().into_static(),
+4 -4
crates/jacquard-oauth/src/resolver.rs
··· 793 793 .await 794 794 .map_err(|e| ResolverError::transport(e))?; 795 795 if res.status() == StatusCode::OK { 796 - let mut metadata = serde_json::from_slice::<OAuthAuthorizationServerMetadata>(res.body())?; 796 + let metadata = serde_json::from_slice::<OAuthAuthorizationServerMetadata>(res.body())?; 797 797 // https://datatracker.ietf.org/doc/html/rfc8414#section-3.3 798 798 // Accept semantically equivalent issuer (normalize to the requested URL form) 799 799 if issuer_equivalent(&metadata.issuer, server.as_str()) { 800 - metadata.issuer = server.as_str().into(); 800 + // if equivalent, keep the canonical form 801 801 Ok(metadata.into_static()) 802 802 } else { 803 803 Err(ResolverError::authorization_server_metadata( ··· 827 827 .await 828 828 .map_err(|e| ResolverError::transport(e))?; 829 829 if res.status() == StatusCode::OK { 830 - let mut metadata = serde_json::from_slice::<OAuthProtectedResourceMetadata>(res.body())?; 830 + let metadata = serde_json::from_slice::<OAuthProtectedResourceMetadata>(res.body())?; 831 831 // https://datatracker.ietf.org/doc/html/rfc8414#section-3.3 832 832 // Accept semantically equivalent resource URL (normalize to the requested URL form) 833 833 if issuer_equivalent(&metadata.resource, server.as_str()) { 834 - metadata.resource = server.as_str().into(); 834 + // if equivalent, keep the canonical form 835 835 Ok(metadata.into_static()) 836 836 } else { 837 837 Err(ResolverError::authorization_server_metadata(
+2 -3
crates/jacquard-oauth/src/session.rs
··· 22 22 use serde::{Deserialize, Serialize}; 23 23 use smol_str::{SmolStr, format_smolstr}; 24 24 use tokio::sync::Mutex; 25 - use url::Url; 26 25 27 26 pub trait DpopDataSource { 28 27 fn key(&self) -> &Key; ··· 148 147 pub state: CowStr<'s>, 149 148 150 149 // URL of the auth server (eg, PDS or entryway) 151 - pub authserver_url: Url, 150 + pub authserver_url: CowStr<'s>, 152 151 153 152 // If the flow started with an account identifier (DID or handle), it should be persisted, to verify against the initial token response. 154 153 #[serde(skip_serializing_if = "std::option::Option::is_none")] ··· 186 185 pkce_verifier: self.pkce_verifier.into_static(), 187 186 dpop_data: self.dpop_data.into_static(), 188 187 state: self.state.into_static(), 189 - authserver_url: self.authserver_url, 188 + authserver_url: self.authserver_url.into_static(), 190 189 account_did: self.account_did.into_static(), 191 190 scopes: self.scopes.into_static(), 192 191 }
+13 -9
crates/jacquard/src/client/token.rs
··· 189 189 pub dpop_authserver_nonce: Option<String>, 190 190 } 191 191 192 - impl From<AuthRequestData<'_>> for OAuthState { 193 - fn from(value: AuthRequestData) -> Self { 194 - OAuthState { 195 - authserver_url: value.authserver_url, 192 + impl TryFrom<AuthRequestData<'_>> for OAuthState { 193 + type Error = url::ParseError; 194 + 195 + fn try_from(value: AuthRequestData) -> Result<Self, Self::Error> { 196 + Ok(OAuthState { 197 + authserver_url: Url::parse(&value.authserver_url)?, 196 198 account_did: value.account_did.map(|s| s.to_string()), 197 199 scopes: value.scopes.into_iter().map(|s| s.to_string()).collect(), 198 200 request_uri: value.request_uri.to_string(), ··· 204 206 dpop_key: value.dpop_data.dpop_key, 205 207 dpop_authserver_nonce: value.dpop_data.dpop_authserver_nonce.map(|s| s.to_string()), 206 208 state: value.state.to_string(), 207 - } 209 + }) 208 210 } 209 211 } 210 212 211 213 impl From<OAuthState> for AuthRequestData<'_> { 212 214 fn from(value: OAuthState) -> Self { 213 215 AuthRequestData { 214 - authserver_url: value.authserver_url, 216 + authserver_url: value.authserver_url.to_cowstr(), 215 217 state: value.state.to_cowstr(), 216 218 account_did: value.account_did.map(|s| Did::from(s).into_static()), 217 219 authserver_revocation_endpoint: value ··· 317 319 auth_req_info: &AuthRequestData<'_>, 318 320 ) -> Result<(), SessionStoreError> { 319 321 let key = format!("authreq_{}", auth_req_info.state); 320 - self.0 321 - .set(key, StoredSession::OAuthState(auth_req_info.clone().into())) 322 - .await?; 322 + let state = auth_req_info 323 + .clone() 324 + .try_into() 325 + .map_err(|e: url::ParseError| SessionStoreError::Other(Box::new(e)))?; 326 + self.0.set(key, StoredSession::OAuthState(state)).await?; 323 327 Ok(()) 324 328 } 325 329