Skip to main content

identity_kit/methods/
mod.rs

1//! DID methods: `did:key` ([`key`]), `did:jwk` ([`jwk`]) and `did:web` ([`web`]).
2//!
3//! Each method is a type implementing [`DidResolver`] and [`DidProducer`], registered with a
4//! [`Resolver`] through [`ResolverBuilder::with`]; [`default_methods`] registers all three at
5//! once. The methods are behind the `did-key`, `did-jwk` and `did-web` Cargo features, all on
6//! by default; `did:web` needs an [`HttpClient`](web::HttpClient), and the `reqwest` feature
7//! provides [`ReqwestClient`](web::ReqwestClient), a hardened default.
8//!
9//! # Examples
10//!
11//! ```
12//! # #[cfg(all(feature = "did-key", feature = "did-jwk", feature = "reqwest"))]
13//! # tokio::runtime::Builder::new_current_thread().enable_all().build()?.block_on(async {
14//! use identity_kit::did::DidUrl;
15//! use identity_kit::methods::web::ReqwestClient;
16//!
17//! let resolver = identity_kit::methods::default_methods(ReqwestClient::new()?).build();
18//! assert_eq!(resolver.methods(), ["jwk", "key", "web"]);
19//!
20//! // Embedded methods need no network: the document is derived from the DID.
21//! let key = DidUrl::parse(
22//!     "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
23//! )?;
24//! let jwk = resolver.resolve_public_key(&key, None).await?;
25//! assert_eq!(jwk.crv(), Some("Ed25519"));
26//! # Ok::<(), Box<dyn std::error::Error>>(())
27//! # })?;
28//! # Ok::<(), Box<dyn std::error::Error>>(())
29//! ```
30
31#[cfg(feature = "did-jwk")]
32#[cfg_attr(docsrs, doc(cfg(feature = "did-jwk")))]
33pub mod jwk;
34#[cfg(feature = "did-key")]
35#[cfg_attr(docsrs, doc(cfg(feature = "did-key")))]
36pub mod key;
37#[cfg(feature = "did-web")]
38#[cfg_attr(docsrs, doc(cfg(feature = "did-web")))]
39pub mod web;
40
41#[cfg(feature = "did-jwk")]
42pub use jwk::DidJwk;
43#[cfg(feature = "did-key")]
44pub use key::DidKey;
45#[cfg(feature = "did-web")]
46pub use web::DidWeb;
47
48use crate::did::{
49    APPLICATION_DID_JSON, APPLICATION_DID_LD_JSON, ResolutionError, ResolutionOptions,
50};
51#[cfg(any(feature = "did-key", feature = "did-jwk"))]
52use crate::jose::Jwk;
53#[cfg(any(feature = "did-key", feature = "did-jwk"))]
54use crate::resolver::ProduceError;
55#[allow(unused_imports)]
56use crate::resolver::{DidProducer, DidResolver, Resolver, ResolverBuilder};
57
58/// A [`ResolverBuilder`] with every method this crate ships registered, with their defaults:
59/// `did:key` ([`DidKey`]), `did:jwk` ([`DidJwk`]) and `did:web` over `http`.
60///
61/// Register a configured method afterwards to replace a default, and add a cache with
62/// [`ResolverBuilder::cache`] before [`build`](ResolverBuilder::build).
63#[cfg(feature = "did-web")]
64#[cfg_attr(docsrs, doc(cfg(feature = "did-web")))]
65pub fn default_methods(http: impl web::HttpClient + 'static) -> ResolverBuilder {
66    let builder = Resolver::builder();
67    #[cfg(feature = "did-key")]
68    let builder = builder.with(DidKey::default());
69    #[cfg(feature = "did-jwk")]
70    let builder = builder.with(DidJwk);
71    builder.with(DidWeb::new(http))
72}
73
74/// The one key an embedded method takes.
75#[cfg(any(feature = "did-key", feature = "did-jwk"))]
76fn single_key(keys: &[Jwk]) -> Result<&Jwk, ProduceError> {
77    match keys {
78        [] => Err(ProduceError::NoKeys),
79        [key] => Ok(key),
80        _ => Err(ProduceError::TooManyKeys {
81            max: 1,
82            actual: keys.len(),
83        }),
84    }
85}
86
87/// The media types a method of this crate can serve, which are all the JSON ones.
88const JSON_MEDIA_TYPES: [&str; 3] = [
89    APPLICATION_DID_JSON,
90    APPLICATION_DID_LD_JSON,
91    "application/json",
92];
93
94/// Refuses an `accept` option asking for anything but a JSON representation.
95fn check_accept(options: &ResolutionOptions) -> Result<(), ResolutionError> {
96    match options.accept.as_deref() {
97        None => Ok(()),
98        Some(accept) if JSON_MEDIA_TYPES.contains(&accept) => Ok(()),
99        Some(_) => Err(ResolutionError::RepresentationNotSupported),
100    }
101}