atrium_identity/
identity_resolver.rs

1use crate::error::{Error, Result};
2use atrium_api::{
3    did_doc::DidDocument,
4    types::string::{AtIdentifier, Did, Handle},
5};
6use atrium_common::resolver::Resolver;
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10pub struct ResolvedIdentity {
11    pub did: String,
12    pub pds: String,
13}
14
15#[derive(Clone, Debug)]
16pub struct IdentityResolverConfig<D, H> {
17    pub did_resolver: D,
18    pub handle_resolver: H,
19}
20
21pub struct IdentityResolver<D, H> {
22    did_resolver: D,
23    handle_resolver: H,
24}
25
26impl<D, H> IdentityResolver<D, H> {
27    pub fn new(config: IdentityResolverConfig<D, H>) -> Self {
28        Self { did_resolver: config.did_resolver, handle_resolver: config.handle_resolver }
29    }
30}
31
32impl<D, H> Resolver for IdentityResolver<D, H>
33where
34    D: Resolver<Input = Did, Output = DidDocument, Error = Error> + Send + Sync,
35    H: Resolver<Input = Handle, Output = Did, Error = Error> + Send + Sync,
36{
37    type Input = str;
38    type Output = ResolvedIdentity;
39    type Error = Error;
40
41    async fn resolve(&self, input: &Self::Input) -> Result<Self::Output> {
42        let document =
43            match input.parse::<AtIdentifier>().map_err(|e| Error::AtIdentifier(e.to_string()))? {
44                AtIdentifier::Did(did) => self.did_resolver.resolve(&did).await?,
45                AtIdentifier::Handle(handle) => {
46                    let did = self.handle_resolver.resolve(&handle).await?;
47                    let document = self.did_resolver.resolve(&did).await?;
48                    if let Some(aka) = &document.also_known_as {
49                        if !aka.contains(&format!("at://{}", handle.as_str())) {
50                            return Err(Error::DidDocument(format!(
51                                "did document for `{}` does not include the handle `{}`",
52                                did.as_str(),
53                                handle.as_str()
54                            )));
55                        }
56                    }
57                    document
58                }
59            };
60        let Some(service) = document.get_pds_endpoint() else {
61            return Err(Error::DidDocument(format!(
62                "no valid `AtprotoPersonalDataServer` service found in `{}`",
63                document.id
64            )));
65        };
66        Ok(ResolvedIdentity { did: document.id, pds: service })
67    }
68}