Skip to main content

Crate webfinger_rs

Crate webfinger_rs 

Source
Expand description

webfinger-rs is a transport-agnostic WebFinger implementation for Rust, centered on the request and response types defined by RFC 7033 with first-party integrations for Reqwest, Axum, and Actix Web.

WebFinger is used to discover information about people or other entities on the internet using URI-based identifiers such as acct:carol@example.com. In practice, it is commonly used for OpenID Connect Discovery, account discovery in federated systems like Mastodon and ActivityPub, and for publishing identity-related metadata from your own site or service.

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

§Why use webfinger-rs?

  • Reusable request and response types shaped around RFC 7033.
  • Optional Reqwest client execution via WebFingerRequest::execute_reqwest.
  • Optional Axum and Actix Web extractor/responder integrations.
  • A permissive dual license (MIT OR Apache-2.0) that fits typical library and application usage.

§Install

Start with the core crate, then enable the integration feature 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 related CLI tool, webfinger-cli, is useful for trying servers by hand:

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

§Feature matrix

FeatureWhat it enables
noneCore request/response types, builders, and URL conversion
reqwestClient execution helpers and Reqwest request/response conversions
axumWebFingerRequest extraction and WebFingerResponse responses in Axum via crate::axum
actixWebFingerRequest extraction and WebFingerResponse responses in Actix Web via crate::actix

§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 the well-known endpoint WELL_KNOWN_PATH 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

See: RFC 7033 section 4.1 for the query-construction rules and percent-encoding details.

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_PATH so the router rejects other paths and methods;
  • configure TLS and forwarded-proto handling at the server or reverse-proxy boundary; and
  • let the crate::axum or crate::actix extractor validate the request host, query parameters, percent encoding, and resource URI.

A successful JRD response might look like this:

{
  "subject": "acct:carol@example.com",
  "links": [
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "href": "https://example.com/users/carol"
    }
  ]
}

See: RFC 7033 section 4.4 for the JRD structure.

§Client quickstart

Enable the reqwest feature to execute WebFinger requests directly from the request type. The current API expects an explicit host, which should normally match the resource host when the resource URI has one.

use webfinger_rs::WebFingerRequest;

const PROFILE_PAGE_REL: &str = "http://webfinger.net/rel/profile-page";
const AVATAR_REL: &str = "http://webfinger.net/rel/avatar";

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

    let response = request.execute_reqwest().await?;
    println!("Subject: {}", response.subject);
    for rel in [PROFILE_PAGE_REL, AVATAR_REL] {
        if let Some(href) = response
            .links
            .iter()
            .find(|link| link.rel.as_ref() == rel)
            .and_then(|link| link.href.as_ref().map(|href| href.as_ref()))
        {
            println!("{rel}: {href}");
        }
    }
    println!("{response}");
    Ok(())
}

§Axum quickstart

Enable the axum feature to extract WebFingerRequest from the incoming request and return WebFingerResponse directly from your handler. Mount the handler at WELL_KNOWN_PATH. See also crate::axum and the Axum example.

use axum::{http::StatusCode, routing::get, Router};
use webfinger_rs::{Link, Rel, WELL_KNOWN_PATH, WebFingerRequest, WebFingerResponse};

const SUBJECT: &str = "acct:carol@example.com";
const PROFILE_PAGE_REL: &str = "http://webfinger.net/rel/profile-page";
const AVATAR_REL: &str = "http://webfinger.net/rel/avatar";
const PROFILE_URL: &str = "https://example.com/users/carol";
const AVATAR_URL: &str = "https://example.com/media/carol.png";
const ROLE_PROPERTY: &str = "https://example.com/ns/account-role";

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

    let mut links = Vec::new();

    let profile_rel = Rel::new(PROFILE_PAGE_REL);
    if request.rels.is_empty() || request.rels.contains(&profile_rel) {
        links.push(
            Link::builder(profile_rel)
                .href(PROFILE_URL)
                .title("en", "Carol's profile")
                .build(),
        );
    }

    let avatar_rel = Rel::new(AVATAR_REL);
    if request.rels.is_empty() || request.rels.contains(&avatar_rel) {
        links.push(
            Link::builder(avatar_rel)
                .href(AVATAR_URL)
                .r#type("image/png")
                .build(),
        );
    }

    let response = WebFingerResponse::builder(subject)
        .alias(PROFILE_URL)
        .property(ROLE_PROPERTY, "maintainer")
        .links(links)
        .build();
    Ok(response)
}

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

