DEV Community

Der Sascha
Der Sascha

Posted on • Originally published at blog.bajonczak.com on

Unveiling WebFinger: A Symphony of Connectivity in the Digital Realm

Unveiling WebFinger: A Symphony of Connectivity in the Digital Realm

WebFinger is a discovery protocol that facilitates the retrieval of additional metadata associated with a specific web address or identity. Imagine it as a digital business card for the web, providing a standardized method to inquire about and retrieve details such as profile information, public keys, or other relevant data. It is built on standards like HTTP requests and JSON Payload. WebFinger operates by allowing clients to query metadata related to a particular resource. The returned information can include a variety of data, aiding in the seamless integration and interoperability of decentralized web technologies.

One of the notable applications of WebFinger is in the realm of social networking. Clients can leverage WebFinger to fetch additional details about a user's URL, such as profile pictures, biographies, or other social information. Moreover, the protocol extends its utility to scenarios like email, where it can be used to retrieve public keys for encryption purposes.

In this Blogpost I will guide you through my process to create a webfinger endpoint to my page and host it. So let's get started.

Creating the server

I use a small webserver from nodejs, so I must initialize the env for it

npm init -y
npm install express body-parser

Enter fullscreen mode Exit fullscreen mode

Next, I will implement the basic response implementation

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;

// Middleware for parsing JSON
app.use(bodyParser.json());

// WebFinger endpoint
app.get('/.well-known/webfinger', (req, res) => {
  const resource = req.query.resource;

  if (!resource) {
    return res.status(400).json({ error: 'Resource parameter is required.' });
  }

  // Generate WebFinger response
  const webFingerResponse = {
    subject: resource,
    links: [
      {
        rel: 'http://webfinger.net/rel/profile-page',
        href: `https://example.com/profile/${resource}`,
      },
      // Add more links as needed
    ],
  };

  res.json(webFingerResponse);
});

// Start the server
app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

Enter fullscreen mode Exit fullscreen mode

This will host in the URL path .../.well-known/webfinger the JSON Webfinger response. In this case a small response with the given input. So start the app with

node app.js
Enter fullscreen mode Exit fullscreen mode

Then call the following url

http://localhost:3000/.well-known/webfinger?resource=sascha@bajonczak.com

This will result in the simple webfinger information:

