Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Did web http client #523

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions did-test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ async fn report_method_web() {
let supported_content_types = vec![TYPE_DID_LD_JSON.to_string()];

let did = "did:web:demo.spruceid.com:2021:07:08";
let did_vector = did_method_vector(&did_web::DIDWeb, did).await;
let did_web_resolver = did_web::DIDWeb::new_with_default_http_client().unwrap();
let did_vector = did_method_vector(&did_web_resolver, did).await;
did_vectors.insert(did.to_string(), did_vector);

let dids = did_vectors.keys().cloned().collect();
Expand Down Expand Up @@ -537,19 +538,21 @@ async fn report_resolver_web() {
executions: Vec::new(),
};

let did_web_resolver = did_web::DIDWeb::new_with_default_http_client().unwrap();

for did in &[
"did:web:identity.foundation",
"did:web:did.actor:nonexistent",
] {
report
.resolve(&did_web::DIDWeb, did, &ResolutionInputMetadata::default())
.resolve(&did_web_resolver, did, &ResolutionInputMetadata::default())
.await;
}

{
let did = &"did:web:identity.foundation";
report
.resolve_representation(&did_web::DIDWeb, did, &ResolutionInputMetadata::default())
.resolve_representation(&did_web_resolver, did, &ResolutionInputMetadata::default())
.await;
}

Expand Down Expand Up @@ -709,14 +712,16 @@ async fn report_dereferencer_web() {
executions: Vec::new(),
};

let did_web_resolver = did_web::DIDWeb::new_with_default_http_client().unwrap();

for did_url in &[
"did:web:did.actor:nonexistent",
"did:web:demo.spruceid.com:2021:07:14:service-example",
"did:web:demo.spruceid.com:2021:07:14:service-example?service=hello",
] {
report
.dereference(
&did_web::DIDWeb,
&did_web_resolver,
did_url,
&DereferencingInputMetadata::default(),
)
Expand Down
74 changes: 51 additions & 23 deletions did-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,42 @@ thread_local! {
/// did:web Method
///
/// [Specification](https://w3c-ccg.github.io/did-method-web/)
pub struct DIDWeb;
///
/// DIDWeb struct has an HTTP client to use for DID resolution. It's incredibly slow to create a new
/// reqwest::Client, due to the overhead of loading the system's root certificates. This HTTP client must
/// be specified by constructing the DIDWeb instance using new_with_default_http_client (to use defaults)
/// or DIDWeb::new_with_http_client if there is an specific reqwest::Client that should be reused.
/// Note that this is the recommended approach to using reqwest::Client (see
/// https://docs.rs/reqwest/latest/reqwest/struct.Client.html).
pub struct DIDWeb {
http_client: reqwest::Client,
}

impl DIDWeb {
/// Create an instance of the DIDWeb resolver with a default HTTP client. See also `DIDWeb::new_with_http_client`.
pub fn new_with_default_http_client() -> Result<Self, String> {
let mut headers = reqwest::header::HeaderMap::new();

headers.insert(
"User-Agent",
reqwest::header::HeaderValue::from_static(USER_AGENT),
);

let http_client = match reqwest::Client::builder().default_headers(headers).build() {
Ok(http_client) => http_client,
Err(err) => {
return Err(format!("Error building HTTP client: {}", err));
}
};

Ok(Self { http_client })
}
/// Create an instance of the DIDWeb resolver with a specific HTTP client. See also
/// `DIDWeb::new_with_default_http_client`.
pub fn new_with_http_client(http_client: reqwest::Client) -> Self {
Self { http_client }
}
}

fn did_web_url(did: &str) -> Result<String, ResolutionMetadata> {
let mut parts = did.split(':').peekable();
Expand Down Expand Up @@ -107,28 +142,17 @@ impl DIDResolver for DIDWeb {
};
// TODO: https://w3c-ccg.github.io/did-method-web/#in-transit-security

let mut headers = reqwest::header::HeaderMap::new();

headers.insert(
"User-Agent",
reqwest::header::HeaderValue::from_static(USER_AGENT),
);

let client = match reqwest::Client::builder().default_headers(headers).build() {
Ok(c) => c,
Err(err) => {
return (
ResolutionMetadata::from_error(&format!("Error building HTTP client: {}", err)),
Vec::new(),
None,
)
}
};
let accept = input_metadata
.accept
.clone()
.unwrap_or_else(|| "application/json".to_string());
let resp = match client.get(&url).header("Accept", accept).send().await {
let resp = match self
.http_client
.get(&url)
.header("Accept", accept)
.send()
.await
{
Ok(req) => req,
Err(err) => {
return (
Expand Down Expand Up @@ -282,7 +306,8 @@ mod tests {
PROXY.with(|proxy| {
proxy.replace(Some(url));
});
let (res_meta, doc_opt, _doc_meta) = DIDWeb
let did_web_resolver = DIDWeb::new_with_default_http_client().unwrap();
let (res_meta, doc_opt, _doc_meta) = did_web_resolver
.resolve("did:web:localhost", &ResolutionInputMetadata::default())
.await;
assert_eq!(res_meta.error, None);
Expand Down Expand Up @@ -320,21 +345,24 @@ mod tests {
..Default::default()
};
let mut context_loader = ssi_json_ld::ContextLoader::default();
let did_web_resolver = DIDWeb::new_with_default_http_client().unwrap();
let proof = vc
.generate_proof(&key, &issue_options, &DIDWeb, &mut context_loader)
.generate_proof(&key, &issue_options, &did_web_resolver, &mut context_loader)
.await
.unwrap();
println!("{}", serde_json::to_string_pretty(&proof).unwrap());
vc.add_proof(proof);
vc.validate().unwrap();
let verification_result = vc.verify(None, &DIDWeb, &mut context_loader).await;
let verification_result = vc
.verify(None, &did_web_resolver, &mut context_loader)
.await;
println!("{:#?}", verification_result);
assert!(verification_result.errors.is_empty());

// test that issuer property is used for verification
vc.issuer = Some(Issuer::URI(URI::String("did:example:bad".to_string())));
assert!(!vc
.verify(None, &DIDWeb, &mut context_loader)
.verify(None, &did_web_resolver, &mut context_loader)
.await
.errors
.is_empty());
Expand Down
1 change: 1 addition & 0 deletions ssi-jws/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ pub fn verify_bytes_warnable(
{
use ed25519_dalek::Verifier;
let public_key = ed25519_dalek::PublicKey::try_from(okp)?;
use k256::ecdsa::signature::Signature;
let signature = ed25519_dalek::Signature::from_bytes(signature)
.map_err(ssi_jwk::Error::from)?;
public_key
Expand Down