§Actix quickstart

Enable the actix feature to use the same request and response types in Actix Web handlers. As with the Axum integration, the route path should be WELL_KNOWN_PATH. See also crate::actix and the Actix example.

use actix_web::{App, web};
use webfinger_rs::{Link, Rel, WELL_KNOWN_PATH, WebFingerRequest, WebFingerResponse};

const SUBJECT: &str = "acct:carol@example.com";
const PROFILE_PAGE_REL: &str = "http://webfinger.net/rel/profile-page";
const AVATAR_REL: &str = "http://webfinger.net/rel/avatar";
const PROFILE_URL: &str = "https://example.com/users/carol";
const AVATAR_URL: &str = "https://example.com/media/carol.png";
const ROLE_PROPERTY: &str = "https://example.com/ns/account-role";

async fn webfinger(request: WebFingerRequest) -> actix_web::Result<WebFingerResponse> {
    let subject = request.resource.to_string();
    if subject != SUBJECT {
        return Err(actix_web::error::ErrorNotFound("not found"));
    }

    let mut links = Vec::new();

    let profile_rel = Rel::new(PROFILE_PAGE_REL);
    if request.rels.is_empty() || request.rels.contains(&profile_rel) {
        links.push(
            Link::builder(profile_rel)
                .href(PROFILE_URL)
                .title("en", "Carol's profile")
                .build(),
        );
    }

    let avatar_rel = Rel::new(AVATAR_REL);
    if request.rels.is_empty() || request.rels.contains(&avatar_rel) {
        links.push(
            Link::builder(avatar_rel)
                .href(AVATAR_URL)
                .r#type("image/png")
                .build(),
        );
    }

    let response = WebFingerResponse::builder(subject)
        .alias(PROFILE_URL)
        .property(ROLE_PROPERTY, "maintainer")
        .links(links)
        .build();
    Ok(response)
}

App::new().route(WELL_KNOWN_PATH, web::get().to(webfinger))

§Compatibility

The current first-party integration targets are:

  • Reqwest 0.13
  • Axum 0.8
  • Actix Web 4

The crate is currently pre-0.1, so API and compatibility adjustments may still land in minor releases while the integration surface settles. These version notes describe the currently integrated crates, not a full protocol-compliance matrix.

§Limitations

  • Client execution is currently implemented only for Reqwest.
  • Server integrations are currently implemented only for Axum and Actix Web.
  • The crate focuses on RFC 7033 request/response handling and framework integration, not a full identity stack around WebFinger.
  • The crate docs aim to stay grounded in RFC 7033, but they document the current implementation rather than exhaustively enumerating every compliance detail.

See: RFC 7033 section 10.1 for the well-known path registration.

§Examples

Runnable examples are available in the repository:

  • cargo run -p webfinger-rs --example axum --features axum
  • cargo run -p webfinger-rs --example actix --features actix
  • cargo run -p webfinger-rs --example client --features reqwest

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 profile-page and avatar links returned by the shared WebFingerResponse type.

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:

Modules§

actixactix
Actix Web integration for WebFinger request extraction and JRD responses.
axumaxum
Axum integration for WebFinger request extraction and JRD responses.

Structs§

JrdUri
A URI string used in a WebFinger JRD response.
Link
A link in the WebFinger response.
LinkBuilder
A builder for a WebFinger link.
Rel
Link relation type.
RequestBuilder
A builder for a WebFinger request.
Resource
A WebFinger resource URI.
ResponseBuilder
A builder for a WebFinger response.
Title
A title in the WebFinger response.
WebFingerRequest
A WebFinger request.
WebFingerResponse
A WebFinger response.

Enums§

Error
Error type for this crate.
ResourceError
Errors that can occur while parsing a WebFinger resource URI.

Constants§

WELL_KNOWN_PATH
The well-known path for WebFinger requests (/.well-known/webfinger).