feat: proper webfinger response for instance actor

This commit is contained in:
Julian Lam
2024-01-23 11:22:18 -05:00
parent f8cfe64c7e
commit da0211b1a0
3 changed files with 41 additions and 15 deletions

View File

@@ -24,7 +24,7 @@ Actors.application = async function (req, res) {
type: 'Application',
name,
preferredUsername: name,
preferredUsername: nconf.get('url_parsed').hostname,
publicKey: {
id: `${nconf.get('url')}#key`,

View File

@@ -9,7 +9,7 @@ const Controller = module.exports;
Controller.webfinger = async (req, res) => {
const { resource } = req.query;
const { host } = nconf.get('url_parsed');
const { host, hostname } = nconf.get('url_parsed');
if (!resource || !resource.startsWith('acct:') || !resource.endsWith(host)) {
return res.sendStatus(400);
@@ -23,30 +23,45 @@ Controller.webfinger = async (req, res) => {
// Get the slug
const slug = resource.slice(5, resource.length - (host.length + 1));
const uid = await user.getUidByUserslug(slug);
if (!uid) {
let uid = await user.getUidByUserslug(slug);
if (slug === hostname) {
uid = 0;
} else if (!uid) {
return res.sendStatus(404);
}
const response = {
subject: `acct:${slug}@${host}`,
aliases: [
};
if (uid) {
response.aliases = [
`${nconf.get('url')}/uid/${uid}`,
`${nconf.get('url')}/user/${slug}`,
],
links: [
{
rel: 'http://webfinger.net/rel/profile-page',
type: 'text/html',
href: `${nconf.get('url')}/user/${slug}`,
},
];
response.links = [
{
rel: 'self',
type: 'application/activity+json',
href: `${nconf.get('url')}/user/${slug}`, // actor
},
],
};
{
rel: 'http://webfinger.net/rel/profile-page',
type: 'text/html',
href: `${nconf.get('url')}/user/${slug}`,
},
];
} else {
response.aliases = [nconf.get('url')];
response.links = [
{
rel: 'self',
type: 'application/activity+json',
href: nconf.get('url'), // actor
},
];
}
res.status(200).json(response);
};

View File

@@ -237,19 +237,30 @@ describe('ActivityPub integration', () => {
assert(body.hasOwnProperty('@context'));
assert(body['@context'].includes('https://www.w3.org/ns/activitystreams'));
['id', 'url', 'inbox', 'outbox'].forEach((prop) => {
['id', 'url', 'inbox', 'outbox', 'name', 'preferredUsername'].forEach((prop) => {
assert(body.hasOwnProperty(prop));
assert(body[prop]);
});
assert.strictEqual(body.id, body.url);
assert.strictEqual(body.type, 'Application');
assert.strictEqual(body.name, meta.config.site_title || 'NodeBB');
assert.strictEqual(body.preferredUsername, nconf.get('url_parsed').hostname);
});
it('should contain a `publicKey` property with a public key', async () => {
assert(body.hasOwnProperty('publicKey'));
assert(['id', 'owner', 'publicKeyPem'].every(prop => body.publicKey.hasOwnProperty(prop)));
});
it('should also have a valid WebFinger response tied to `preferredUsername`', async () => {
const { response, body: body2 } = await request.get(`${nconf.get('url')}/.well-known/webfinger?resource=acct:${body.preferredUsername}@${nconf.get('url_parsed').host}`);
assert.strictEqual(response.statusCode, 200);
assert(body2 && body2.aliases && body2.links);
assert(body2.aliases.includes(nconf.get('url')));
assert(body2.links.some(item => item.rel === 'self' && item.type === 'application/activity+json' && item.href === nconf.get('url')));
});
});
describe('http signature signing and verification', () => {