npm package: @jgarber/eleventy-plugin-webfinger
Description: An Eleventy plugin for generating a Webfinger configuration file.
Author: Jason Garber
Latest version: 0.2.0

# eleventy-plugin-webfinger

**An [Eleventy](https://www.11ty.dev) plugin for generating a [Webfinger](https://webfinger.net) configuration file.**

[![npm](https://img.shields.io/npm/v/@jgarber/eleventy-plugin-webfinger.svg?logo=npm&style=for-the-badge)](https://www.npmjs.com/package/@jgarber/eleventy-plugin-webfinger)
[![Downloads](https://img.shields.io/npm/dt/@jgarber/eleventy-plugin-webfinger.svg?logo=npm&style=for-the-badge)](https://www.npmjs.com/package/@jgarber/eleventy-plugin-webfinger)

## Usage

First, add the plugin as [a development dependency](https://docs.npmjs.com/cli/configuring-npm/package-json#devdependencies) to your project's `package.json` file:

```sh
npm install --save-dev @jgarber/eleventy-plugin-webfinger
```

Next, add the plugin to your project's [Eleventy configuration file](https://www.11ty.dev/docs/config#default-filenames) (e.g. `eleventy.config.js`):

```js
import eleventyPluginWebfinger from "@jgarber/eleventy-plugin-webfinger";

export default async function(eleventyConfig) {
  eleventyConfig.addPlugin(eleventyPluginWebfinger);
}
```

With no additional configuration, eleventy-plugin-webfinger will use a global data object named `webfinger` to populate a template at the permalink `/.well-known/webfinger`.

```js
// Using a global data file at `_data/webfinger.js`
const subject = "acct:me@jgarber.example";

const aliases = ["https://jgarber.example"];

const links = [
  {
    href: "https://jgarber.example/avatar.png",
    rel: "http://webfinger.net/rel/avatar",
    type: "image/png",
  },
  {
    href: "https://jgarber.example/about",
    rel: "http://webfinger.net/rel/profile-page",
    type: "text/html",
  },
  {
    href: "https://jgarber.example/users/jgarber",
    rel: "self",
    type: "application/activity+json",
  },
];

export default { subject, aliases, links };
```

If a global data file (or other data file) doesn't suit your needs, you may directly provide configuration to the plugin:

```js
import eleventyPluginWebfinger from "@jgarber/eleventy-plugin-webfinger";

export default async function(eleventyConfig) {
  eleventyConfig.addPlugin(eleventyPluginWebfinger, {
    subject: "acct:me@jgarber.example",
    aliases: ["https://jgarber.example"],
    links: [
      {
        href: "https://jgarber.example/avatar.png",
        rel: "http://webfinger.net/rel/avatar",
        type: "image/png",
      },
      {
        href: "https://jgarber.example/about",
        rel: "http://webfinger.net/rel/profile-page",
        type: "text/html",
      },
      {
        href: "https://jgarber.example/users/jgarber",
        rel: "self",
        type: "application/activity+json",
      },
    ],
  });
}
```
The examples above are fairly rudimentary and not _that_ much different from creating a template in your project's source directory. However, consider the example below which reuses data from a different global data file:

```js
// _data/actor.json
const url = new URL("https://jgarber.example");
const id = new URL("users/jgarber", url);

const icon = {
  mediaType: "image/png",
  type: "Image",
  url: new URL("avatar.png", url),
};

const preferredUsername = "me";

export default { url, id, icon, preferredUsername };
```

```js
// _data/webfinger.js
import actor from "./actor.js";

const { url, id, icon, preferredUsername } = actor;

export default {
  subject: `${preferredUsername}@${url.hostname}`,
  aliases: [url, id],
  links: [
    {
      href: icon.url,
      rel: "http://webfinger.net/rel/avatar",
      type: icon.mediaType,
    },
    {
      href: new URL("about", url),
      rel: "http://webfinger.net/rel/profile-page",
      type: "text/html",
    },
    {
      href: id,
      rel: "self",
      type: "application/activity+json",
    },
  ],
};
```

## Deployment Considerations

The file generated by this plugin, `.well-known/webfinger`, doesn't have a file extension and should be served with a content type of `application/jrd+json`. Consult [RFC 7033](https://datatracker.ietf.org/doc/html/rfc7033) for details.

You should configure the following HTTP response headers using your hosting provider's configuration. The following configuration is suitable for a `_headers` file deployed to Cloudflare Pages:

```
/.well-known/webfinger
  Access-Control-Allow-Origin: *
  Content-Type: application/jrd+json; charset=UTF-8
```

## Limitations

The `./well-known/webfinger` configuration produced by this plugin is a static file and, as such, will not respond differently to queries defined by the Webfinger specification (see [section 4.1](https://datatracker.ietf.org/doc/html/rfc7033#section-4.1)). This plugin is well-suited for personal websites that represent a single "resource" (e.g. a person [you!], a group [your band!], an organization [your crew!]).

Your hosting provider _may_ support query parameters in `_redirects` files, so perhaps there's a way to dynamically generate files using this plugin. Give it a try!

## Acknowledgments

First and foremost, eleventy-plugin-webfinger wouldn't be possible without [Zach Leatherman](https://www.zachleat.com)'s incredible work creating Eleventy and his stewardship of its community.

eleventy-plugin-webfinger is written and maintained by [Jason Garber](https://sixtwothree.org).