Skip to content

Commit

Permalink
Bind latest did:web resolve from #190
Browse files Browse the repository at this point in the history
  • Loading branch information
KendallWeihe committed Jun 17, 2024
1 parent e6622c5 commit fa13331
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 15 deletions.
10 changes: 6 additions & 4 deletions bind/uniffi/src/dids/methods/did_web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ use web5::apid::dids::methods::did_web::DidWeb;

pub struct RcbDidWeb(pub DidWeb);

pub fn rcb_did_web_resolve(uri: &str) -> RcbResult<Arc<RcbResolutionResult>> {
let resolution_result = DidWeb::resolve(uri).map_err(|e| Arc::new(e.into()))?;
pub async fn rcb_did_web_resolve(uri: &str) -> RcbResult<Arc<RcbResolutionResult>> {
let resolution_result = DidWeb::resolve(uri).await.map_err(|e| Arc::new(e.into()))?;
Ok(Arc::new(RcbResolutionResult(resolution_result)))
}

impl RcbDidWeb {
pub fn from_uri(uri: &str) -> RcbResult<Self> {
let did_web = DidWeb::from_uri(uri).map_err(|e| Arc::new(e.into()))?;
pub async fn from_uri(uri: &str) -> RcbResult<Self> {
let did_web = DidWeb::from_uri(uri)
.await
.map_err(|e| Arc::new(e.into()))?;
Ok(Self(did_web))
}

Expand Down
4 changes: 2 additions & 2 deletions bind/uniffi/src/web5.udl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace web5 {

[Throws=RcbError]
RcbResolutionResult rcb_did_jwk_resolve([ByRef] string uri);
[Throws=RcbError]
[Async, Throws=RcbError]
RcbResolutionResult rcb_did_web_resolve([ByRef] string uri);
[Throws=RcbError]
RcbResolutionResult rcb_did_dht_resolve([ByRef] string uri);
Expand Down Expand Up @@ -161,7 +161,7 @@ dictionary RcbDidWebData {
};

interface RcbDidWeb {
[Name=from_uri, Throws=RcbError]
[Async, Name=from_uri, Throws=RcbError]
constructor([ByRef] string uri);
RcbDidWebData get_data();
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
mod resolver;

use super::{MethodError, Result};
use crate::apid::dids::{
did::Did,
document::Document,
resolution_result::{ResolutionMetadataError, ResolutionResult},
resolution_result::{ResolutionMetadata, ResolutionMetadataError, ResolutionResult},
};
use resolver::Resolver;

#[derive(Clone)]
pub struct DidWeb {
Expand All @@ -12,8 +15,8 @@ pub struct DidWeb {
}

impl DidWeb {
pub fn from_uri(uri: &str) -> Result<Self> {
let resolution_result = DidWeb::resolve(uri)?;
pub async fn from_uri(uri: &str) -> Result<Self> {
let resolution_result = DidWeb::resolve(uri).await?;
match resolution_result.document {
None => Err(match resolution_result.resolution_metadata.error {
None => MethodError::ResolutionError(ResolutionMetadataError::InternalError),
Expand All @@ -29,11 +32,16 @@ impl DidWeb {
}
}

pub fn resolve(uri: &str) -> Result<ResolutionResult> {
// 🚧 use existing PR which replaces spruce dep
println!("DidWeb::resolve() called with {}", uri);
Ok(ResolutionResult {
..Default::default()
pub async fn resolve(uri: &str) -> Result<ResolutionResult> {
let did = Did::new(uri)?;
let resolution_result = Resolver::new(did).await;

Ok(match resolution_result {
Err(e) => ResolutionResult {
resolution_metadata: ResolutionMetadata { error: Some(e) },
..Default::default()
},
Ok(r) => r,
})
}
}
108 changes: 108 additions & 0 deletions crates/web5/src/apid/dids/methods/did_web/resolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use crate::apid::dids::{
did::Did,
document::Document,
resolution_result::{ResolutionMetadataError, ResolutionResult},
};
use reqwest::header::HeaderMap;
use std::{
future::{Future, IntoFuture},
pin::Pin,
};

// PORT_SEP is the : character that separates the domain from the port in a URI.
const PORT_SEP: &str = "%3A";

/// Resolver is the implementation of the did:web method for resolving DID URIs. It is responsible
/// for fetching the DID Document from the web according for the did-web spec.
pub struct Resolver {
did_url: String,
}

impl Resolver {
pub fn new(did: Did) -> Self {
// note: delimited is : generally, but ; is allowed by the spec. The did-web spec (§3.2) says
// ; should be avoided because of it's potential use for matrix URIs.
let did_url = match did.id.split_once(':') {
Some((domain, path)) => format!(
"{}/{}",
domain.replace(PORT_SEP, ":"),
path.split(':').collect::<Vec<&str>>().join("/"),
),
None => format!("{}/{}", did.id.replace(PORT_SEP, ":"), ".well-known",),
};

Self {
did_url: format!("https://{}/did.json", did_url),
}
}

async fn resolve(url: String) -> Result<ResolutionResult, ResolutionMetadataError> {
let headers = HeaderMap::new();

let client = reqwest::Client::builder()
.default_headers(headers)
.build()
.map_err(|_| ResolutionMetadataError::InternalError)?;

let response = client
.get(&url)
.send()
.await
.map_err(|_| ResolutionMetadataError::InternalError)?;

if response.status().is_success() {
let did_document = response
.json::<Document>()
.await
.map_err(|_| ResolutionMetadataError::RepresentationNotSupported)?;

Ok(ResolutionResult {
document: Some(did_document),
..Default::default()
})
} else {
Err(ResolutionMetadataError::NotFound)
}
}
}

// This trait implements the actual logic for resolving a DID URI to a DID Document.
impl IntoFuture for Resolver {
type Output = Result<ResolutionResult, ResolutionMetadataError>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;

fn into_future(self) -> Self::IntoFuture {
let did_url = self.did_url;
Box::pin(async move { Self::resolve(did_url).await })
}
}

#[cfg(test)]
mod tests {
use super::*;

#[tokio::test]
async fn resolution_success() {
let did_uri = "did:web:tbd.website";
let result = Resolver::new(Did::new(did_uri).unwrap());
assert_eq!(result.did_url, "https://tbd.website/.well-known/did.json");

let did_uri = "did:web:tbd.website:with:path";
let result = Resolver::new(Did::new(did_uri).unwrap());
assert_eq!(result.did_url, "https://tbd.website/with/path/did.json");

let did_uri = "did:web:tbd.website%3A8080";
let result = Resolver::new(Did::new(did_uri).unwrap());
assert_eq!(
result.did_url,
"https://tbd.website:8080/.well-known/did.json"
);

let did_uri = "did:web:tbd.website%3A8080:with:path";
let result = Resolver::new(Did::new(did_uri).unwrap());
assert_eq!(
result.did_url,
"https://tbd.website:8080/with/path/did.json"
);
}
}
2 changes: 1 addition & 1 deletion crates/web5/src/apid/dids/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use base64::DecodeError;
use serde_json::Error as SerdeJsonError;

pub mod did_dht;
pub mod did_web;

pub mod did_jwk;
pub mod did_web;

#[derive(thiserror::Error, Debug)]
pub enum MethodError {
Expand Down

0 comments on commit fa13331

Please sign in to comment.