diff --git a/crates/jacquard-oauth/src/client.rs b/crates/jacquard-oauth/src/client.rs --- a/crates/jacquard-oauth/src/client.rs +++ b/crates/jacquard-oauth/src/client.rs @@ -264,7 +264,7 @@ let client_data = ClientSessionData { account_did: token_set.sub.clone(), session_id: auth_req_info.state, - host_url: token_set.iss.clone(), + host_url: token_set.aud.clone(), authserver_url: auth_req_info.authserver_url.to_cowstr(), authserver_token_endpoint: auth_req_info.authserver_token_endpoint, authserver_revocation_endpoint: auth_req_info.authserver_revocation_endpoint, @@ -279,6 +279,8 @@ }, token_set, }; + + dbg!(&client_data); self.create_session(client_data).await } diff --git a/crates/jacquard-oauth/src/request.rs b/crates/jacquard-oauth/src/request.rs --- a/crates/jacquard-oauth/src/request.rs +++ b/crates/jacquard-oauth/src/request.rs @@ -528,8 +528,7 @@ }; let auth_req_data = AuthRequestData { state, - authserver_url: url::Url::parse(&metadata.server_metadata.issuer) - .expect("Failed to parse issuer URL"), + authserver_url: metadata.server_metadata.issuer.clone(), account_did: None, scopes, request_uri: par_response.request_uri.to_cowstr().into_static(), diff --git a/crates/jacquard-oauth/src/resolver.rs b/crates/jacquard-oauth/src/resolver.rs --- a/crates/jacquard-oauth/src/resolver.rs +++ b/crates/jacquard-oauth/src/resolver.rs @@ -793,11 +793,11 @@ .await .map_err(|e| ResolverError::transport(e))?; if res.status() == StatusCode::OK { - let mut metadata = serde_json::from_slice::(res.body())?; + let metadata = serde_json::from_slice::(res.body())?; // https://datatracker.ietf.org/doc/html/rfc8414#section-3.3 // Accept semantically equivalent issuer (normalize to the requested URL form) if issuer_equivalent(&metadata.issuer, server.as_str()) { - metadata.issuer = server.as_str().into(); + // if equivalent, keep the canonical form Ok(metadata.into_static()) } else { Err(ResolverError::authorization_server_metadata( @@ -827,11 +827,11 @@ .await .map_err(|e| ResolverError::transport(e))?; if res.status() == StatusCode::OK { - let mut metadata = serde_json::from_slice::(res.body())?; + let metadata = serde_json::from_slice::(res.body())?; // https://datatracker.ietf.org/doc/html/rfc8414#section-3.3 // Accept semantically equivalent resource URL (normalize to the requested URL form) if issuer_equivalent(&metadata.resource, server.as_str()) { - metadata.resource = server.as_str().into(); + // if equivalent, keep the canonical form Ok(metadata.into_static()) } else { Err(ResolverError::authorization_server_metadata( diff --git a/crates/jacquard-oauth/src/session.rs b/crates/jacquard-oauth/src/session.rs --- a/crates/jacquard-oauth/src/session.rs +++ b/crates/jacquard-oauth/src/session.rs @@ -22,7 +22,6 @@ use serde::{Deserialize, Serialize}; use smol_str::{SmolStr, format_smolstr}; use tokio::sync::Mutex; -use url::Url; pub trait DpopDataSource { fn key(&self) -> &Key; @@ -148,7 +147,7 @@ pub state: CowStr<'s>, // URL of the auth server (eg, PDS or entryway) - pub authserver_url: Url, + pub authserver_url: CowStr<'s>, // If the flow started with an account identifier (DID or handle), it should be persisted, to verify against the initial token response. #[serde(skip_serializing_if = "std::option::Option::is_none")] @@ -186,7 +185,7 @@ pkce_verifier: self.pkce_verifier.into_static(), dpop_data: self.dpop_data.into_static(), state: self.state.into_static(), - authserver_url: self.authserver_url, + authserver_url: self.authserver_url.into_static(), account_did: self.account_did.into_static(), scopes: self.scopes.into_static(), } diff --git a/crates/jacquard/src/client/token.rs b/crates/jacquard/src/client/token.rs --- a/crates/jacquard/src/client/token.rs +++ b/crates/jacquard/src/client/token.rs @@ -189,10 +189,12 @@ pub dpop_authserver_nonce: Option, } -impl From> for OAuthState { - fn from(value: AuthRequestData) -> Self { - OAuthState { - authserver_url: value.authserver_url, +impl TryFrom> for OAuthState { + type Error = url::ParseError; + + fn try_from(value: AuthRequestData) -> Result { + Ok(OAuthState { + authserver_url: Url::parse(&value.authserver_url)?, account_did: value.account_did.map(|s| s.to_string()), scopes: value.scopes.into_iter().map(|s| s.to_string()).collect(), request_uri: value.request_uri.to_string(), @@ -204,14 +206,14 @@ dpop_key: value.dpop_data.dpop_key, dpop_authserver_nonce: value.dpop_data.dpop_authserver_nonce.map(|s| s.to_string()), state: value.state.to_string(), - } + }) } } impl From for AuthRequestData<'_> { fn from(value: OAuthState) -> Self { AuthRequestData { - authserver_url: value.authserver_url, + authserver_url: value.authserver_url.to_cowstr(), state: value.state.to_cowstr(), account_did: value.account_did.map(|s| Did::from(s).into_static()), authserver_revocation_endpoint: value @@ -317,9 +319,11 @@ auth_req_info: &AuthRequestData<'_>, ) -> Result<(), SessionStoreError> { let key = format!("authreq_{}", auth_req_info.state); - self.0 - .set(key, StoredSession::OAuthState(auth_req_info.clone().into())) - .await?; + let state = auth_req_info + .clone() + .try_into() + .map_err(|e: url::ParseError| SessionStoreError::Other(Box::new(e)))?; + self.0.set(key, StoredSession::OAuthState(state)).await?; Ok(()) }