Skip to main content

auths_infra_http/
identity_resolver.rs

1use auths_core::ports::network::{IdentityResolver, ResolutionError, ResolvedIdentity};
2use auths_verifier::core::Ed25519PublicKey;
3use serde::Deserialize;
4use std::future::Future;
5
6use crate::default_http_client;
7use crate::request::{build_get_request, execute_request, parse_response_json};
8
9#[derive(Debug, Deserialize)]
10struct ResolvedIdentityResponse {
11    did: String,
12    public_key: Vec<u8>,
13    method: String,
14    #[serde(default)]
15    sequence: u64,
16    #[serde(default)]
17    can_rotate: bool,
18}
19
20/// HTTP-backed implementation of `IdentityResolver`.
21///
22/// Calls a remote registry endpoint to resolve DIDs to their current
23/// cryptographic material.
24///
25/// Usage:
26/// ```ignore
27/// use auths_infra_http::HttpIdentityResolver;
28///
29/// let resolver = HttpIdentityResolver::new("https://registry.example.com");
30/// let identity = resolver.resolve_identity("did:keri:EAbcdef...").await?;
31/// ```
32pub struct HttpIdentityResolver {
33    base_url: String,
34    client: reqwest::Client,
35}
36
37impl HttpIdentityResolver {
38    pub fn new(base_url: impl Into<String>) -> Self {
39        Self {
40            base_url: base_url.into().trim_end_matches('/').to_string(),
41            client: default_http_client(),
42        }
43    }
44}
45
46impl IdentityResolver for HttpIdentityResolver {
47    fn resolve_identity(
48        &self,
49        did: &str,
50    ) -> impl Future<Output = Result<ResolvedIdentity, ResolutionError>> + Send {
51        let url = format!("{}/resolve/{}", self.base_url, did);
52        let request = build_get_request(&self.client, &url);
53        let did_owned = did.to_string();
54
55        async move {
56            let response = execute_request(request, &url)
57                .await
58                .map_err(ResolutionError::Network)?;
59
60            let status = response.status().as_u16();
61            if status == 404 {
62                return Err(ResolutionError::DidNotFound { did: did_owned });
63            }
64
65            let parsed: ResolvedIdentityResponse = parse_response_json(response, &did_owned)
66                .await
67                .map_err(ResolutionError::Network)?;
68
69            let public_key = Ed25519PublicKey::try_from_slice(&parsed.public_key).map_err(|e| {
70                ResolutionError::InvalidDid {
71                    did: parsed.did.clone(),
72                    reason: format!("invalid public key: {e}"),
73                }
74            })?;
75
76            match parsed.method.as_str() {
77                "key" => Ok(ResolvedIdentity::Key {
78                    did: parsed.did,
79                    public_key,
80                }),
81                "keri" => Ok(ResolvedIdentity::Keri {
82                    did: parsed.did,
83                    public_key,
84                    sequence: parsed.sequence,
85                    can_rotate: parsed.can_rotate,
86                }),
87                other => Err(ResolutionError::InvalidDid {
88                    did: did_owned,
89                    reason: format!("unsupported method: {other}"),
90                }),
91            }
92        }
93    }
94}