activitypub-webfinger
v0.5.0
Published
WebFinger utilities for ActivityPub
Readme
activitypub-webfinger
WebFinger utilities for ActivityPub
Resolve WebFinger addresses to ActivityPub actor IDs and derive WebFinger addresses from actor documents. This package uses ECMAScript modules and supports an injected Fetch-compatible function.
For background on forward and reverse discovery, see the ActivityPub and WebFinger profile, published by the W3C Social Web Incubator Community Group on 8 June 2024.
Table of Contents
Security
This package does not perform server-side request forgery (SSRF) checks. Its
default transport is global fetch, which can connect to private or internal
network addresses, including through redirects.
When looking up user-supplied addresses or actor URLs, pass a Fetch-compatible
function through options.fetch that enforces your network policy, including
checks on resolved IP addresses and redirect destinations. Both discovery functions pass
this fetch function to their verification lookups when verify is enabled.
The verify option checks identity consistency; it does not provide SSRF
protection.
For example, install the optional
guarded-fetch package in your
application:
npm install guarded-fetchThen supply its Fetch-compatible function:
import { guardedFetch } from 'guarded-fetch'
import { actorIdOf, webfingerOf } from 'activitypub-webfinger'
const actorId = await actorIdOf('[email protected]', {
fetch: guardedFetch,
verify: true
})
const address = await webfingerOf('https://remote.example/user/river', {
fetch: guardedFetch,
verify: true
})guarded-fetch checks destination IP addresses and redirects to help prevent
SSRF. It is an optional application dependency, not a dependency of this package.
Its bare guardedFetch function does not limit response-body size; consult its
documentation when choosing resource limits for your application. Blocked or
failed discovery requests result in null.
Install
Install from the repository:
npm install github:social-web-foundation/activitypub-webfingerUse a Node.js version that provides global fetch.
Usage
import { actorIdOf, webfingerOf } from 'activitypub-webfinger'
const actorId = await actorIdOf('[email protected]')
const address = await webfingerOf('https://remote.example/user/river')The example addresses are placeholders. Replace them with an existing account
and actor URL. Both functions return null when lookup or processing fails.
See CHANGELOG.md for changes.
API
actorIdOf(webfinger, options = {})
An async function that returns an actor ID string, or null if none can be found.
webfinger: a WebFinger address, such as[email protected].options.fetch: an optional Fetch-compatible function. Defaults to globalfetch.options.verify: an optional boolean, defaulting tofalse. Whentrue, discovers the returned actor's WebFinger address and requires it to match the input address. A failed verification lookup or mismatch returnsnull.
Queries WebFinger and returns the href of the first matching self link with
media type application/activity+json or
application/ld+json; profile="https://www.w3.org/ns/activitystreams".
It fetches the linked actor document only when verification is enabled.
The input address may contain a Unicode username, a Unicode domain, or both,
such as élise@例え.example. For the WebFinger request, the username is
percent-encoded and the domain is converted to its ASCII Punycode form. In this
example, the resource is acct:%C3%[email protected]; this resource is
then encoded as a query parameter. ASCII Punycode domains are also accepted.
The returned actor ID is the link's href as supplied by the server; its domain
and path are not converted or decoded.
webfingerOf(actorId, options = {})
An async function that returns a WebFinger address string, or null if none can be found.
actorId: an ActivityPub actor URL.options.fetch: an optional Fetch-compatible function. Defaults to globalfetch.options.verify: an optional boolean, defaulting tofalse. Whentrue, resolves the discovered WebFinger address and requires the resulting actor ID to match the input actor ID. A failed verification lookup or mismatch returnsnull.
Fetches the actor document and delegates address extraction to
webfingerFromJson(). If the document contains
https://purl.archive.org/socialweb/webfinger#webfinger, defined by
FEP-2c59: Discovery of a Webfinger address from an ActivityPub actor,
returns its first string value. An acct: URI is converted to a readable
address by decoding the username and converting its Punycode domain to Unicode.
For example, acct:%C3%[email protected] becomes élise@例え.example.
This conversion occurs before verification.
Otherwise, combines the first preferredUsername value with the hostname of
the actor document's id, producing an address such as [email protected].
The fallback requires an HTTPS actor ID.
The actor URL may use a Unicode or ASCII Punycode domain. In the
preferredUsername fallback, the username is preserved as supplied by the actor
document and the URL's Punycode hostname is converted to Unicode. For example,
an actor URL on xn--r8jz45g.example with preferredUsername set to élise
produces élise@例え.example.
An explicit webfinger value without an acct: prefix is returned unchanged.
Explicit metadata takes precedence over the fallback.
The fallback is inferred from the actor document and may differ from the account
address when the account and actor use different domains. Set verify: true to
check that it resolves back to the original actor.
The profile describes verifying that an inferred address resolves back to the
same actor. Enable this with webfingerOf(actorId, { verify: true }).
Both functions use the supplied fetch for the initial and verification
lookups. The reverse lookup runs without verification to avoid recursion.
When verify is false or omitted, only the initial lookup is performed,
regardless of whether a reverse lookup would match. Verification uses exact
string comparisons; it does not normalize addresses or actor IDs.
For forward discovery, check whether the discovered actor's reported or inferred
WebFinger address matches the address you started with. Use
actorIdOf(address, { verify: true }).
Aliases, an acct: prefix, or hostname capitalization can cause a mismatch even
when the original address resolves to the intended actor.
Unicode and Punycode forms of the same domain, percent-encoded and unencoded
usernames, or different Unicode normalization forms can also cause a mismatch.
For example, forward verification of é[email protected] fails if the
reverse lookup returns élise@例え.example.
webfingerFromJson(json)
A synchronous function that returns a readable WebFinger address string from an actor's JSON profile. It performs no network requests or JSON-LD expansion and does not return a Promise.
json: the actor document as a parsed JavaScript object.
Recognizes these field names in precedence order:
| Value | Field names, highest precedence first |
| --- | --- |
| WebFinger address | webfinger, wf:webfinger, https://purl.archive.org/socialweb/webfinger#webfinger |
| Username | preferredUsername, as:preferredUsername, https://www.w3.org/ns/activitystreams#preferredUsername |
| Actor ID | id, @id |
For each value, the first present field is selected; an invalid value does not cause later spellings to be tried. Fields must contain strings, rather than JSON-LD arrays or value objects. Context definitions and custom aliases are not interpreted.
A string WebFinger address takes precedence over username-based derivation.
If it starts with acct:, the username is percent-decoded and the domain is
converted from Punycode to Unicode; otherwise the value is returned unchanged.
When the selected WebFinger field is absent or is not a string, derives the
address from the selected username and actor ID using
webfingerFromUsername().
Throws synchronously for invalid input or insufficient properties to derive
an address. Address conversion and fallback validation errors also propagate.
Unlike webfingerOf(), this helper does not convert errors to null or verify
the resulting address.
webfingerFromUsername(preferredUsername, id)
A synchronous function that returns a readable username@domain address
without performing network requests.
preferredUsername: a nonempty, unencoded username string, preserved as supplied.id: an absolute HTTPS actor ID string. Its hostname is converted from Punycode to Unicode; its port, path, query, and fragment are excluded.
The returned address has no acct: prefix. Throws a TypeError for empty or
non-string arguments, or an error for a malformed URL or a scheme other than HTTPS.
This helper infers an address; it does not validate the username against PRECIS
or verify that the address resolves to the actor.
Maintainers
Contributing
Use GitHub Issues for questions, bug reports, and proposed changes. Please discuss substantial changes with the maintainer before submitting a pull request.
Install dependencies, check StandardJS style, and run the tests from a local checkout:
npm install
npm run lint
npm testTests use Node's built-in test runner and @evanp/activitypub-nock.
Run the browser suite in headless Chromium:
npx playwright install chromium
npm run test:browserThe suite bundles the library and its dependencies with esbuild for the browser, then tests both lookup directions, verification matches and mismatches, custom fetch, HTTP failures, and username fallback. Playwright intercepts requests; the tests do not contact external services. Browser installation requires network access. CI requires both Node.js and Chromium tests before publishing.
License
Licensed under the Apache License, Version 2.0.
