webfinger-rs 0.0.33

WebFinger request and response types for Rust, with first-party Reqwest, Axum, and Actix Web integrations.
Documentation

Webfinger-rs

Crates.io badge License badge Docs.rs badge Deps.rs badge

webfinger-rs is a Rust library for building and serving WebFinger requests and responses with RFC 7033-shaped types and first-party integrations for Reqwest, Axum, and Actix Web.

The crate keeps request parsing, JRD response construction, and framework adapters in one place so clients, servers, and tests use the same WebFinger types.

The docs.rs page is the full API reference and usage guide.

Why webfinger-rs

  • Model WebFinger requests and JRD responses with reusable library types.
  • Execute client requests with Reqwest.
  • Expose WebFinger endpoints in Axum or Actix Web with the same request and response types.
  • Stay close to RFC 7033 without pulling in a larger identity stack.

Supported integrations

Feature What it enables
none Core request and response types, builders, and URL conversion
reqwest Client execution helpers and Reqwest request/response conversions
axum Axum extractor and responder integration
actix Actix Web extractor and responder integration

Current integration targets:

  • Reqwest 0.13
  • Axum 0.8
  • Actix Web 4

Repository tools

This repository also contains a configurable WebFinger responder split across three crates:

  • webfinger-service is the runtime-neutral core. It parses TOML configuration, owns provider traits, and resolves exact WebFinger resources with rel filtering.
  • webfinger-service-axum is the native Axum server for local development and non-Worker deployments.
  • webfinger-service-worker is the Cloudflare Worker adapter. It reads TOML configuration from Workers KV and serves /.well-known/webfinger.

This repository also contains a Rust viewer for inspecting WebFinger discovery behavior. It is a debugging tool, not the server-side WebFinger responder: the page accepts an acct: resource or full WebFinger URL, asks the Rust server to fetch the target /.well-known/webfinger endpoint server-side, and renders the target HTTP status, content type, redirect Location, parsed JRD fields, raw JSON, and a copyable curl command.

The viewer is split into three crates:

  • webfinger-viewer owns shared route policy, lookup request/result types, htmx behavior, Askama templates, assets, and runtime-neutral config.
  • webfinger-viewer-axum owns the native Axum server, reqwest-backed target fetches, Tokio and rustls setup, Tower HTTP tracing, and the local CLI.
  • webfinger-viewer-worker owns the Cloudflare Worker entrypoint, Worker request/response conversion, Worker fetch(), and wasm console tracing.

The viewer is same-origin by default for public deployments, so a page mounted at https://example.com/webfinger inspects https://example.com/.well-known/webfinger. Native or Wrangler sessions can use full loopback WebFinger URLs to inspect another local server. See webfinger-viewer/README.md for shared viewer behavior, webfinger-viewer-axum/README.md for native local development, and webfinger-viewer-worker/README.md for Cloudflare deployment.

Primary types

  • WebFingerRequest models the WebFinger query target, host, and optional relation filters. Build one directly for client requests, or extract one from an Axum or Actix handler.
  • WebFingerResponse models the JSON Resource Descriptor returned by a WebFinger endpoint. Return one from server handlers or parse one from a Reqwest response.
  • Link and Rel model JRD link objects and relation filters so servers can apply the same relation-filtering rules that clients request.
  • Resource and JrdUri validate URI-valued protocol fields before they enter requests or JRD responses.

Protocol overview

A WebFinger query is an HTTPS GET against /.well-known/webfinger with a required resource parameter and, optionally, one or more rel parameters. The resource parameter is the query target URI; builders and server extractors reject relative references such as carol, /relative, ../x, and empty values.

A request built by this crate today for acct:carol@example.com filtered to the profile-page relation looks like this:

GET https://example.com/.well-known/webfinger?resource=acct%3Acarol%40example.com&rel=http%3A%2F%2Fwebfinger.net%2Frel%2Fprofile-page

Server integrations leave routing and TLS at the framework boundary, then use WebFinger extractors for protocol parsing:

  • mount the handler as GET at /.well-known/webfinger so the router rejects other paths and methods;
  • configure TLS and forwarded-proto handling at the server or reverse-proxy boundary; and
  • let the Axum or Actix Web extractor validate the request host, query parameters, percent encoding, and resource URI.

Install

Add the crate with the feature set you need:

cargo add webfinger-rs
cargo add webfinger-rs --features reqwest
cargo add webfinger-rs --features axum
cargo add webfinger-rs --features actix

The companion CLI is useful for trying servers by hand:

cargo install webfinger-cli
webfinger acct:carol@example.com --rel http://webfinger.net/rel/avatar

Axum server quick example

Enable axum to extract WebFingerRequest and return WebFingerResponse from a handler mounted at /.well-known/webfinger:

cargo add webfinger-rs --features axum
cargo add axum
use axum::{http::StatusCode, routing::get, Router};
use webfinger_rs::{Link, Rel, WELL_KNOWN_PATH, WebFingerRequest, WebFingerResponse};

async fn webfinger(request: WebFingerRequest) -> axum::response::Result<WebFingerResponse> {
    let subject = request.resource.to_string();
    if subject != "acct:carol@example.com" {
        return Err((StatusCode::NOT_FOUND, "not found").into());
    }

    let rel = Rel::new("http://webfinger.net/rel/profile-page");
    let response = if request.rels.is_empty() || request.rels.contains(&rel) {
        let link = Link::builder(rel).href("https://example.com/users/carol");
        WebFingerResponse::builder(subject).link(link).build()
    } else {
        WebFingerResponse::builder(subject).build()
    };
    Ok(response)
}

Router::new().route(WELL_KNOWN_PATH, get(webfinger))

Reqwest client quick example

Enable reqwest to execute a request directly from WebFingerRequest:

use webfinger_rs::WebFingerRequest;

async fn example() -> Result<(), Box<dyn std::error::Error>> {
    let request = WebFingerRequest::builder("acct:carol@example.com")?
        .host("example.com")
        .rel("http://webfinger.net/rel/profile-page")
        .build();

    let response = request.execute_reqwest().await?;
    println!("{response}");
    Ok(())
}

Learn more

  • API docs and deeper usage guide: docs.rs/webfinger-rs
  • Runnable example servers: cargo run -p webfinger-rs --example axum --features axum cargo run -p webfinger-rs --example actix --features actix
  • Deployable WebFinger service Worker: webfinger-service-worker
  • Configurable native WebFinger service: webfinger-service-axum
  • Runtime-neutral WebFinger service core: webfinger-service
  • Runnable example client: cargo run -p webfinger-rs --example client --features reqwest
  • CLI crate: webfinger-cli

Run one server example first, then run the client example in another shell. The client example queries https://localhost:3000, accepts the self-signed certificate generated by either server example, and prints the shared WebFingerResponse as JSON.

The server examples also work with the CLI. Query without --rel to get both links, or pass a relation filter to narrow the returned links array:

webfinger acct:carol@localhost localhost:3000 --insecure
webfinger acct:carol@localhost localhost:3000 --insecure \
  --rel http://webfinger.net/rel/profile-page
webfinger acct:carol@localhost localhost:3000 --insecure \
  --rel http://webfinger.net/rel/avatar

License

Copyright (c) Josh McKinney

This project is licensed under either of:

MSRV

This library is tested on the latest stable release of Rust. The minimum supported version is the previous stable release. The library may work on older versions, but that is not guaranteed.

Contributing

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

See CONTRIBUTING.md.