Skip to main content

atrium_identity/handle/
well_known_resolver.rs

1use super::HandleResolver;
2use crate::error::{Error, Result};
3use atrium_api::types::string::{Did, Handle};
4use atrium_common::resolver::Resolver;
5use atrium_xrpc::HttpClient;
6use atrium_xrpc::http::Request;
7use std::sync::Arc;
8
9const WELL_KNWON_PATH: &str = "/.well-known/atproto-did";
10
11#[derive(Clone, Debug)]
12pub struct WellKnownHandleResolverConfig<T> {
13    pub http_client: Arc<T>,
14}
15
16pub struct WellKnownHandleResolver<T> {
17    http_client: Arc<T>,
18}
19
20impl<T> WellKnownHandleResolver<T> {
21    pub fn new(config: WellKnownHandleResolverConfig<T>) -> Self {
22        Self { http_client: config.http_client }
23    }
24}
25
26impl<T> Resolver for WellKnownHandleResolver<T>
27where
28    T: HttpClient + Send + Sync + 'static,
29{
30    type Input = Handle;
31    type Output = Did;
32    type Error = Error;
33
34    async fn resolve(&self, handle: &Self::Input) -> Result<Self::Output> {
35        let url = format!("https://{}{WELL_KNWON_PATH}", handle.as_str());
36        // TODO: no-cache?
37        let res = self
38            .http_client
39            .send_http(Request::builder().uri(url).body(Vec::new())?)
40            .await
41            .map_err(Error::HttpClient)?;
42        if res.status().is_success() {
43            String::from_utf8_lossy(res.body())
44                .trim() // strip whitespace for best compatibility. https://atproto.com/specs/handle#https-well-known-method
45                .parse::<Did>()
46                .map_err(|e| Error::Did(e.to_string()))
47        } else {
48            Err(Error::HttpStatus(res.status()))
49        }
50    }
51}
52
53impl<T> HandleResolver for WellKnownHandleResolver<T> where T: HttpClient + Send + Sync + 'static {}