{
    "subject": "sascha@bajonczak.com",
    "links": [
        {
            "rel": "http://webfinger.net/rel/profile-page",
            "href": "https://example.com/profile/sascha@bajonczak.com"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

So in this example, it will redirect to an example page to get a profile. Later at the end I will redirect it to my mastodon profile. Hey Mastodon.... Let's add Mastodon support.

Adding Mastodon support

So in fact that webfinger is for decentralized platforms, and I want to add support for Mastodon now. For this, I must adjust the script a little bit and add it to my mastodon account

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;

// Middleware for parsing JSON
app.use(bodyParser.json());

// WebFinger endpoint
app.get('/.well-known/webfinger', (req, res) => {
  const resource = req.query.resource;

  if (!resource) {
    return res.status(400).json({ error: 'Resource parameter is required.' });
  }

  // Generate WebFinger response
  const webFingerResponse = {
    subject: resource,
    links: [
      {
        rel: 'http://webfinger.net/rel/profile-page',
        href: `https://example.com/profile/${resource}`,
      },
      {
        rel: 'http://schemas.google.com/g/2010#updates-from',
        href: 'https://mastodon.social/@sashca',
        type: 'application/activity+json',
      }
    ],
  };

  res.json(webFingerResponse);
});

// Start the server
app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

Enter fullscreen mode Exit fullscreen mode

The important part is this

      {
        rel: 'http://schemas.google.com/g/2010#updates-from',
        href: 'https://mastodon.social/@sashca',
        type: 'application/activity+json',
      }
Enter fullscreen mode Exit fullscreen mode

This will give the information about my mastodon account. After running and calling the webfinger again it will result in this

{
    "subject": "sascha@bajonczak.com",
    "links": [
        {
            "rel": "http://webfinger.net/rel/profile-page",
            "href": "https://example.com/profile/sascha@bajonczak.com"
        },
        {
            "rel": "http://schemas.google.com/g/2010#updates-from",
            "href": "https://mastodon.social/@sashca",
            "type": "application/activity+json"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Adding Blog refernces

I think Mastodon is one way, so I want to publish to the Fediverse my blog articles too. So I declare in the webfinger my blog and also the definition where a user can subscribe to my blog (if needet). My complete definition looks now like this

....
      {
        rel: 'alternate',
        type: 'application/rss+xml',
        href: 'https://blog.bajonczak.com/rss/',
      }    
....
Enter fullscreen mode Exit fullscreen mode

So my final result will redirect to the profile page to Mastodon, it adds also my blog to the webfinger method tool. The next step is to host it on my domain.

My Final Result looks now like this

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;

// Middleware for parsing JSON
app.use(bodyParser.json());

// WebFinger endpoint
app.get('/.well-known/webfinger', (req, res) => {
    const resource = req.query.resource;

    if (!resource) {
        return res.status(400).json({ error: 'Resource parameter is required.' });
    }
    res.setHeader('Content-Type', 'application/jrd+json');

    // Generate WebFinger response
    const webFingerResponse = {
        subject: resource,
        aliases: [
            'https://hachyderm.io/@Sascha',
            'https://hachyderm.io/users/Sascha'
        ],
        links: [
            {
                rel: 'http://webfinger.net/rel/profile-page',
                type: 'text/html',
                href: 'https://hachyderm.io/@Sascha'
            },
            {
                rel: 'self',
                type: 'application/activity+json',
                href: 'https://hachyderm.io/users/Sascha'
            },
            {
                rel: 'http://ostatus.org/schema/1.0/subscribe',
                template: 'https://hachyderm.io/authorize_interaction?uri={uri}'
            },
            {
                rel: 'http://schemas.google.com/g/2010#updates-from',
                href: 'https://mastodon.social/@sashca',
                type: 'application/activity+json',
            },
            {
                rel: 'avatar',
                href: `https://blog.bajonczak.com/content/images/2022/10/20221014_171637.png`,  
                type: 'image/png',
              },
            {
                rel: 'alternate',
                type: 'application/rss+xml',
                href: 'https://blog.bajonczak.com/rss/',
            }],
    };

    res.json(webFingerResponse);
});

// Start the server
app.listen(port, () => {
    console.log(`Server is running at http://localhost:${port}`);
});

Enter fullscreen mode Exit fullscreen mode

Hosting

So instead of installing different tools or other things, I create a small container for this and host it onto a webapplication in Azure.

Creating the container

So my Dockerfile looks like this

# Verwende ein offizielles Node.js-Image als Basis
FROM node:14

# Erstelle das Arbeitsverzeichnis
WORKDIR /usr/src/app

# Kopiere die Abhängigkeiten und das Anwendungscode in das Arbeitsverzeichnis
COPY package*.json ./
RUN npm install
COPY . .

# Setze die Standardumgebungsvariable für den Port
ENV PORT=3000

# Öffne den Port, den deine Anwendung verwendet
EXPOSE $PORT

# Starte die Anwendung
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

This must be placed in the same directory in which the app.js is located. I build and run it with the following command

docker build -t webfinger .
docker run -p 3000:3000 -d webfinger
Enter fullscreen mode Exit fullscreen mode

I'll check it, and the response works from within the container

Unveiling WebFinger: A Symphony of Connectivity in the Digital Realm

After pushing it onto a registry (for me it's hosted in dockerhub). I can install this into my Azure app service for now. Instead of handling complex startup commands to open up a port I use a docker-compose file for forwarding the internal request to the external http and https requests:

version: '3'
services:
  app:
    image: yourimage:latest
    ports:
      - 80:3000
      - 443:3000

Enter fullscreen mode Exit fullscreen mode

Final Testing

Now my webfinger definition is online, the result looks like this:

Unveiling WebFinger: A Symphony of Connectivity in the Digital Realm

When I now go to mastodon and try to search for any user in my domain it will popup my avatar and link to my mastodon profile

Unveiling WebFinger: A Symphony of Connectivity in the Digital Realm

Now it's your turn to increase my counter for followers ;)

Conslusion

In conclusion, webfinger is a good method to publish information in the decentralized world. It's like a business card on the internet and provides data that can be shared with other services. In this example, I used webfinger to populate my own domain to mastodon and add more meta information like my blog.

What's your opinion on this?

Top comments (0)