auths_sdk/
registration.rs1use std::sync::Arc;
2
3use serde::{Deserialize, Serialize};
4
5use auths_core::ports::network::{NetworkError, RegistryClient};
6use auths_id::keri::Prefix;
7use auths_id::ports::registry::RegistryBackend;
8use auths_id::storage::attestation::AttestationSource;
9use auths_id::storage::identity::IdentityStorage;
10
11use crate::error::RegistrationError;
12use crate::result::RegistrationOutcome;
13
14pub const DEFAULT_REGISTRY_URL: &str = "https://auths-registry.fly.dev";
16
17#[derive(Serialize)]
18struct RegistryOnboardingPayload {
19 inception_event: serde_json::Value,
20 attestations: Vec<serde_json::Value>,
21 proof_url: Option<String>,
22}
23
24#[derive(Deserialize)]
25struct RegistrationResponse {
26 did_prefix: String,
27 platform_claims_indexed: usize,
28}
29
30pub async fn register_identity(
48 identity_storage: Arc<dyn IdentityStorage + Send + Sync>,
49 registry: Arc<dyn RegistryBackend + Send + Sync>,
50 attestation_source: Arc<dyn AttestationSource + Send + Sync>,
51 registry_url: &str,
52 proof_url: Option<String>,
53 registry_client: &impl RegistryClient,
54) -> Result<RegistrationOutcome, RegistrationError> {
55 let identity = identity_storage
56 .load_identity()
57 .map_err(|e| RegistrationError::LocalDataError(e.to_string()))?;
58
59 let did_prefix = identity
60 .controller_did
61 .as_str()
62 .strip_prefix("did:keri:")
63 .ok_or_else(|| {
64 RegistrationError::LocalDataError(format!(
65 "Invalid DID format, expected 'did:keri:': {}",
66 identity.controller_did
67 ))
68 })?;
69
70 let prefix = Prefix::new_unchecked(did_prefix.to_string());
71 let inception = registry.get_event(&prefix, 0).map_err(|_| {
72 RegistrationError::LocalDataError(
73 "No KEL events found for identity. The identity may be corrupted.".to_string(),
74 )
75 })?;
76 let inception_event = serde_json::to_value(&inception)
77 .map_err(|e| RegistrationError::LocalDataError(e.to_string()))?;
78
79 let attestations = attestation_source
80 .load_all_attestations()
81 .unwrap_or_default();
82 let attestation_values: Vec<serde_json::Value> = attestations
83 .iter()
84 .filter_map(|a| serde_json::to_value(a).ok())
85 .collect();
86
87 let payload = RegistryOnboardingPayload {
88 inception_event,
89 attestations: attestation_values,
90 proof_url,
91 };
92
93 let json_body = serde_json::to_vec(&payload)
94 .map_err(|e| RegistrationError::LocalDataError(e.to_string()))?;
95
96 let registry_url = registry_url.trim_end_matches('/');
97 let response = registry_client
98 .post_json(registry_url, "v1/identities", &json_body)
99 .await
100 .map_err(RegistrationError::NetworkError)?;
101
102 match response.status {
103 201 => {
104 let body: RegistrationResponse =
105 serde_json::from_slice(&response.body).map_err(|e| {
106 RegistrationError::NetworkError(NetworkError::InvalidResponse {
107 detail: e.to_string(),
108 })
109 })?;
110
111 Ok(RegistrationOutcome {
112 did_prefix: body.did_prefix,
113 registry: registry_url.to_string(),
114 platform_claims_indexed: body.platform_claims_indexed,
115 })
116 }
117 409 => Err(RegistrationError::AlreadyRegistered),
118 429 => Err(RegistrationError::QuotaExceeded),
119 _ => {
120 let body = String::from_utf8_lossy(&response.body);
121 Err(RegistrationError::NetworkError(
122 NetworkError::InvalidResponse {
123 detail: format!("Registry error ({}): {}", response.status, body),
124 },
125 ))
126 }
127 }
128}