diff --git a/atrium-oauth/oauth-client/src/oauth_client.rs b/atrium-oauth/oauth-client/src/oauth_client.rs index ca1534a3..fd19345b 100644 --- a/atrium-oauth/oauth-client/src/oauth_client.rs +++ b/atrium-oauth/oauth-client/src/oauth_client.rs @@ -11,7 +11,8 @@ use crate::types::{ TryIntoOAuthClientMetadata, }; use crate::utils::{compare_algos, generate_key, generate_nonce, get_random_values}; -use atrium_identity::{did::DidResolver, handle::HandleResolver, Resolver}; +use atrium_common::resolver::Resolver; +use atrium_identity::{did::DidResolver, handle::HandleResolver}; use atrium_xrpc::HttpClient; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; @@ -145,7 +146,9 @@ where } else { self.client_metadata.redirect_uris[0].clone() }; - let (metadata, identity) = self.resolver.resolve(input.as_ref()).await?; + let result = self.resolver.resolve(input.as_ref()).await?; + let (metadata, identity) = + result.ok_or_else(|| Error::Identity(atrium_identity::Error::NotFound))?; let Some(dpop_key) = Self::generate_dpop_key(&metadata) else { return Err(Error::Authorize("none of the algorithms worked".into())); }; diff --git a/atrium-oauth/oauth-client/src/resolver.rs b/atrium-oauth/oauth-client/src/resolver.rs index ad36e813..280462db 100644 --- a/atrium-oauth/oauth-client/src/resolver.rs +++ b/atrium-oauth/oauth-client/src/resolver.rs @@ -1,3 +1,11 @@ +use atrium_common::resolver::CachedResolver; +use atrium_common::resolver::Resolver; +use atrium_common::resolver::ThrottledResolver; +use atrium_common::types::cached::r#impl::Cache; +use atrium_common::types::cached::r#impl::CacheImpl; +use atrium_common::types::cached::CacheConfig; +use atrium_common::types::cached::Cacheable; +use atrium_common::types::throttled::Throttleable; mod oauth_authorization_server_resolver; mod oauth_protected_resource_resolver; @@ -7,10 +15,7 @@ use crate::types::{OAuthAuthorizationServerMetadata, OAuthProtectedResourceMetad use atrium_identity::identity_resolver::{ IdentityResolver, IdentityResolverConfig, ResolvedIdentity, }; -use atrium_identity::resolver::{ - Cacheable, CachedResolver, CachedResolverConfig, Throttleable, ThrottledResolver, -}; -use atrium_identity::{did::DidResolver, handle::HandleResolver, Resolver}; +use atrium_identity::{did::DidResolver, handle::HandleResolver}; use atrium_identity::{Error, Result}; use atrium_xrpc::HttpClient; use std::marker::PhantomData; @@ -19,13 +24,13 @@ use std::time::Duration; #[derive(Clone, Debug)] pub struct OAuthAuthorizationServerMetadataResolverConfig { - pub cache: CachedResolverConfig, + pub cache: CacheConfig, } impl Default for OAuthAuthorizationServerMetadataResolverConfig { fn default() -> Self { Self { - cache: CachedResolverConfig { + cache: CacheConfig { max_capacity: Some(100), time_to_live: Some(Duration::from_secs(60)), }, @@ -35,13 +40,13 @@ impl Default for OAuthAuthorizationServerMetadataResolverConfig { #[derive(Clone, Debug)] pub struct OAuthProtectedResourceMetadataResolverConfig { - pub cache: CachedResolverConfig, + pub cache: CacheConfig, } impl Default for OAuthProtectedResourceMetadataResolverConfig { fn default() -> Self { Self { - cache: CachedResolverConfig { + cache: CacheConfig { max_capacity: Some(100), time_to_live: Some(Duration::from_secs(60)), }, @@ -81,11 +86,11 @@ where let protected_resource_resolver = DefaultOAuthProtectedResourceResolver::new(http_client.clone()) .throttled() - .cached(config.authorization_server_metadata.cache); + .cached(CacheImpl::new(config.authorization_server_metadata.cache)); let authorization_server_resolver = DefaultOAuthAuthorizationServerResolver::new(http_client.clone()) .throttled() - .cached(config.protected_resource_metadata.cache); + .cached(CacheImpl::new(config.protected_resource_metadata.cache)); Self { identity_resolver: IdentityResolver::new(IdentityResolverConfig { did_resolver: config.did_resolver, @@ -108,7 +113,9 @@ where &self, issuer: impl AsRef, ) -> Result { - self.authorization_server_resolver.resolve(&issuer.as_ref().to_string()).await + let result = + self.authorization_server_resolver.resolve(&issuer.as_ref().to_string()).await?; + result.ok_or_else(|| Error::NotFound) } async fn resolve_from_service(&self, input: &str) -> Result { // Assume first that input is a PDS URL (as required by ATPROTO) @@ -122,7 +129,8 @@ where &self, input: &str, ) -> Result<(OAuthAuthorizationServerMetadata, ResolvedIdentity)> { - let identity = self.identity_resolver.resolve(input).await?; + let result = self.identity_resolver.resolve(input).await; + let identity = result.and_then(|result| result.ok_or_else(|| Error::NotFound))?; let metadata = self.get_resource_server_metadata(&identity.pds).await?; Ok((metadata, identity)) } @@ -130,7 +138,8 @@ where &self, pds: &str, ) -> Result { - let rs_metadata = self.protected_resource_resolver.resolve(&pds.to_string()).await?; + let result = self.protected_resource_resolver.resolve(&pds.to_string()).await?; + let rs_metadata = result.ok_or_else(|| Error::NotFound)?; // ATPROTO requires one, and only one, authorization server entry // > That document MUST contain a single item in the authorization_servers array. // https://github.com/bluesky-social/proposals/tree/main/0004-oauth#server-metadata @@ -182,16 +191,17 @@ where { type Input = str; type Output = (OAuthAuthorizationServerMetadata, Option); + type Error = Error; - async fn resolve(&self, input: &Self::Input) -> Result { + async fn resolve(&self, input: &Self::Input) -> Result> { // Allow using an entryway, or PDS url, directly as login input (e.g. // when the user forgot their handle, or when the handle does not // resolve to a DID) Ok(if input.starts_with("https://") { - (self.resolve_from_service(input.as_ref()).await?, None) + Some((self.resolve_from_service(input.as_ref()).await?, None)) } else { let (metadata, identity) = self.resolve_from_identity(input).await?; - (metadata, Some(identity)) + Some((metadata, Some(identity))) }) } } diff --git a/atrium-oauth/oauth-client/src/resolver/oauth_authorization_server_resolver.rs b/atrium-oauth/oauth-client/src/resolver/oauth_authorization_server_resolver.rs index e38428fe..df47b150 100644 --- a/atrium-oauth/oauth-client/src/resolver/oauth_authorization_server_resolver.rs +++ b/atrium-oauth/oauth-client/src/resolver/oauth_authorization_server_resolver.rs @@ -1,5 +1,6 @@ +use crate::resolver::Resolver; use crate::types::OAuthAuthorizationServerMetadata; -use atrium_identity::{Error, Resolver, Result}; +use atrium_identity::{Error, Result}; use atrium_xrpc::http::uri::Builder; use atrium_xrpc::http::{Request, StatusCode, Uri}; use atrium_xrpc::HttpClient; @@ -21,8 +22,9 @@ where { type Input = String; type Output = OAuthAuthorizationServerMetadata; + type Error = Error; - async fn resolve(&self, issuer: &Self::Input) -> Result { + async fn resolve(&self, issuer: &Self::Input) -> Result> { let uri = Builder::from(issuer.parse::()?) .path_and_query("/.well-known/oauth-authorization-server") .build()?; @@ -36,7 +38,7 @@ where let metadata = serde_json::from_slice::(res.body())?; // https://datatracker.ietf.org/doc/html/rfc8414#section-3.3 if &metadata.issuer == issuer { - Ok(metadata) + Ok(Some(metadata)) } else { Err(Error::AuthorizationServerMetadata(format!( "invalid issuer: {}", diff --git a/atrium-oauth/oauth-client/src/resolver/oauth_protected_resource_resolver.rs b/atrium-oauth/oauth-client/src/resolver/oauth_protected_resource_resolver.rs index 98c2ea7a..9ba556b7 100644 --- a/atrium-oauth/oauth-client/src/resolver/oauth_protected_resource_resolver.rs +++ b/atrium-oauth/oauth-client/src/resolver/oauth_protected_resource_resolver.rs @@ -1,5 +1,6 @@ use crate::types::OAuthProtectedResourceMetadata; -use atrium_identity::{Error, Resolver, Result}; +use atrium_common::resolver::Resolver; +use atrium_identity::{Error, Result}; use atrium_xrpc::http::uri::Builder; use atrium_xrpc::http::{Request, StatusCode, Uri}; use atrium_xrpc::HttpClient; @@ -21,8 +22,9 @@ where { type Input = String; type Output = OAuthProtectedResourceMetadata; + type Error = Error; - async fn resolve(&self, resource: &Self::Input) -> Result { + async fn resolve(&self, resource: &Self::Input) -> Result> { let uri = Builder::from(resource.parse::()?) .path_and_query("/.well-known/oauth-protected-resource") .build()?; @@ -36,7 +38,7 @@ where let metadata = serde_json::from_slice::(res.body())?; // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-08#section-3.3 if &metadata.resource == resource { - Ok(metadata) + Ok(Some(metadata)) } else { Err(Error::ProtectedResourceMetadata(format!( "invalid resource: {}",