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