1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! `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.
//!
//! This crate exists to provide one set of WebFinger types that can be reused across clients,
//! servers, and tests instead of reimplementing the protocol details for each framework. It is
//! intended to be practical as both a library dependency and an integration layer for modern Rust
//! web stacks.
//!
//! It also fills a gap left by older WebFinger crates that are no longer actively maintained or
//! are licensed in a way that is less convenient for reuse as a general-purpose Rust library.
//!
//! # 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.
//!
//! [RFC 7033]: https://www.rfc-editor.org/rfc/rfc7033.html
//! [WebFinger]: https://en.wikipedia.org/wiki/WebFinger
//! [Reqwest]: https://crates.io/crates/reqwest
//! [Axum]: https://crates.io/crates/axum
//! [Actix Web]: https://crates.io/crates/actix-web
//! [OpenID Connect Discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html
//! [Mastodon]: https://docs.joinmastodon.org/spec/webfinger/
//! [ActivityPub]: https://www.w3.org/TR/activitypub/
//! [RFC 7033 section 4.1]: https://www.rfc-editor.org/rfc/rfc7033.html#section-4.1
//! [RFC 7033 section 4.4]: https://www.rfc-editor.org/rfc/rfc7033.html#section-4.4
//! [RFC 7033 section 10.1]: https://www.rfc-editor.org/rfc/rfc7033.html#section-10.1
//!
//! # Install
//!
//! Start with the core crate, then enable the integration feature you need:
//!
//! ```shell
//! 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:
//!
//! ```shell
//! cargo install webfinger-cli
//! webfinger acct:carol@example.com --rel http://webfinger.net/rel/avatar
//! ```
//!
//! [`webfinger-cli`]: https://crates.io/crates/webfinger-cli
//!
//! # Feature matrix
//!
//! | Feature | What it enables |
//! | --- | --- |
//! | none | Core request/response types, builders, and URL conversion support |
//! | `reqwest` | Client execution helpers and Reqwest request/response conversions |
//! | `axum` | [`WebFingerRequest`] extraction and [`WebFingerResponse`] responses in Axum via [`crate::axum`] |
//! | `actix` | [`WebFingerRequest`] extraction and [`WebFingerResponse`] responses in Actix Web via [`crate::actix`] |
//!
//! # 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.
//!
//! A request built by this crate today for `acct:carol@example.com` filtered to the profile-page
//! relation looks like this:
//!
//! ```text
//! GET https://example.com/.well-known/webfinger?resource=acct:carol@example.com&rel=http://webfinger.net/rel/profile-page
//! ```
//!
//! See: [RFC 7033 section 4.1] for the query-construction rules and percent-encoding details.
//!
//! A successful JRD response might look like this:
//!
//! ```json
//! {
//! "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.
//!
//! ```rust,no_run
//! # #[cfg(feature = "reqwest")] {
//! 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(())
//! }
//! # }
//! ```
//!
//! # 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].
//!
//! ```rust
//! # #[cfg(feature = "axum")]
//! # fn app() -> axum::Router {
//! 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))
//! # }
//! ```
//!
//! [Axum example]:
//! https://github.com/joshka/webfinger-rs/blob/main/webfinger-rs/examples/axum.rs
//!
//! # 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].
//!
//! ```rust
//! # #[cfg(feature = "actix")]
//! # fn app() -> actix_web::App<
//! # impl actix_web::dev::ServiceFactory<
//! # actix_web::dev::ServiceRequest,
//! # Config = (),
//! # Response = actix_web::dev::ServiceResponse,
//! # Error = actix_web::Error,
//! # InitError = (),
//! # >,
//! # > {
//! use actix_web::{get, App};
//! use webfinger_rs::{Link, Rel, WebFingerRequest, WebFingerResponse};
//!
//! #[get("/.well-known/webfinger")]
//! async fn webfinger(request: WebFingerRequest) -> actix_web::Result<WebFingerResponse> {
//! let subject = request.resource.to_string();
//! if subject != "acct:carol@example.com" {
//! return Err(actix_web::error::ErrorNotFound("not found"));
//! }
//!
//! 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)
//! }
//!
//! App::new().service(webfinger)
//! # }
//! ```
//!
//! [Actix example]:
//! https://github.com/joshka/webfinger-rs/blob/main/webfinger-rs/examples/actix.rs
//!
//! # 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 --example client --features reqwest`
//! - `cargo run --example axum --features axum`
//! - `cargo run --example actix --features actix`
//!
//! The server examples listen on `https://localhost:3000` and can be queried with:
//!
//! ```shell
//! webfinger acct:carol@localhost localhost:3000 --insecure --rel http://webfinger.net/rel/profile-page
//! ```
//!
//! # License
//!
//! Copyright (c) Josh McKinney
//!
//! This project is licensed under either of:
//!
//! - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
//! <https://apache.org/licenses/LICENSE-2.0>)
//! - MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>) at your
//! option
pub use crateError;
pub use crate;
/// The well-known path for WebFinger requests (`/.well-known/webfinger`).
///
/// This is the path that should be used to query for WebFinger resources.
///
/// See [RFC 7033 Section 10.1](https://www.rfc-editor.org/rfc/rfc7033.html#section-10.1) for more
/// information.
pub const WELL_KNOWN_PATH: &str = "/.well-known/webfinger";